SqlServer 并发事务三:闩锁

/*

闩锁:

当数据页从磁盘读取前,数据库引擎会先在内存中预留适当是内存页,给这些内存加闩锁,数据才能顺利地读到内存。(如果一个线程没法立即获得闩锁,线程则等待,让CPU给其他线程使用)

 

自旋锁:与闩锁类型的概念.不同闩锁的是:如果一个线程没法立即获得自旋锁,线程则开始轮询,重复检查资源是否可用以继续使用.当然页不会长时间轮询,不久就会让出CPU给其他线程使用.

*/

DBCC SQLPERF('',Clear)


--闩锁总累计等待次数和时间
SELECT wait_type,wait_time_ms,waiting_tasks_count,wait_time_ms/NULLIF(waiting_tasks_count,0) AS avg_wait_time
FROM sys.dm_os_wait_stats
WHERE wait_type LIKE 'LATCH%'
or wait_type LIKE 'PAGELATCH%'
or wait_type LIKE 'PAGEIOLATCH%'

--各种类闩锁详细累计等待次数和时间
SELECT * FROM sys.dm_os_latch_stats


--查看自旋锁
SELECT * FROM sys.dm_os_spinlock_stats

DBCC SQLPERF(spinlockstats)


LATCH:

LatchSQLserver内部用来同步资源访问的一个数据结构和操作系统的criticalsection ReaderWriterLock类似Latch保护了那些想保护的资源使得访问同步有序比方说当某个线程获得某个资源的latch的独占使用权的时候别的线程如果也需要访问这个latch则它必须等待

 

PAGELATCH:

用来同步访问数据库PAGElatch就是PAGELATCHSQLserverBuffpool里每个数据库页8kbPAGE都有一个对应的LATCH要访问某个PAGE必须首先获得这个PAGELATCHPAGELATCH有很多种如共享的PAGELATCH_SH,独占的PAGELATCH_EX独占的意思是排他性访问共享的意思是可以有多个线程同时获得这个latch

 

PAGEIOLATCH:

就是当这个数据库页不在内存里面必须从磁盘读取的时候那么latch的类型就是PAGEIOLATCH其它方面和PAGELATCH一样



【闩锁示例】

--	DROP TABLE LatchTable
create table LatchTable(id int,name varchar(1))

insert into LatchTable values(1,'A')

SELECT * FROM [test].[dbo].[LatchTable]

--	查看表数据页
DBCC IND(test,'dbo.LatchTable',-1)

--	找到数据页PageFID=1,PagePID=430

--	查看数据页内容
DBCC TRACEON(3604)
DBCC PAGE('test',1,897,1)

 

PAGE: (1:897)


BUFFER:


BUF @0x043BF460

bpage = 0x0CAE0000                   bhash = 0x00000000                   bpageno = (1:897)
bdbid = 7                            breferences = 0                      bcputicks = 0
bsampleCount = 0                     bUse1 = 6652                         bstat = 0xc0000b
blog = 0x5979bbbb                    bnext = 0x00000000                   

PAGE HEADER:


Page @0x0CAE0000

m_pageId = (1:897)                   m_headerVersion = 1                  m_type = 1
m_typeFlagBits = 0x4                 m_level = 0                          m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 225    m_indexId (AllocUnitId.idInd) = 256  
Metadata: AllocUnitId = 72057594052673536                                 
Metadata: PartitionId = 72057594048610304                                 Metadata: IndexId = 0
Metadata: ObjectId = 491148795       m_prevPage = (0:0)                   m_nextPage = (0:0)
pminlen = 8                          m_slotCnt = 1                        m_freeCnt = 8078
m_freeData = 112                     m_reservedCnt = 0                    m_lsn = (356:315:18)
m_xactReserved = 0                   m_xdesId = (0:0)                     m_ghostRecCnt = 0
m_tornBits = 0                       

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 16,DumpStyle BYTE

Record Type = PRIMARY_RECORD         Record Attributes =  NULL_BITMAP VARIABLE_COLUMNS
Record Size = 16                     
Memory Dump @0x646CC060

00000000:   30000800 01000000 02000001 00100041 †0..............A         

OFFSET TABLE:

Row - Offset                         
0 (0x0) - 96 (0x60)                  


DBCC 执行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

/*

页头PAGE HEADER:  m_slotCnt = 1,m_freeData = 112

页数据DATA:    Slot 0,Length16

偏移量OFFSET TABLE:   Row- Offset 0 (0x0) - 96 (0x60)

 

数据页中有一行数据(m_slotCnt = 1),长度为个字节(Length16),在页面中96的位置开始.

从第112位开始是空的(m_freeData = 112 ).可确定所在位置(96+16=112).

*/



--现在模拟说明不存在闩锁的使用原理(实际模拟不出)
--事务1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
WAITFOR TIME '23:00:00'
insert into LatchTable values(2,'B')

--事务2
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
WAITFOR TIME '23:00:00'
insert into LatchTable values(3,'C')


DBCC PAGE('test',1) –假如输出页信息如下


PAGE: (1:897)
BUFFER:
BUF @0x043BF460
…………
PAGE HEADER:
Page @0x0CAE0000
…………
pminlen = 8                          m_slotCnt = 2                        m_freeCnt = 8060
m_freeData = 128                     m_reservedCnt = 0                    m_lsn = (359:205:2)
…………       
DATA:
Slot 0,DumpStyle BYTE
…………  
Slot 1,Offset 0x70,DumpStyle BYTE
…………
Row - Offset                                        
1 (0x1) - 112 (0x70)                 
0 (0x0) - 96 (0x60)  

假设同时对某个数据页中插入数据(实际模拟不出),两个事务并发执行,此时都可以访问数据行,都能在页面得到意向排他锁(IX)。缓冲区管理器将在内存中读取各自的数据页。假如【事务1】先到达数据页,根据页原有的记录(m_slotCnt = 2m_freeData= 128)将新的行数据分配到插槽(Slot1)中,页头等信息还没有被更改。此时!【事务2】到达,也根据页原有的记录将新的行数据分配到插槽(Slot 1)中,因此把第一个事务的数据给覆盖了!!同样,页头信息【事务2】也覆盖【事务1】。


--存在闩锁情况

当【事务1】获得内存中的页时,它会获得一个页的排他闩锁。【事务2】此时就必须等待【事务1】完成采可执行。可在视图上查看sys.dm_os_latch_stats(BUFFER)sys.dm_os_wait_stats(PAGELATCH_*)。当【事务1】更改完成后即释放这个闩锁,不管【事务1】的整个事务是否完成。因此,闩锁不依赖与事务,它只保护内存中的数据完整性。

--锁模式s
SELECT * FROM sys.dm_xe_map_values where name = 'latch_mode'

可以看到LASTLATCH的六种闩锁模式:
NL: 内部使用的空(null)闩锁.
KP: 保持闩锁,一个页面在请求保持不被销毁.
SH: 共享闩锁,去读取时使用.与事务中的共享锁概念类似.
UP: 常见的更新闩锁.更新闩锁与共享闩锁兼容(与update不同,update使用排他锁)
EX: 显式更改或添加时使用.
DT: 删除页面时,页面从惰性写入器获得一个销毁闩锁.如果页面没有正在使用或有保持锁(KP),页面数据则从缓存删除写入磁盘.数据库引擎维护一个哈希表记录当前页面的内存地址.


BUF闩锁'PAGELATCH,PAGEIOLATCH:(其他为Non-BUF)
SELECT * FROM sys.dm_os_latch_stats where latch_class='BUFFER' –buf闩锁总累计值

SELECT * FROM sys.dm_os_wait_stats –buf闩锁个详细值
WHERE wait_type LIKE 'PAGELATCH%' or wait_type LIKE 'PAGEIOLATCH%'


--闩锁监视
SELECT * FROM sys.dm_os_wait_stats  where wait_type=''—-某个闩锁累计值
SELECT * FROM sys.dm_os_waiting_tasks where wait_type=''—当前正在等待该值的session

相关文章

本篇内容主要讲解“sqlalchemy的常用数据类型怎么使用”,感...
今天小编给大家分享一下sqlServer实现分页查询的方式有哪些的...
这篇文章主要介绍“sqlmap之osshell怎么使用”,在日常操作中...
本篇内容介绍了“SQL注入的知识点有哪些”的有关知识,在实际...
1. mssql权限sa权限:数据库操作,文件管理,命令执行,注册...
sql执行计划如何查看?在SPL庞大的数据中我们不知道如何查看...