sql-server - UPDATE 会导致死锁吗
问题描述
同时运行相同的 UPDATE 查询会导致死锁吗?
假设有一个包含数百万条记录的表,我们需要一次更新数千条记录。
UPDATE TABLEX
SET Column1 = '1'
WHERE Column2 BETWEEN 1 AND 10000
我想知道并发运行此查询是否会导致死锁,因为每个更新查询可能会以不同的顺序获取页/行 U 锁。
换句话说,以下是一种可能的情况。场景是两个并发会话运行相同的查询来更新同一组记录。
- 会话 1:获取第 1 行的更新锁(Column2 = 1)
- 会话 2:获取第 2 行的更新锁(Column2 = 2)
- 会话 1:尝试获取第 2 行的更新锁,但由于会话 2 已经持有它而失败。(Column2 = 2)
- 会话 2:尝试获取第 1 行的更新锁,但由于会话 1 已经持有它而失败。(Column2 = 1)
- 检测到死锁。
在这里,我的假设是每个查询可能以不同的顺序扫描行,这意味着对行的锁定将以不同的顺序进行。
解决方案
我同意其他人所说的——从两个不同的会话运行这个特定的查询不会导致死锁,因为不涉及交叉引用。
所以让我们看看当我们尝试时会发生什么......
我这样设置你的 TABLEX:
CREATE TABLE dbo.TableX
(
[id] INT NOT NULL IDENTITY (1,1)
CONSTRAINT [PK_TableX] PRIMARY KEY CLUSTERED
, [Column1] CHAR(1) NULL
, [Column2] INT NULL
CONSTRAINT [UQ_TableX_Column2] UNIQUE
) ;
WITH cte_Nums AS
(
SELECT 1 AS [n]
UNION ALL
SELECT [n] + 1
FROM cte_Nums
WHERE [n] < 500000
)
INSERT
INTO dbo.TableX ( [Column1], [Column2] )
SELECT 'x', [n]
FROM cte_Nums
OPTION ( MAXRECURSION 0 ) ;
GO
然后我从 2 个不同的会话(spid 61 和 58)运行相同的 UPDATE 查询,让事务挂起以查看使用了哪些锁:
BEGIN TRAN ;
UPDATE dbo.TableX
SET [Column1] = '1'
WHERE [Column2] BETWEEN 1 AND 10000 ;
然后,在第三个会话中,我跑去sp_lock
获取锁定信息:
EXEC sp_lock 61, 58 ;
结果如下:
蜘蛛 | 出价 | 对象标识 | 工业标识 | 类型 | 资源 | 模式 | 地位 |
---|---|---|---|---|---|---|---|
58 | 11 | 0 | 0 | D B | 小号 | 授予 | |
61 | 11 | 0 | 0 | D B | 小号 | 授予 | |
61 | 11 | 565577053 | 0 | 标签 | X | 授予 | |
58 | 11 | 565577053 | 0 | 标签 | 九 | 等待 |
因此,无论如何,在这种情况下,SQL Server 将整个表的排他 (X) 锁授予第一个查询 (spid 61),从而防止第二个查询 (spid 58) 接触任何行,直到第一个更新完成。
鉴于您提供的示例,您的问题的答案是否定的。同样的 UPDATE 查询,从不同的会话并发运行,不会导致死锁,因为要更新的锁是在整个表上获取的,而不是在单个行上。
推荐阅读
- php - mysql,从多个表中选择数据,表中没有公共字段
- php - 带有分页的手风琴项目的 Codeigniter 链接
- php - PHP For循环中的每7个和8个表格单元格样式
- java - 即使使用 table.setRowSelectionAllowed(true) 添加颜色后也无法选择 jtable 行
- c# - ManualResetEvent 在 Mono 中无法按预期工作
- ruby - ruby gsub 和 preg_replace 中的排序替换
- javascript - 如何在不进行任何 API 调用的情况下获取浏览器 IP 地址和访问我网站的访问者国家/地区
- android - 在 Android Studio 版本 3.1.3 上测量我的应用性能
- ibm-cloud - 为什么我在 Watson IoT Platform 上的历史数据库服务不断变得“未配置”?
- chef-infra - chef-server-ctl 重新配置失败