Key lock 的秘密

研究死锁,或者观察sp_lock,有时候最恼人的莫过于你看到下面研究成果的key lock,但是却不知道究竟是哪个page 哪个row被lock住了:

Exec sp_lock:


 
 
就说上面的key (9dd27be994c0) 吧,能不能知道这个key,究竟是对应于那个table,那个data page,甚至哪一行(row)呢?

可以的。且听我慢慢说来。

先说这一行:

52        20        978102525      2          KEY      (9dd27be994c0)                      X          GRANT

其中20就是dbid了,indid 2表示是nonclustered  index,而978102525就是objectid了。这个比较好懂。你可以使用Select object_name(978102525)来得到表的名字:

而Key (9dd27be994c0)其实是键值的哈希(hash)值。Hash出来的值,你无法逆向得到它的键值。但是幸运的是,你可以使用微软的undocumented的宏,%%lockres%%,来对表的所有键进行一次同样的hash运算,然后你就可以从hash的结果集里面找到你想要的键值啦。首先,需要知道index为2的是那个index:

select name,index_id,type_desc from sys.indexes where object_id=object_id(‘test‘)

name           
index_id    type_desc

--------------- ----------- ---------------------

cidx1           1           CLUSTERED

idx1            2           NONCLUSTERED

然后就可以使用%%lockres%%了:

SELECT   %%lockres%% ‘key‘, test.*  FROM  test with(index(idx1))

或者:

SELECT   %%lockres%% ‘key‘, test.*  FROM  test with(index=2)

结果如下:

你可以看到,对应sp_lock 的key的hash为9dd27be994c0的行,就是c1=2的那行。简单点你甚至可以使用下面 的语句直接得到某个key hash值的行:

SELECT   %%lockres%% ‘key‘, test.*  FROM  test with(index(idx1)) where %%lockres%% =‘(9dd27be994c0)‘

再进一步,能否得到该行对应的page和fileid呢?这个就需要使用另一个undocumented的宏%%physloc%%了:

select %%physloc%%,test.*  from test with(index(idx1))

上面的返回值是一个十六进制的值,表示page,file,和slot id。可以使用sys.fn_physlocformatter函数将它转换成一个可读的(file:page:slot)格式的值:

select %%physloc%%,sys.fn_physlocformatter(%%physloc%%) ‘physical location‘,test.*  from test with(index(idx1))

结果如下:

对应第二行数据的,就在37828这个page了。这个时候,你可以使用DBCC PAGE来打印出它的内容,探究slot为2 的那行:

dbcc traceon(3604)

dbcc page(20,1,37828,1)

结果:

PAGE: (1:37828)

BUFFER:

BUF @0x00000000803BB400

bpage = 0x000000002CAA6000         
bhash = 0x0000000000000000         
bpageno = (1:37828)

bdbid = 20                         
breferences = 0                    
bcputicks = 0

bsampleCount = 0                   
bUse1 = 42097                       bstat = 0x10b

blog = 0x5ab21c9a                  
bnext = 0x0000000000000000

PAGE HEADER:

Page @0x000000002CAA6000

m_pageId = (1:37828)               
m_headerVersion = 1                
m_type = 2

m_typeFlagBits = 0x0                m_level = 0                         m_flagBits = 0x0

m_objId (AllocUnitId.idObj) = 194  
m_indexId (AllocUnitId.idInd) = 256

Metadata: AllocUnitId = 72057594050641920

Metadata: PartitionId = 72057594046251008                                Metadata: IndexId = 2

Metadata: ObjectId = 978102525     
m_prevPage = (0:0)                 
m_nextPage = (0:0)

pminlen = 5                        
m_slotCnt = 5                      
m_freeCnt = 7997

m_freeData = 217                    m_reservedCnt = 0                   m_lsn = (17704:185:18)

m_xactReserved = 0                 
m_xdesId = (0:20808688)            
m_ghostRecCnt = 0

m_tornBits = -1577081955           
DB Frag ID = 1

Allocation Status

GAM (1:2) = ALLOCATED              
SGAM (1:3) = ALLOCATED

PFS (1:32352) = 0x60 MIXED_EXT ALLOCATED   0_PCT_FULL                    DIFF (1:6) = CHANGED

ML (1:7) = NOT MIN_LOGGED

DATA:

Slot 0, Offset 0x60, Length 16, DumpStyle BYTE

Record Type = INDEX_RECORD         
Record Attributes =  NULL_BITMAP
VARIABLE_COLUMNS

Record Size = 16

Memory Dump @0x000000005166A060

0000000000000000:   36010000
00030000 01001000 61626364          
6...........abcd

Slot 1, Offset 0x70, Length 22, DumpStyle BYTE

Record Type = INDEX_RECORD         
Record Attributes =  NULL_BITMAP
VARIABLE_COLUMNS

Record Size = 22

Memory Dump @0x000000005166A070

0000000000000000:   36010000
00030000 02001200 16006162 63640100 
6.............abcd..

0000000000000014:   0000                                         
..

Slot 2, Offset 0xc9, Length 16, DumpStyle BYTE

Record Type = INDEX_RECORD          Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS

Record Size = 16

Memory Dump @0x000000005166A0C9

0000000000000000:   36020000
00030000 01001000 31323334          
6...........1234

Slot 3, Offset 0x96, Length 18, DumpStyle BYTE

Record Type = INDEX_RECORD         
Record Attributes =  NULL_BITMAP
VARIABLE_COLUMNS

Record Size = 18

Memory Dump @0x000000005166A096

0000000000000000:   36030000
00030000 01001200 65656566 6666     
6...........eeefff

Slot 4, Offset 0xa8, Length 17, DumpStyle BYTE

Record Type = INDEX_RECORD         
Record Attributes =  NULL_BITMAP
VARIABLE_COLUMNS

Record Size = 17

Memory Dump @0x000000005166A0A8

0000000000000000:   36040000
00030000 01001100 38397070 70       
6...........89ppp

OFFSET TABLE:

Row - Offset

4 (0x4) - 168 (0xa8)

3 (0x3) - 150 (0x96)

2 (0x2) - 201 (0xc9)

1 (0x1) - 112 (0x70)

0 (0x0) - 96 (0x60)

DBCC execution completed. If DBCC printed error messages, contact your system administrator.

注意,这个行就是index record,和indid=2完全吻合。

参考:http://blogs.msdn.com/b/apgcdsd/archive/2014/08/20/key-lock.aspx

时间: 2024-11-06 07:35:33

Key lock 的秘密的相关文章

关于InnoDB的Next-Key lock

最近一段时间在准备新员工培训的材料,本来打算介绍介绍概念就OK的,但是既然写了事务的章节,就特别想介绍一下锁,介绍了锁,就忍不住想介绍一下Next-Key Lock. 大家知道,标准的事务隔离级别有READ UNCOMMITTED,READ COMMITTED,REPEATED READ和SERIALIZABLE.其中InnoDB默认实现了REPEATED READ级别,这个级别可以解决非一致性读的问题,但是不能解决幻读的问题,不过InnoDB采用了Next Key Lock算法,在该级别实现了

C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)

本文来自:http://www.cnblogs.com/SkySoot/archive/2012/04/02/2430295.html 本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而对每种方式的使用细节,适用场合不会过多解释. 让我们来看看这几个类的关系图: 1. lock 关键字     lock 是 C# 关键词,它将语句块标记为临界区,确

gap lock/next-key lock浅析Basic-Paxos协议日志同步应用

http://www.cnblogs.com/renolei/p/4673842.html 当InnoDB在判断行锁是否冲突的时候, 除了最基本的IS/IX/S/X锁的冲突判断意外, InnoDB还将锁细分为如下几种子类型: record lock (RK) 记录锁, 仅仅锁住索引记录的一行 gap lock (GK) 区间锁, 仅仅锁住一个区间(开区间) insert intention lock (IK) 意向插入锁 next key lock (NK) record lock + gap

gap lock/next-key lock浅析 Basic-Paxos协议日志同步应用

http://www.cnblogs.com/renolei/p/4673842.html 当InnoDB在判断行锁是否冲突的时候, 除了最基本的IS/IX/S/X锁的冲突判断意外, InnoDB还将锁细分为如下几种子类型: record lock (RK) 记录锁, 仅仅锁住索引记录的一行 gap lock (GK) 区间锁, 仅仅锁住一个区间(开区间) insert intention lock (IK) 意向插入锁 next key lock (NK) record lock + gap

WPF 依赖属性与依赖对象

在介绍依赖属性之前,我先介绍下属性的历史 属性的历史: 早期C++的类中,只有字段及方法,暴露数据靠的是方法, 但是字段直接暴露会不安全,所以才用方法来暴露,在设置的时候加些约束,在MFC中就是这样的.但是为了访问某一个字段,总有设置及获得两个方法,太过分散,不利于管理.所以在C#中又引入了属性的概念,后来WPF又引入了依赖属性,可以节省实例对内存的开销,还可以通过binding依赖在其他对象上. 注意:字段是每个实例都要占用内存开销,而属性就如同方法(可以反编译查看,其实就是两个方法,这表示属

Ehcache(08)——可阻塞的Cache—BlockingCache

可阻塞的Cache-BlockingCache 在上一节我们提到了显示使用Ehcache锁的问题,其实我们还可以隐式的来使用Ehcache的锁,那就是通过BlockingCache.BlockingCache是Ehcache的一个封装类,可以让我们对Ehcache进行并发操作.其内部的锁机制是使用的net.sf.ehcache.concurrent.ReadWriteLockSync,与显示锁调用是一样的实现,而ReadWriteLockSync内部使用的是Java的java.util.conc

MySQL锁小结

锁的作用:避免并发请求时对同一个数据对象同时修改,导致数据不一致. 怎么加锁: 1.事务T1在对某个数据对象R1操作之前,先向系统发出请求,对其加锁L1. 2.之后,事务T1对该数据对象R1有了相应的控制,在T1释放L1之前,其它事务不能修改R1. 锁类型: 1.排它锁(X). 2.共享锁(S). 通常的锁范围: 1.全局锁(global lock). 2.表锁(table lock). 3.行锁(row lock). InnoDB行锁范围(粒度): 1.record lock. 2.gap l

<转>一个最不可思议的MySQL死锁分析

1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2.1 Delete操作的加锁逻辑 4 2.2 死锁预防策略 5 2.3 剖析死锁的成因 6 3 总结 7 死锁问题背景 做MySQL代码的深入分析也有些年头了,再加上自己10年左右的数据库内核研发经验,自认为对于MySQL/InnoDB的加锁实现了如指掌,正因如此,前段时间,还专门写了一篇洋洋洒洒的文章,专门分析MySQL的加锁实现细节:<MySQL加锁处理分

2.RABBITMQ 入门 - WINDOWS - 生产和消费消息 一个完整案例

关于安装和配置,见上一篇 1.RABBITMQ 入门 - WINDOWS - 获取,安装,配置 公司有需求,要求使用winform开发这个东西(消息中间件),另外还要求开发一个日志中间件,但是也是要求做成win form的,这明显不合理,因为之前,服务器上我已经放置了一个  短信的winform的服务.那么到后期的话,登录服务器之后,全是 一个个的窗体挂在那儿,这明显合不合常理,但是领导要求这么玩,也没办法, 因为卧虎要负责的是消费 消息,所以重点说明 消费端 该案例的接收端,源自网上的代码片段