【问题标题】:High concurrency upserts cause deadlocks and query time of > 30s高并发 upsert 导致死锁和查询时间 > 30s
【发布时间】: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


【解决方案1】:

默认情况下,merge 获取 updlocks,因此 with (updlock) 不会为您做任何事情。将您的updlock 更改为holdlock(或serializable)并在事务中运行该语句将保证在操作期间获取并持有正确的锁。

为防止并发会话使用相同的密钥插入数据,必须获取不兼容的锁以确保只有一个会话可以读取密钥,并且必须保持该锁直到事务完成。


你需要使用set transaction isolation level serializable吗?

在这种情况下,如果上面的代码就是事务中的全部内容,我认为将事务隔离级别显式设置为serializable 不会有任何区别。 merge with (holdlock) 将默认获取与共享锁不兼容的更新锁,使用holdlock 提示解决了Dan Guzman 在上面引用的文章和摘录中解释的竞争条件问题。


with (holdlock)table hint。表提示会覆盖语句的默认行为。

如果您的事务中有其他语句,那么这些语句将受到事务隔离级别与默认隔离级别的差异或显式设置的set transaction isolation level(会话级别)的影响,除非被表提示覆盖。

最高粒度获胜:

  • 最低:数据库(默认为read committed
  • 中间:会话 (set transaction isolation level ...)
  • 最高:表格提示 (with (updlock, serializable))

有关事务隔离级别的更多信息:

【讨论】:

  • 那么在没有holdlock 的可序列化事务和使用holdlock 的可重复读取事务之间,表行为有何不同?
  • @caesay 这两个场景 = 可序列化。
  • 我通过缩减我们的生产服务器来降低并发级别。我还切换到带有holdlock 的可重复读取事务。我没有注意到任何非常糟糕的锁定,但肯定看到插入需要 > 3 秒,所以它仍然不理想。
  • @caesay 你能发布交易的完整代码吗?
猜你喜欢
  • 2018-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-10
  • 2017-09-22
相关资源
最近更新 更多