微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何解决 SqlException 在锁定时死锁 |通信缓冲区资源

如何解决如何解决 SqlException 在锁定时死锁 |通信缓冲区资源

stackoverflow 上已经有这个问题的不同版本,但没有一个能帮助我找到问题的根源。所以,我再次详细介绍我的问题。

我们随机得到 Transaction (Process ID xx) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction. 。让我说清楚,这不是行级或表级锁定。我已经尝试了足够多的猜测/随机的事情;我需要有关如何解决通信缓冲区死锁问题的准确分步指南。

如果您对具体细节感兴趣,请继续阅读。

场景的具体细节:我们有一个非常简单的基于 Dapper ORM 的 C# .net 核心 Web API,它接收请求并对托管在此 Microsoft sql 服务器上的数据库执行 CRUD 操作。为此,连接管理器(注册为范围服务)在请求范围内打开一个新的 IDbConnection 连接;此连接用于执行删除、插入、更新或获取。对于插入/更新/删除 C# 行看起来像这样 await connection.ExecuteAsync("<Create update or delete statement>",entity); 对于 GET 请求,我们只需运行 await connection.QueryFirstOrDefaultAsync<TEntity>("<select statement>",entity); ;有 5 种类型的实体(都呈现简单的非关系表)。他们都通过 ID 进行 CRUD。

到目前为止已经尝试过什么

  1. MAXDOP=1 sql 语句的查询提示
  2. 确保在给定的时间点,一种实体只有 1 个实体 CRUD。
  3. 重新启动 sql 服务器/应用程序实例
  4. 确保端口/RAM/cpu/网络带宽不会耗尽
  5. 更改 DATABASE XXXXXX SET READ_COMMITTED_SNAPSHOT 开/关
  6. 尽量减少交易量
  7. 持久重试策略作为一种解决方法(用于处理问题的随机瞬态性质)
  8. 每个实体类型的单个线程

服务器规格: 我们将 Microsoft sql Server 2016 On Azure 托管在具有 64 核和 400GB RAM 的虚拟机中。这台服务器上的通常工作负载是 10% 的 cpu 和 30% 的内存,偶尔它会上升到 80% 的 cpu 和 350GB 内存。在发生此问题的所有时间,cpu 使用率都低于 20%(大部分在 10% 左右,只有一次是 20%,RAM 在所有情况下都低于 30%)。

根据@Dan Guzman 的请求,死锁 XML 事件

这篇文章文件太大,所以创建了这个谷歌驱动文件。请点击以下链接,然后在右上角点击下载。这是一个压缩文件

https://drive.google.com/file/d/1oZ4dT8Yrd2uW2oBqBy9XK_laq7ftGzFJ/view?usp=sharing

解决方法

死锁通常是需要进行查询和索引调整的症状。下面是来自死锁跟踪的示例查询,它表明了死锁的根本原因:

<inputbuf>
@SomeStatus1 nvarchar(4000),@ProductName nvarchar(4000),@ProductNameSide nvarchar(4000),@BayNo nvarchar(4000),@CreatedDateTime datetime,@EffectiveDate datetime,@ForSaleFrom datetime,@ForSaleTo datetime,@SetupInfoNode nvarchar(4000),@LocationNumber nvarchar(4000),@AverageProductPrice decimal(3,2),@NetAverageCost decimal(3,1),@FocustProductType nvarchar(4000),@IsProduceCode nvarchar(4000),@ActivationIndicator nvarchar(4000),@ResourceType nvarchar(4000),@ProductIdentifierNumber nvarchar(4000),@SellingStatus nvarchar(4000),@SectionId nvarchar(4000),@SectionName nvarchar(4000),@SellPriceGroup nvarchar(4000),@ShelfCapacity decimal(1,0),@SellingPriceTaxExclu decimal(2,@SellingPriceTaxInclu decimal(2,@UnitToSell nvarchar(4000),@VendorNumber nvarchar(4000),@PastDate datetime,@PastPrice decimal(29,0))
UPDATE dbo.ProductPricingTable 
SET SellingPriceTaxExclu = @SellingPriceTaxExclu,SellingPriceTaxInclu = @SellingPriceTaxInclu,SellPriceGroup = @SellPriceGroup,ActivationIndicator = @ActivationIndicator,IsProduceCode = @IsProduceCode,EffectiveDate = @EffectiveDate,NetCos
</inputbuf>

尽管 SQL 语句文本被截断,但它确实表明所有参数声明都是 nvarchar(4000)(ORM 的常见问题)。这可能会在 join/where 子句中引用的列类型不同时阻止索引的有效使用,从而导致在并发查询期间导致死锁的完整扫描。

更改参数类型以匹配引用列的类型并检查执行计划的效率。

,

@DanGuzman 帮助了我,所以我不得不投票/选择他的答案作为接受的答案。但是,我想总结一下这里发生了什么、我学到了什么以及如何解决通信缓冲区死锁(或任何与此相关的死锁)的分步方法。

步骤 - 1
拉出死锁报告。我使用了以下查询,但您也可以使用@DanGuzman 建议的查询(在此问题的评论部分)。

SELECT
   xed.value('@timestamp','datetime2(3)') as CreationDate,xed.query('.') AS XEvent
FROM
(
   SELECT CAST([target_data] AS XML) AS TargetData
   FROM sys.dm_xe_session_targets AS st
      INNER JOIN sys.dm_xe_sessions AS s
         ON s.address = st.event_session_address
      WHERE s.name = N'system_health'
         AND st.target_name = N'ring_buffer'
) AS Data
CROSS APPLY TargetData.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS XEventData (xed)
ORDER BY CreationDate DESC

步骤 - 2
定位到你的sql异常计时/数据对应的死锁事件。然后结合 Detecting and Ending Deadlocks 指南阅读此报告以了解死锁问题的根本原因。在我的情况下,我在通信缓冲区上遇到了死锁,因此根据本指南,内存(检测和结束死锁指南的 Memory 部分)一定是导致问题的原因。正如丹指出的那样,就我而言,以下查询出现在死锁报告中,该报告使用了太多缓冲区(由于查询效率低下)。那么什么是通信缓冲区死锁呢?好吧,如果这个查询需要太多的缓冲区来完成它的执行,那么两个这样的查询可以同时开始执行并开始声明他们需要的缓冲区,但在某些时候可用缓冲区可能不够,他们将不得不等待更多的缓冲区从其他查询的执行完成中释放出来。因此,两个查询都等待对方完成,以期释放更多缓冲区。这可能会导致缓冲区死锁(根据指南的内存部分)

<inputbuf>
@SomeStatus1 nvarchar(4000),NetCos
</inputbuf>

第 3 步(修复)
等待 !!!!但是我用的是Dapper。那它怎么能把我的查询转换成这样一个致命的查询呢?好吧,Dapper 非常适合大多数具有开箱即用默认值的情况,但是,很明显,在我的情况下,默认 4000 nvarchar 杀死了它(请阅读 Dan 的回答以了解此类查询如何导致问题)。正如丹建议的那样,我从输入实体中自动构建参数,例如 await connection.ExecuteAsync("<Create update or delete statement>",entity);,其中 entity 是 C# 模型类的实例。我更改了它的自定义参数,如下所示。 (为简单起见,我只添加了一个参数,但您可以使用所有必需的参数)

            var parameters = new DynamicParameters();
            parameters.Add("Reference",entity.Reference,DbType.AnsiString,size: 18 );
await connection.ExecuteAsync("<Create update or delete statement>",parameters );

我可以在探查器中看到请求现在具有完全匹配的类型列参数类型。就是这样,此修复程序使问题消失了。谢谢丹。

结论
我可以得出结论,在我的情况下,通信缓冲区发生死锁是因为一个错误的查询占用了太多缓冲区来执行。之所以如此,是因为我盲目地使用了默认的 Dapper 参数构建器。使用 Dapper 的自定义参数构建器解决了这个问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?