SQL Server里因丢失索引造成的死锁
在今天的文章里我想演示下SQL Server里在表上丢失索引如何引起死锁(deadlock)的。为了准备测试场景,下列代码会创建2个表,然后2个表都插入4条记录。
-- Create a table without any indexes
CREATE TABLE Table1
(
Column1 INT,
Column2 INT
)
GO -- Insert a few record
INSERT INTO Table1 VALUES (1, 1)
INSERT INTO Table1 VALUES (2, 2)
INSERT INTO Table1 VALUES (3, 3)
INSERT INTO Table1 VALUES (4, 4)
GO -- Create a table without any indexes
CREATE TABLE Table2
(
Column1 INT,
Column2 INT
)
GO -- Insert a few record
INSERT INTO Table2 VALUES (1, 1)
INSERT INTO Table2 VALUES (2, 2)
INSERT INTO Table2 VALUES (3, 3)
INSERT INTO Table2 VALUES (4, 4)
GO
在我向你重现死锁前,先看下列的代码,它是个简单的UPDATE语句,在第1个表里更新一个指定行。
-- Acquires an Exclusive Lock on the row
UPDATE Table1 SET Column1 = 3 WHERE Column2 = 1
因为在Column2上没有索引定义,对于我们的UPDATE语句,查询优化器在执行计划里必须选择表扫描(Table Scan)运算符来查找符合的记录:

这就是说我们必须扫描整个堆表来找我们想更新的行。在那个情况下,SQL Server用排它锁(Exclusive Lock)锁定表里的第1行。当你在不同的会话执行一个SELECT语句,引用另一个堆表里“将发生”的行,表扫描(Table Scan)运算符会阻塞,因为首先你必须读取所有堆表里“已发生”的行,即获取你查询里逻辑请求的行。
-- This query now requests a Shared Lock, but get's blocked, because the other session/transaction has an Exclusive Lock on one row, that is currently updated
SELECT Column1 FROM Table1
WHERE Column2 = 4
表扫描(Table Scan)默认意味这你必须扫描整个表,因此你必须在每条记录上获得共享锁(Shared Lock)——即使在你逻辑上不请求的记录上。如果你用不同的顺序,在不同的会话里访问2个表,当你从同个表尝试读写时,这个情况会导致死锁情形。下面代码显示来自第1个查询的事务:
BEGIN TRANSACTION -- Acquires an Exclusive Lock on the row
UPDATE Table1 SET Column1 = 3 WHERE Column2 = 1 -- Execute the query from Session 2...
-- This query acquires an Exclusive Lock on one row from Table2... -- This query now requests a Shared Lock, but get's blocked, because the other session/transaction has an Exclusive Lock on one row, that is currently updated
SELECT Column1 FROM Table2
WHERE Column2 = 3 ROLLBACK TRANSACTION
GO
下面显示来自第2个事务的代码:
BEGIN TRANSACTION -- Acquires an Exclusive Lock on the row
UPDATE Table2 SET Column1 = 5 WHERE Column2 = 2 -- Continue with the query from Session 2...
-- This query now requests a Shared Lock, but get's blocked, because the other session/transaction has an Exclusive Lock on one row, that is currently updated -- This query now requests a Shared Lock, but get's blocked, because the other session/transaction has an Exclusive Lock on one row, that is currently updated
SELECT Column1 FROM Table1
WHERE Column2 = 4 ROLLBACK TRANSACTION
GO
从2个事务可以看到,2个表在不同的顺序里被访问。如果时机合适,在同个时间运行这2个事务会导致死锁(deadlock)情形。假设下列的执行顺序:
- 在Table1上第1个事务运行UPDATE语句。
- 在Table2上第2个事务运行UPDATE语句。
- 在Table2上第1个事务运行SELECT语句。这个SELECT语句会阻塞,因为表扫描(Table Scan)运算符想要在行上获得的共享锁(Shared Lock),已经被第2个事务排它锁(exclusively lock)锁定。
- 在Table1上第2个事务运行SELECT语句。这个SELECT语句会阻塞,因为表扫描(Table Scan)运算符想要在行上获得的共享锁(Shared Lock),已经被第1个事务排它锁(exclusively lock)锁定。
下图演示了这个死锁情形:

现在2个事务相互阻塞,因此在SQL Server里你引起了死锁。在那个情况下死锁监控器(Deadlock Monitor)后台进程踢入,进行最“便宜”的事务的回滚(基于事务需要写入事务日志的字节数)。
你可以在2个表里通过为Column2提供一个索引来轻松解决这个死锁。在那个情况下SQL Server可以进行符合列的查找(Seek)运算符操作,因此当你执行SELECT语句时,可以跳过已经在索引叶子层的锁定行:
CREATE NONCLUSTERED INDEX idx_Column2 ON Table1(Column2)
CREATE NONCLUSTERED INDEX idx_Column2 ON Table2(Column2)
GO
下图演示了现在的死锁情形是怎样的:

使用查找操作你可以跳过索引叶子层的锁定行,你可以避免我们已经讨论过的死锁。因此当你在你的数据库看到死锁情形时,仔细看下你的索引战略(设计),这非常重要!在SQL Server里,索引一直是一个很重要的东西——始终记住这个!
感谢关注!
参考文章:
https://www.sqlpassion.at/archive/2014/11/24/deadlocks-caused-by-missing-indexes-in-sql-server/
SQL Server里因丢失索引造成的死锁的更多相关文章
- 在SQL Server里禁用聚集索引——真的好么?
有人问了我一个最有意思的问题:“你能禁用聚集索引么?” 对这个问题,我先是吓了一跳,因为我从未想过禁用聚集索引,因为聚集索引代表表数据,对这个最有趣问题,我立即答道:“我认为可以,但是...” 好吧, ...
- 在SQL Server里如何处理死锁
在今天的文章里,我想谈下SQL Server里如何处理死锁.当2个查询彼此等待时会发生死锁,没有一个查询可以继续它们的操作.首先我想给你大致讲下SQL Server如何处理死锁.最后我会展示下SQL ...
- SQL Server里如何处理死锁
在今天的文章里,我想谈下SQL Server里如何处理死锁.当2个查询彼此等待时会发生死锁,没有一个查询可以继续它们的操作.首先我想给你大致讲下SQL Server如何处理死锁.最后我会展示下SQL ...
- SQL Server里如何处理死锁 (转)
http://www.cnblogs.com/woodytu/p/6437049.html 在今天的文章里,我想谈下SQL Server里如何处理死锁.当2个查询彼此等待时会发生死锁,没有一个查询可以 ...
- 在SQL Server里如何进行页级别的恢复
在今天的文章里我想谈下每个DBA应该知道的一个重要话题:在SQL Server里如何进行页级别还原操作.假设在SQL Server里你有一个损坏的页,你要从最近的数据库备份只还原有问题的页,而不是还原 ...
- 在SQL Server里如何进行数据页级别的恢复
在SQL Server里如何进行页级别的恢复 关键词:数据页修复 在今天的文章里我想谈下每个DBA应该知道的一个重要话题:在SQL Server里如何进行页级别还原操作.假设在SQL Server里你 ...
- SQL Server里在文件组间如何移动数据?
平常我不知道被问了几次这样的问题:“SQL Server里在文件组间如何移动数据?“你意识到这个问题:你只有一个主文件组的默认配置,后来围观了“SQL Server里的文件和文件组”后,你知道,有多 ...
- SQL Server里的文件和文件组
在今天的文章里,我想谈下SQL Server里非常重要的话题:SQL Server如何处理文件的文件组.当你用CREATE DATABASE命令创建一个简单的数据库时,SQL Server为你创建2个 ...
- SQL Server里的闩锁介绍
在今天的文章里我想谈下SQL Server使用的更高级的,轻量级的同步对象:闩锁(Latch).闩锁是SQL Server存储引擎使用轻量级同步对象,用来保护多线程访问内存内结构.文章的第1部分我会介 ...
随机推荐
- Redis 数据结构之Keys
这是Redis官方文档的keys列表 (1) set key value--设置某个键为某个值 (2) get key -- 获取设置的值 (3)del key -- 删除设置的键 (4)expir ...
- Mysql 如何设置字段自动获取当前时间
应用场景: 1.在数据表中,要记录每条数据是什么时候创建的,不需要应用程序去特意记录,而由数据数据库获取当前时间自动记录创建时间: 2.在数据库中,要记录每条数据是什么时候修改的,不需要应用程序去特意 ...
- 解决adb server端口被占用的问题
先执行adb nodaemon server ,查看adb server的端口是多少 C:\Users\xxxx>adb nodaemon server cannot bind 'tcp:5 ...
- nginx 反向代理 与 Apache backend的配置联合配置
nginx 反向代理 与 Apache backend的配置联合配置: 说明: nginx 将http映射到Apache上的特定子目录. 配置方法步骤: 1. 设置域名, 子域名映射到指定服务器ip ...
- asp.net发布到IIS中出现错误:处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler”
asp.net发布到IIS中出现错误:处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler” http:// ...
- java利用透明的图片轮廓抠图
需要处理的图片: 1.png(空白区域为透明) 2.png 处理后的结果图片:result.png 代码如下: import java.awt.Graphics2D; import java.awt. ...
- 在 Excel 中使用正则表达式进行查找与替换
在 Excel 中,使用 Alt+F11 快捷键打开 VBA 项目窗口,在左侧的工作表名称上点右键,选择查看代码,即可出出现右侧的代码编辑窗口 在代码窗口中输入以下代码: Private Sub Re ...
- vs多项目模板及add-in开发
本文分2部分 第一为自定义多项目模板 第二为vs add-in开发 效果图 1.自定义模板 2. 工具菜单 3.窗口 4.工程 5.文件 ... 一. 多项目模板 单项目模板做起来很简单 选中一个项目 ...
- LoadRunner执行过程报错“Failed to connect to server "xxx.xxx.xxx.xxx:xx":[10060] connetion time out”
执行性能测试过程中,LR报错: Action.c(6):Error -27796: Failed to connect to server "xxx.xxx.xxx.xxx:xx" ...
- 在线PDF编辑网站http://www.pdfescape.com
网站地址:http://www.pdfescape.com 先转载一个简单介绍的文章 如果你以前很少阅读PDF文档,电脑中也没有PDF阅读器:adobe reader,foxit reader之类的软 ...