【发布时间】:2017-02-09 02:04:18
【问题描述】:
我正在开发一个应用程序,该应用程序使用 EF6 进行大多数数据库操作,并且在大多数情况下,数据库内容是非关键的,并且不会承受很大的负载。此规则有一个例外,我们有可能 100/s 的事件流传入,需要根据列值在特定表中插入或更新行。
总的来说,我对 SQL 有点弱,但我编写了这个查询来插入或更新并返回元素的 id:
DECLARE @Id [int];
MERGE {tableName} WITH (UPDLOCK) AS target
USING (SELECT @MatchName AS id) AS source
ON source.id = target.MatchColumn
WHEN MATCHED
THEN UPDATE SET @Id = target.Id, ...
WHEN NOT MATCHED
THEN INSERT (...) VALUES (...);
IF @Id IS NULL
BEGIN
SELECT @Id = CAST(SCOPE_IDENTITY() as [int]);
END;
SELECT @Id;
它在 (EF) 可序列化事务块内完成,它是唯一在显式事务中执行的东西,也是更新此表的唯一代码。 (其他东西可以读)。如果事务被数据库回滚(EF 抛出异常),它会立即重试最多 3 次。
问题在于,当我们开始进入更高负载的情况时,我们最终可能会处于这样一种状态,即有很多东西都在尝试更新数据库,并且针对该表的查询可能开始需要 30 多秒(对其他表的查询仍然存在)美好的)。我的印象是,即使这在可序列化事务中执行,它也只会锁定由合并匹配表达式选择的行,这应该是一个相对快速的操作。
过去几天我一直在做一些研究,有些人建议在默认事务中只有HOLDLOCK 就足够了,而其他人则声称需要可序列化的事务,否则您可能会遇到数据完整性问题。
我希望有人能解释为什么会发生长时间的死锁,并详细说明在这种情况下哪种锁定机制是最佳的。
【问题讨论】:
-
如果您使用一些值执行此语句并将实际执行计划附加到问题中会很好。
-
@Evk:这些值只是几个整数/短字符串列,可以附加执行计划。
标签: c# sql sql-server entity-framework concurrency