5

我在 SQL Server 2012 中遇到了死锁情况。

运行 SQL Server Profiler 后,我得到一个死锁图,如下所示:

死锁图

当将鼠标移到进程(椭圆)上时,两个进程都显示相同的 PrepareStatement 查询(我使用的是 JDBC)。

我正在使用的查询如下:

MERGE INTO MA4TB_MT_LOG_MSG  USING (VALUES (1)) AS S(Num) ON ( MSG_ID = ? )
            WHEN MATCHED THEN
                UPDATE SET 
                    DIST_DATE   = ?,
                    DIST_CODE   = ?
            WHEN NOT MATCHED THEN
                INSERT (
                    MSG_ID, DIST_DATE, DIST_CODE
                ) VALUES (
                    ?,?,?
                );

困扰我的是 Key lock 资源框下的索引名称。

我在 MA4TB_MT_LOG_MSG 表下没有名为“1”的索引。

MSG_ID 是 MA4TB_MT_LOG_MSG 的主键,在 DIST_DATE、DIST_CODE 上没有索引。

对于这种僵局情况的任何形式的建议将不胜感激。

提前致谢,

4

4 回答 4

4

让我们开始回答你的第一个问题。我没有索引 id = 1。

是的你是!!

让我们看一下 SQL Server 2014 CTP2 上的 Adventure Works 2012 数据库。这是我的笔记本电脑规格。

有一个表名 [AWBuildVersion]。它有一个聚集索引,就像你的表一样。在索引下,我们可以看到 PK 出现了。如果我们获取表的对象 id (sys.objects) 并查找索引 (sys.indexes) 的条目,我们可以看到索引位于位置 1。

简而言之,默认情况下主键是聚集索引。

http://technet.microsoft.com/en-us/library/ms177443(v=sql.105).aspx

在此处输入图像描述

好的,那么没有索引的表有什么?这些表称为堆。他们在位置 0 处确实有自己的索引,该索引指向第一个 IAM 页面。

http://technet.microsoft.com/en-us/library/ms188270(v=sql.105).aspx

下面的代码创建一个名为 [crafty] 的模式,并使用 SELECT INTO 将 [AWBuildVersion] 表复制到一个新模式。SELECT INTO 的好处是没有索引被结转。

use AdventureWorks2012
go

create schema [crafty] authorization [dbo];
go

select * into crafty.awbuildversion from dbo.awbuildversion
go

简而言之,我们可以看到在位置 0 处使用索引定义的堆。

在此处输入图像描述

那么什么是死锁,请求模式U是什么意思呢?

死锁是两个进程同时获取资源但顺序不同。简而言之,这两个过程都无法进行。引擎选择回滚时间最少的会话并终止该进程。

http://technet.microsoft.com/en-us/library/ms178104(v=sql.105).aspx

一张图片胜过千言万语!事务 1 抢资源 1。事务 2 抢资源 2。当他们试图抢对方资源时,就会产生死锁。

在此处输入图像描述

那么按键锁和用户模式U是什么意思呢?

要更新您的表格,您需要更新数据/索引页面。但是数据页实际上是表中的索引页(聚集索引)。SQL 引擎取出 (U)PDATE 锁。在实际更新期间,此锁将升级为排他锁 (X)。

当两个进程请求独占锁时,就有可能发生死锁。

为了完成这个主题,共享锁(SELECT)可以在不阻塞(同时)的情况下执行。通常一个进程从阻塞开始,然后当Engines死锁进程线程检测到循环图时变成死锁。

默认隔离级别为未提交读。

http://technet.microsoft.com/en-us/library/ms175519(v=sql.105).aspx

在这一点上,你有一个死锁。

你从这里去哪里?

1 - 可能有多个会话 (SPID) 运行相同的代码。为什么?你能改变这一点,使一次只有一个进程运行代码吗?

2 - 获取由 JDBC 生成的实际 TSQL。这可以通过 SQL 分析器和/或查看您的 DMV 来完成。

合并语句同时执行 UPDATE 和/或 INSERT。因此是复合操作。

3 - 你可以将隔离级别更改为可序列化吗?
http://technet.microsoft.com/en-us/library/ms173763.aspx

这将添加更多锁,并且可能会将您的死锁问题变为超时问题。请参阅 Kalen Daleny 文章,了解如何设置 LOCK_TIMEOUT。您将不得不调整您的代码以再次重试该操作,中间有一些延迟。

http://sqlmag.com/sql-server/inside-sql-server-controlling-locking

我希望这些信息对您有所帮助。

如果您需要更多帮助,请发布您的 TSQL。

于 2014-04-24T15:26:29.287 回答
1

SQL Profiler 生成的信息比该图表/鼠标悬停中显示的信息多,并且该信息可能会或可能不会更清楚地说明您的问题。尝试这个:

  • 在 Profiler 中选择导致图表显示的行
  • 执行“复制”(ctrL+C,或菜单选项)
  • 将其粘贴到文本编辑器中(例如 SSMS 查询窗口)
  • 您想要的是 Profiler“文本”列的 XML 内容——删除其他所有内容
  • 在 XML 编辑器中打开这个 XML(无论你有什么使它清晰,我使用 MIcrosoft 的 XML 记事本)
  • 向下钻取。这需要一点时间,但是一旦你掌握了布局的窍门(层次结构、标签名称等),事情就应该清楚了
于 2014-04-22T22:29:00.243 回答
0

Lock 图似乎显示整个表被锁定。

从根本上说,我建议您检查您编写的 MERGE 语句。确保您有适当的过滤条件。尝试有适当的索引,以便数据访问更快并减少锁定持续时间。仅在它们应该存在时才实施交易。了解哪个进程在 MA4TB_MT_LOG_MS 之上创建了死锁场景,然后尝试解决那段代码。增加锁定选择性(即行\页锁而不是表锁)和持续时间。

很多东西让你看!

于 2014-04-19T17:51:50.693 回答
0

我认为“索引名称:1”是指聚集索引的对象 ID

于 2014-04-19T17:53:53.063 回答