Sql server NHibernate下的SQL Server死锁
我有一个多线程应用程序,可以处理大型数据库(文件大小>1GB,数据库有38个表,每个表有超过500K的实体)。它使用了Castle 3.1.0.0、NHibernate 3.3.1.4000、FluentNibernate 1.3.0.733、SQL Server 2012 NHibernate以下一种方式配置:Sql server NHibernate下的SQL Server死锁,sql-server,nhibernate,transactions,locking,isolation-level,Sql Server,Nhibernate,Transactions,Locking,Isolation Level,我有一个多线程应用程序,可以处理大型数据库(文件大小>1GB,数据库有38个表,每个表有超过500K的实体)。它使用了Castle 3.1.0.0、NHibernate 3.3.1.4000、FluentNibernate 1.3.0.733、SQL Server 2012 NHibernate以下一种方式配置: config.SetProperty(Environment.CommandTimeout, "300"); config.SetProperty(Environment.BatchS
config.SetProperty(Environment.CommandTimeout, "300");
config.SetProperty(Environment.BatchSize, "0");
config.SetProperty(Environment.GenerateStatistics, "true");
config.SetProperty(Environment.ReleaseConnections, "auto");
config.SetProperty(Environment.UseQueryCache, "true");
config.SetProperty(Environment.SqlExceptionConverter, typeof(MsSqlExceptionConverter).AssemblyQualifiedName);
//...
.MaxFetchDepth(1)
我使用每个线程一个会话(Castle.Windsor)和短事务。每个数据库更新、保存、删除过程都由代码锁定:
public abstract class BaseEntityRepository<T, TId> : IBaseEntityRepository<T, TId> where T : BaseEntity<TId> {
protected static readonly object Locker = new object();
public bool Save(T item) {
bool result = false;
if ((item != null) && (item.IsTransient())) {
lock (Locker) {
using (ITransaction tr = Session.BeginTransaction()) {
try {
Session.Save(item);
if ((tr.IsActive) && (!tr.WasCommitted) && (!tr.WasRolledBack))
tr.Commit();
result = true;
} catch {
if ((tr.IsActive) && (!tr.WasCommitted) && (!tr.WasRolledBack))
tr.Rollback();
Session.Clear();
throw;
}
}
}
}
return result;
}
//same for delete and update
public T Get(TId itemId) {
T result = default(T);
try {
result = Session.Get<T>(itemId);
} catch {
throw;
}
return result;
}
public IList<T> Find(Expression<Func<T, bool>> predicate) {
IList<T> result = new List<T>();
try {
result = Session.Query<T>().Where(predicate).ToList();
} catch {
throw;
}
return result;
}
}
无法将数据库状态与会话同步
NHibernate.StaleObjectStateException:行被另一个事务更新或删除(或未保存的值映射不正确):[Foo\544353]
NHibernate.TransactionException:事务未连接或已断开连接
我没有DB实体的Version属性,代码中也没有OptimisticLock.Version()行,所以我使用了悲观锁隐式。我可以添加版本和乐观锁,但我认为这不会解决问题
我试着做一个简单的测试:
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m2);
t1.Start();
t2.Start();
private static void m1() {
FooRepository rep1 = BootstrapContainer.Instance.Resolve<FooRepository>();
Foo foo1 = rep1.Get(1);
foo1.Field1 = "bbbb";
Thread.Sleep(60*1000);
rep1.Update(foo1);
}
private static void m2() {
FooRepository rep2 = BootstrapContainer.Instance.Resolve<FooRepository>();
Thread.Sleep(5*1000);
Foo foo2 = rep2.Get(1);
foo2.Field2 = "aaaaa";
Thread.Sleep(5*1000);
rep2.Update(foo2);
}
螺纹t1=新螺纹(m1);
螺纹t2=新螺纹(m2);
t1.Start();
t2.Start();
私有静态void m1(){
FooRepository rep1=BootstrapContainer.Instance.Resolve();
Foo foo1=rep1.Get(1);
foo1.Field1=“bbbb”;
线程。睡眠(60*1000);
rep1.更新(foo1);
}
私有静态void m2(){
FooRepository rep2=BootstrapContainer.Instance.Resolve();
线程。睡眠(5*1000);
Foo foo2=rep2.Get(1);
foo2.Field2=“AAAA”;
线程。睡眠(5*1000);
rep2.更新(foo2);
}
一切正常,没有任何错误
为什么我会有这些错误(我没有更改代码,只是将数据库合并到一个数据库中,并且在合并之前,所有这些都可以正常工作)?如果我使用锁定来防止同时更新不同线程中的实体,为什么会出现这种错误。您可以从查询并行化中获得死锁。。。也就是说,当一个查询将工作拆分为多个并行操作时,它可能会针对自身死锁。我有好几次遇到过这种情况。如果您使用的是hql,那么可以将选项(MAXDOP 1)添加到查询/语句中,以查看这是否解决了问题
或者,您可以将整个服务器设置为MAXDOP 1。。。这意味着您将永远无法获得并行性()。如果不深入分析工作负载,我不建议这样做,但它可以帮助您了解并行性是否是死锁的根源。您咨询过DBA吗?您是否重新创建了索引并缩减了磁盘空间?当您运行测试,但将每个线程中的同一字段更新为不同的值时,会发生什么情况?将foo1.Field1=“bbbb”和foo2.Field1设置为“aaaaa”。
in NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session)
in NHibernate.Persister.Entity.AbstractEntityPersister.UpdateOrInsert(Object id, Object[] fields, Object[] oldFields, Object rowId, Boolean[] includeProperty, Int32 j, Object oldVersion, Object obj, SqlCommandInfo sql, ISessionImplementor session)
in NHibernate.Persister.Entity.AbstractEntityPersister.Update(Object id, Object[] fields, Int32[] dirtyFields, Boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object obj, Object rowId, ISessionImplementor session)
in NHibernate.Action.EntityUpdateAction.Execute()
in NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
in NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
in NHibernate.Engine.ActionQueue.ExecuteActions()
in NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
in NHibernate.Transaction.AdoTransaction.CheckNotZombied()
in NHibernate.Transaction.AdoTransaction.Rollback()
in My.DBLayer.Data.Repositories.BaseEntityRepository`2.Update(T item)
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m2);
t1.Start();
t2.Start();
private static void m1() {
FooRepository rep1 = BootstrapContainer.Instance.Resolve<FooRepository>();
Foo foo1 = rep1.Get(1);
foo1.Field1 = "bbbb";
Thread.Sleep(60*1000);
rep1.Update(foo1);
}
private static void m2() {
FooRepository rep2 = BootstrapContainer.Instance.Resolve<FooRepository>();
Thread.Sleep(5*1000);
Foo foo2 = rep2.Get(1);
foo2.Field2 = "aaaaa";
Thread.Sleep(5*1000);
rep2.Update(foo2);
}