要理解Latch和Spinlock,你需要知道它们真正的定义以及为什么SQL Server需要它们。
定义
Latch是SQL Server引擎使用的一个内部对象,它不是你能够直接影响得到的。如果你需要从一个特定的页获取数据,SQL Server需要获取一个Latch,对于这一点你别无选择,要获取哪种Latch,也是由SQL Server引擎决定的。区别就是这不仅关系数据保护,还关系内存保护。即便你愿意容忍脏读,并相应地选择了你的锁策略,你对Latch也没那么奢侈。
Spinlock和Latch的概念类似,因为它也是轻量级同步原语,但它们稍有不同。主要的不同点是,如果一个线程获取Latch失败了,它会立即屈服,使CPU能够用于其他事情。如果一个线程获取Spinlock失败了,线程会开始循环(looping/spinning),重复地检查资源,带着资源立即可用的期望,然而,它不会永远spin,一会儿时间后,它会后退,向CPU上的其他进程屈服。下面将展示模拟的实例。
Latching实例
创建环境:
CREATE DATABASE LatchInAction;
GO
USE LatchInAction;
CREATE TABLE dbo.LatchTable
( COL1 INT
,COL2 INT
);
INSERT INTO dbo.LatchTable ( COL1, COL2 )
VALUES (1,100);
通过PagePID查看页的输出信息:
DBCC TRACEON(3604);
DBCC PAGE('LatchInAction',1,118,1);
输出结果中值得注意的信息有PAGE HEADER中m_slotCnt和m_freeData,DATA中的Slot 0及Length=15,OFFSET TABLE中的Offset 96。这告诉我们页里有一个单行(slot),即Slot 0, 它有15个字节长度,从位置96开始,位置111后面是空的(freedata)。无独有偶,111=96+15。现在通过两个Session分别各插入一笔数据:
/*TRANSACTION 1 SESSION 1*/
INSERT INTO LatchTable
VALUES (2,200);
/*TRANSACTION 2 SESSION 2*/
INSERT INTO LatchTable
VALUES (3,300);
两笔插入是并发的,同时被锁管理器接收。两者都不存在行,所以该行没有X锁。两个Session都收到一个页上的IX锁,彼此一致。通过DBCC PAGE再次查看,其输出如下:DBCC execution completed. If DBCC printed error messages, contact your system administrator.
PAGE: (1:118)
BUFFER:
BUF @0x0000000080126E40
bpage = 0x00000001E85CC000 bhash = 0x0000000000000000 bpageno = (1:118)
bdbid = 12 breferences = 0 bcputicks = 0
bsampleCount = 0 bUse1 = 9861 bstat = 0x10b
blog = 0x15a bnext = 0x0000000000000000
PAGE HEADER:
Page @0x00000001E85CC000
m_pageId = (1:118) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 84 m_indexId (AllocUnitId.idInd) = 256
Metadata: AllocUnitId = 72057594043432960
Metadata: PartitionId = 72057594039042048 Metadata: IndexId = 0
Metadata: ObjectId = 245575913 m_prevPage = (0:0) m_nextPage = (0:0)
pminlen = 12 m_slotCnt = 3 m_freeCnt = 8045
m_freeData = 141 m_reservedCnt = 0 m_lsn = (32:252:2)
m_xactReserved = 0 m_xdesId = (0:0) m_ghostRecCnt = 0
m_tornBits = 1862860983 DB Frag ID = 1
Allocation Status
GAM (1:2) = ALLOCATED SGAM (1:3) = ALLOCATED
PFS (1:1) = 0x61 MIXED_EXT ALLOCATED 50_PCT_FULL DIFF (1:6) = CHANGED
ML (1:7) = NOT MIN_LOGGED
DATA:
Slot 0, Offset 0x60, Length 15, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 15
Memory Dump @0x000000000940A060
0000000000000000: 10000c00 01000000 64000000 020000 ........d......
Slot 1, Offset 0x6f, Length 15, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 15
Memory Dump @0x000000000940A06F
0000000000000000: 10000c00 02000000 c8000000 020000 ...............
Slot 2, Offset 0x7e, Length 15, DumpStyle BYTE
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP Record Size = 15
Memory Dump @0x000000000940A07E
0000000000000000: 10000c00 03000000 2c010000 020000 ........,......
OFFSET TABLE:
Row - Offset
2 (0x2) - 126 (0x7e)
1 (0x1) - 111 (0x6f)
0 (0x0) - 96 (0x60)
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
当Transaction 1(2,200)到达内存页时,它获取一个latch,这是一个EX Latch。然而片刻之后,Transaction 2(2,300)也想要一个EX Latch,但不能得到。它必须等待Transaction 1完成,你可以在sys.dm_os_wait_stats看到等待。
Transaction 1需要保留EX Latch来完成写入行、更新页头和偏移(offset),在释放Latch之前,它不会等待事务的完成。锁保护事务的完整性,而Latch保护内存的完整性。一旦Transaction 1的Latch释放,3,300事务就会进入,并拥有EX Latch,插入数据行、更行页头及偏移。
没有Latching,数据会丢失;有了latching就不会。不管事务使用哪种隔离级别,SQL Server都会通过Latch保护数据。