gpt4 book ai didi

sql-server - NHibernate下SQL Server死锁

转载 作者:行者123 更新时间:2023-12-03 16:37:17 25 4
gpt4 key购买 nike

我有一个多线程应用程序,它适用于大型数据库(文件大小 >1 Gb,数据库有 38 个表,每个表有超过 500 K 的实体)。它使用 城堡 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.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)

我用 每个线程一个 session (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;
}
}

在我将 3 个数据库(每个 300-400 Mb)合并到一个大数据库(如上所述)之前,一切正常。我通过 Microsoft SQL Server Management Studio 导出/导入数据向导合并了数据库。合并后,我设置了主键,并为 设置了身份规范。身份证 列。在这之后我得到了很多 SQL Server 错误(此错误未重现,它们不时出现):

Transaction (Process ID X) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.



所以我读了 this article , this .我完成了所有步骤,发现两个线程在同一个关联对象 ID 上执行相同的页锁。我什至不明白这是怎么回事,因为我以编程方式锁定了每个实体更新,所以它甚至不可能是这样的问题。这就是我将数据库更改为快照隔离的原因:

ALTER DATABASE MY_DB SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MY_DB SET READ_COMMITTED_SNAPSHOT ON



我也改变了 NHibernate 隔离级别 来自 已提交阅读 快照 来解决这个问题。在此之后,我收到了以下错误:

Could not execute command: UPDATE [Foo] SET Field1 = @p0, Field2= @p1 WHERE Id = @p2
System.Data.SqlClient.SqlException (0x80131904): Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to access table 'dbo.Bar' directly or indirectly in database 'My_DB' to update, delete, or insert the row that has been modified or deleted by another transaction. Retry the transaction or change the isolation level for the update/delete statement.


in System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
in System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
in System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
in System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
in System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
in System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
in System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
in System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
in NHibernate.AdoNet.AbstractBatcher.ExecuteNonQuery(IDbCommand cmd)

Could not synchronize database state with session NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Foo#544353]


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)

NHibernate.TransactionException: Transaction not connected, or was disconnected


in NHibernate.Transaction.AdoTransaction.CheckNotZombied()
in NHibernate.Transaction.AdoTransaction.Rollback()
in My.DBLayer.Data.Repositories.BaseEntityRepository`2.Update(T item)

我没有 版本 DB 实体的属性,并且我的代码中没有 OptimisticLock.Version() 行,所以我隐式使用 PessimisticLock。我可以加 版本 乐观锁 ,但不要认为这会解决问题。

我试着做简单的测试:
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);
}

一切正常,没有任何错误。

为什么我有所有这些错误(我没有更改代码,只是将数据库合并为一个,并且在合并之前一切正常)?如果我使用锁定来防止同时更新不同线程中的实体,为什么会出现此错误。

最佳答案

您可以从查询并行化中获得死锁……也就是说,当将工作拆分为多个并行操作时,单个查询可能会对自身造成死锁。我曾多次遇到这种情况。如果您使用的是 hql,您可以将 OPTION(MAXDOP 1) 添加到您的查询/语句中以查看是否可以解决问题。

或者,您可以将整个服务器设置为 MAXDOP 1...这意味着您永远不会获得并行性( https://msdn.microsoft.com/en-us/library/ms189094.aspx )。如果没有对工作负载进行深入分析,我不会推荐这样做,但它可以帮助您了解并行性是否是死锁的根源。

关于sql-server - NHibernate下SQL Server死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15986889/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com