SQL Server 死锁 (Page锁)诊断

在数据库中打开死锁监测可以收集到数据库发生的死锁情况。打开的方式有2种:

1 打开1222监控

执行SQL语句:

Dbcc traceon(1222,-1);

然后在系统日志里查看死锁的信息。

2 启动SQL Profiler(建议使用):

下面就是一个发生死锁的实例图:

下面提供对这个死锁分析思路,如有不当之处,还望大家批评指正。

一共3个问题,下面逐个回答。

第一个问题:被锁定的资源是什么?

上面写的很清楚,是一个Page 锁, 那么Page 锁是什么呢?

通常死锁是你操作A表,然后又要操作B表,而另外一个进程先操作的B表,然后等待你释放A表的锁而导致的死锁,这时看的图上面就会明确说Table Lock,而不是Page Lock。

要解释Page Lock 得先解释下什么是Page

在SQL Server 中,数据存放的时候是放在一个8K的数据单元里,这个数据单元称为 Page.

读取数据时,不是一笔一笔的读取,而是一个Page 一个Page 的读取,所以SQL Server 的文档中都会说,可能读取了一些不需要的数据。

知道了Page 之后,怎么会有Page 锁呢? Table Lock ,Row Lock 比较容易理解,但是Page Lock 就不太容易理解,实际上,这时SQL Server 对于大表采取的一种它认为性价比最好的策略。如果采取表锁,那么一旦Update 数据,则别人就不能Select 了,否则会出现数据不一致的情况(就是所谓的脏读,幻读),但是你有可能会说,你修改你的,我读取的并不是你要改的那一笔,你不要锁定我,应该采取Row Lock,但是想象一下,对于一个有100万笔数据的表来说,采取行锁,成本得有多高?所以SQL Server采取了一个中庸的方式,使用8K的数据页Page作为锁的单位,这样就平衡了并发与性能的问题,性价比最高。

定位Page

一个表里有很多Page,那么本例中锁住的Page 到底是什么呢?

执行SQL语句查询DB的名字

Select db_name(5)

打开这个数据库,执行如下SQL语句,可以查询这个Page 属于谁.

     select object_name(i.object_id), i.name
     from sys.partitions as p

     inner join sys.indexes as i on i.object_id = p.object_id and i.index_id = p.index_id
     where p.partition_id = ‘72057594048544768‘

是一个非聚集的索引。

Page里放了什么?

目前我们已经了解了,它锁定的是一个索引的一个数据页(如果您对索引的Page 组织结构感兴趣,请参考 索引内部结构实例)。 那么锁住的2个Page里面存放了什么信息?

执行如下SQL:

DBCC TRACEON(3604)

DBCC Page (DBName,1,46574417,1)

结果如下:

执行如下SQL:

DBCC Page (DBName,1,93865146,1)

结果如下:

第二个问题:是谁在锁住这些页?

查看之后发现:

1个SQL语句是Select

2个SQL 语句是Update

Select 语句为什么要锁定这2页?

本例中的Select 语句使用了本索引,使用它来快速定位,那么它根据索引的分支节点寻找,然后到指定的Page 中去读取寻找,因为是Top 500,所以,可能找到一批数据,但是还不够500,所以接着往其他页里找,直到找满500笔为止

Update 语句为什么要锁定这2页?

找到这笔数据直接修改不就完了吗?并不是的,修改一个索引的值,至少要经过如下3个步骤:

第三个问题: Exchange Event是啥意思?

本例分析总结:

某个Index 里有多个数据页,而 Update 语句正在更新这个Index 里的一笔数据,锁定了2个页,与Select 中使用的2页发生互抢,形成死锁。

解决方案:

调整语句,调整Select 吗?

让它不使用这个索引?索引建立就是为了给Select 用的,这个Select 不用,其他Select 也会用到。

调整 Update 吗?

因为更新的栏位是包含在索引中的,无论怎么调整,更新时总是会同步修改索引的。

结语:

死锁是不可避免的,我们要做的是降低发生的频率,这是我们常听到的,但是为什么呢? 如果是更新表的顺序不一致,那么应该可以通过修改提交更新SQL可以消除,但是本例呢?你打算通过什么方式让这种情况永不存在

降低发生的频率是对我们的要求,比如本例,怎么降低?

    降低SQL 执行的频率:

发出Select 语句和Update 语句太频繁,业务上是否真的这么频繁?是否都定相同的时间来执行?比如Update和Select 都是5秒轮一次?

    提高SQL执行的效率:

Select 语句返回的数据有多少笔?能否再减少一些?返回有多少栏位?能否减少一些?

Select 语句的查询计划是否应该优化?最多3-5页面读取就应该返回结果,这个Select有多少logic read ?是否需要添加其他准确定位的索引?(难道一个Select 我们就添加一个Index吗?当然不是,这是在频繁出现这种死锁的情况使用的,因为既然频繁出现,说明这个 Select执行更频繁,很容易和Update 撞在一起。)

能不能Select 不要锁?

业务上是否可以允许小范围的脏读?

时间: 2024-08-07 09:55:36

SQL Server 死锁 (Page锁)诊断的相关文章

SQL Server中的锁的简单学习

原文:SQL Server中的锁的简单学习 简介 在SQL Server中,每一个查询都会找到最短路径实现自己的目标.如果数据库只接受一个连接一次只执行一个查询.那么查询当然是要多快好省的完成工作.但对于大多数数据库来说是需要同时处理多个查询的.这些查询并不会像绅士那样排队等待执行,而是会找最短的路径执行.因此,就像十字路口需要一个红绿灯那样,SQL Server也需要一个红绿灯来告诉查询:什么时候走,什么时候不可以走.这个红绿灯就是锁. 图1.查询可不会像绅士们那样按照次序进行排队 为什么需要

T-SQL查询进阶—理解SQL Server中的锁

在SQL Server中,每一个查询都会找到最短路径实现自己的目标.如果数据库只接受一个连接一次只执行一个查询.那么查询当然是要多快好省的完成工作.但对于大多数数据库来说是需要同时处理多个查询的.这些查询并不会像绅士那样排队等待执行,而是会找最短的路径执行.因此,就像十字路口需要一个红绿灯那样,SQL Server也需要一个红绿灯来告诉查询:什么时候走,什么时候不可以走.这个红绿灯就是锁. 图1.查询可不会像绅士们那样按照次序进行排队 为什么需要锁 在开始谈锁之前,首先要简单了解一下事务和事务的

Update导致SQL Server死锁的典型方法(转载)

此文为转载文章,描述的很好,没有验证过. 最近遇到了一个看上去很奇怪,分析起来很有意思的死锁问题.这个死锁看上去难以理解.而分析过程中,又使用了很多分析SQL Server死锁的典型方法.记录下来整个分析过程还是很有意义的. 问题重现步骤: 经过提炼,问题重现的步骤非常简单,在SQL 2008上可以很容易地重现. 1.         首先,创建一张表格,上面有一个clustered index,两个non-clustered index. create table tt(id int iden

SQL Server中的锁可以分为如下几类

从大类来看,SQL Server中的锁可以分为如下几类: 共享锁(S锁):用于读取资源所加的锁.拥有共享锁的资源不能被修改.共享锁默认情况下是读取了资源马上被释放.比如我读100条数据,可以想像成读完了第一条,马上释放第一条,然后再给第二条数据上锁,再释放第二条,再给第三条上锁.以此类推直到第100条.这也是为什么我在图9和图10中的查询需要将隔离等级设置为可重复读,只有设置了可重复读以上级别的隔离等级或是使用提示时,S锁才能持续到事务结束.实际上,在同一个资源上可以加无数把S锁. 排他锁(X锁

SQL Server死锁产生原因及解决办法

SQL Server死锁产生原因及解决办法 2006-07-18 05:12:10 分类: SQL Server 其实所有的死锁最深层的原因就是一个:资源竞争 表现一: 一个用户A 访问表A(锁住了表A),然后又访问表B,另一个用户B 访问表B(锁住了表B),然后企图访问表A,这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了,同样用户B要等用户A释放表A才能继续这就死锁了. 解决方法: 这种死锁是由于你的程序的BUG产生的,除了调整你的程序

SQL Server中的锁

NOLOCK(不加锁) 此选项被选中时,SQL Server 在读取或修改数据时不加任何锁. 在这种情况下,用户有可能读取到未完成事务(Uncommited Transaction)或回滚(Roll Back)中的数据, 即所谓的“脏数据”. HOLDLOCK(保持锁) 此选项被选中时,SQL Server 会将此共享锁保持至整个事务结束,而不会在途中释放. UPDLOCK(修改锁) 此选项被选中时,SQL Server 在读取数据时使用修改锁来代替共享锁,并将此锁保持至整个事务或命令结束.使用

SQL Server中的锁类型及用法

一. 为什么要引入锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 丢失更新 A,B两个用户读同一数据并进行修改,其中一个用户的修改结果破坏了另一个修改的结果,比如订票系统 脏读 A用户修改了数据,随后B用户又读出该数据,但A用户因为某些原因取消了对数据的修改,数据恢复原值,此时B得到的数据就与数据库内的数据产生了不一致 不可重复读 A用户读取数据,随后B用户读出该数据并修改,此时A用户再读取数据时发现前后两次的值不一致 并发控制的主要方法是封锁,锁就是在一段时间内禁止用户做某些

快钱支付与Sql Server的乐观锁和悲观锁

在实际的多用户并发访问的生产环境里边,我们经常要尽可能的保持数据的一致性.而其中最典型的例子就是我们从表里边读取数据,检查验证后对数据进行修改,然后写回到数据库中.在读取和写入的过程中,如果在多用户并发的环境里边,其他用户已经把你要修改的数据进行了修改是非常有可能发生的情况,这样就造成了数据的不一致性. 最近在做快钱支付的时候就碰到了这个问题,原来的代码如下:1. 表Order的结构:    OrderId   int 自增长    Status   nvarchar(10)  //未处理时的状

SQL SERVER 数据库的锁

1SQL SERVER 锁的概念 共享锁:用于读取资源所加的锁.拥有共享锁的资源不能被修改.共享锁在默认情况下是读取了资源马上被释放. 排他锁:和其他锁都不兼容,包括其他排他锁,排它锁用于修改数据,当资源上加了排他锁时,其他请求读取或修改这个资源的事物都会被阻塞,直到排他锁被释放为止. 更新锁:是共享锁和排它锁的结合,用于更新数据,更新数据时首先需要找到被更新的数据,此时可以理解为被查找的数据上了共享锁.当找到需要修改的数据时,需要对被修改的资源上排他锁. sql server 通过更新锁来避免