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部分我会介 ...
随机推荐
- IntelliJ IDEA 2016.2.4 最新版激活方法
新版激活方法: 1.在线激活 2016年7月14日 更新: 该域名已无法激活,参见2016.2 的搭建授权服务器激活, 菜单help >>>> Register 选择Licen ...
- java中获取比毫秒更为精确的时间
所以这里提醒做非常精确的时间统计的朋友,谨慎使用System.currentTimeMillis() . 在Java中可以通过System.currentTimeMillis()或者System.na ...
- linux centos java 应用服务器配置
备忘: https://oneinstack.com/ 1.用root装jdk nginx.tomcat. 2.配置tomcat主机,上传应用.修改数据库连接帐号,修改log4j文件路径.3.安装my ...
- 旧手机作为USB无线网卡使用(分享WIFI、蓝牙连接)
首先开启手机的WIFI或者蓝牙功能,建立访问互联网的连接,然后设置-更多-网络共享与便携热点,打开安卓手机USB网络共享功能,即可在计算机上通过手机(无电话卡.数据卡)访问互联网.而且此时手机一直在充 ...
- 【Vegas原创】RHEL6.2安装vmtools
1,mount虚拟cdrom # mount /dev/cdrom /mnt/cdrom 2, ls一下,看有没有 # cd /mnt/cdrom # ls –l ...
- PHP多次调用Mysql存储过程报错解决办法
PHP多次调用Mysql数据库的存储过程会出现问题,主要问题为存储过程中执行多次SQL语句不能一一释放导致的,网上找了一些解决办法,比如使用 multi_query 然后一个一个释放,但是发现根本不适 ...
- S7-200系列PLC与WINCC以太网通信CP243i的实例
S7-200系列PLC与WINCC以太网通信CP243i的实例 ----选用大连德嘉国际电子www.dl-winbest.cn的CP243i作为连接S7-200的PPI口转以太网RJ45的接口转换器. ...
- Linux--Ubuntu中文文件夹转英文
前言 在安装Ubuntu的时候,如果选择的系统语言为汉语,安装完成后,Ubuntu系统的主文件夹下的几个文件目录就是中文的,这样才纯终端下,输入起来确实非常的不方便.当然,如果安装Ubuntu的时候, ...
- Codeforces Beta Round #80 (Div. 2 Only)【ABCD】
Codeforces Beta Round #80 (Div. 2 Only) A Blackjack1 题意 一共52张扑克,A代表1或者11,2-10表示自己的数字,其他都表示10 现在你已经有一 ...
- Asp.net Core 使用Redis存储Session
前言 Asp.net Core 改变了之前的封闭,现在开源且开放,下面我们来用Redis存储Session来做一个简单的测试,或者叫做中间件(middleware). 对于Session来说褒贬不一, ...