有关Latch争用

Latch是什么

    Latch是SQL
Server引擎保证内存中的协会的一致性的轻量同步机制。比如索引,数据页和内部结构(比如非叶级索引页)。SQL
Server使用Buffer Latch爱抚缓冲池中的页,用I/O
Latch保养还免加载到缓冲池的页,用Non-Buffer Latch敬重内存中的内部结构。

  1. Buffer
    Latch:当工作线程访问缓冲池中之有页往日,必须要先期得此页的Latch。重要用以掩护用户对象与系对象的页。等待类型表现为PAGELATCH_*
  2. I/O
    Latch:当工作线程请求访问的页未在缓冲池中不时,就会面发作一个异步I/O从存储系统将相应之页加载到缓冲池中。那些历程会得到相应页上 I/O
    Latch,防止任何线程使用不般配的Latch加载同一页到缓冲池中。等待类型表现也PAGEIOLATCH_*
  3. Non-Buffer
    Latch:珍重缓冲池页之外的内内存结构时选拔。等待类型表现也LATCH_XX。

   
Latch只于相应页或者内部结构被操作间具有,而休像锁这样以一切业务中有着。比如,使用WITH
NOLOCK查询某表。查询过程遭到无会面当表达的其它层级上落共享锁,可是以数据页可以读取前,需要取得这一个的页的Latch。

 

Latch的模式

   Latch跟锁一样,是SQL
Server引擎并发控制的同样片段。在高并发环境下之Latch争用的气象无可制止。SQL
Server使用情势兼容性强制互不兼容的有限只Latch请求线程中之一个待其他一个得操作并释放目标资源及之Latch后,才会顾是目标资源。

Latch有5种模式:

  • KP – Keep Latch 保证引用的布局不可知吃摔
  • SH – Shared Latch, 读数据页的时节要
  • UP – Update Latch 更改数据页的早晚需要
  • EX – Exclusive Latch 独占形式,紧要用于形容数据页的时需要
  • DT – Destroy Latch 在毁掉引用的数据结构时所用 

Latch情势兼容性,Y表示相当,N表示未兼容:

KP SH UP EX DT
Y Y Y Y N
Y Y Y N N
Y Y N N N
Y N N N N
N N N N N

 

  • 潜移默化Latch争用的要素
因素
明说
使用过多的逻辑CPU
任何多核的系统都会出现Latch争用。Latch争用超过可接受程度的系统,多数使用16个或者以上的核心数。
架构设计和访问模式
B树深度,索引的大小、页密度和设计,数据操作的访问模式都可能会导致过多的Latch争用
应用层的并发度过高
多数Latch争用都会伴有应用层的高并发请求。
数据库逻辑文件的布局
逻辑文件布局影响着分配单元结构(如PFS,GAM,SGAM,IAM等)的布局,从而影响Latch争用程度。最著名的例子就是:频繁创建和删除时临表,导致tempdb的PFS页争用
I/O子系统性能
大量的PAGEIOLATCH等待就说明SQL Server在等待I/O子系统。

 

诊断Latch争用

   诊断的基本点方法以及工具包括:

  •     观察性监视器的CPU利用率和SQL
    Server等待时,判断双方是否持有关联性。
  •     通过DMV获取引起Latch争用的现实项目及资源。
  •     诊断某些Non-Buffer Latch争用,可能还需得到SQL
    Server进程的内存转储文件并构成Window调试工具并分析。

 
  Latch争用是同种正常的移动,唯有当也博得目的资源的Latch而起的争用和等时影响了网吞吐量时,才当是祸的。为了确定一个客观之争用程度,需要组合性能、系统吞吐理、IO和CPU资源并分析。

 

经过等待时衡量Latch争用对利用性能的影响程度

  1.页平均Latch等待时加强以及网吞吐理增长一致

    假诺页平均Latch等待时加强及系统吞吐理增长一致,特别是Buffer
Latch等待时加强的跨了蕴藏系统的应时间,就应用sys.dm_os_waiting_tasks检查时的等候任务。还亟需整合系统活动和负载特征观看。诊断的相似经过:

  •    使用“Query sys.dm_os_waiting_tasks Ordered by Session
    ID”脚本或者“Calculate Waits Over a 提姆(Tim)e
    Period”脚本观看当前底等待任务和平均Latch等待时意况。
  •  
     使用“QueryBufferDescriptorsToDetermineObjectsCausingLatch”脚本确定争用暴发的岗位(索引以及注脚)。
  •    使用性能计数器观看MSSQL%InstanceName%\Wait Statistics\Page
    Latch Waits\Average Wait
    提姆(Tim)e或者查询sys.dm_os_wait_stats观看页平均Latch等待时。

 2.事情高峰期Latch等待时占总待时百分比

 
 如若Latch等待时比率随着以负载增长而直线增长,可能Latch争用会影响属性,需要优化。通过伺机总结性计数观看Page和Non-Page
Latch等待情形。然后以的同CPU\RAM\IO\Network吞吐量等息息相关的计数器相比较。例如使用Transactions/sect和Batch
Requests/sec衡量资源使用境况。

   
sys.dm_os_wait_stats没有含有等待类型的待时,这是盖她记录是的从上次实例启动(或清空)未来的守候数。也得以经dbcc
SQLPERF (‘sys.dm_os_wait_stats’,
‘CLEAR’)手动清空它。在事情高峰前落一软sys.dm_os_wait_stats数据,在业务高峰时得到一糟,然后总计差别。

 

 3. 系统吞吐理没有增长(甚至下降),同时拔取负载加重,SQL
Server可用的CPU增添

   
在高并发和多CPU系统面临,对近似自增聚集索引的面世插入,会招同种植如现:CPU数量加,而网吞吐量下降,同时Latch页等待会扩充。

 

 4. 行使负载增长时CPU利用率却不曾增长。

   当CPU利用率没有随着以出现负载增长要加强,表达SQL
Server在等某种资源(Latch争用的展现)。

 

询问时底Latch

  下边的询问好查看时实时底等候音讯。wait_type为PAGELATCH_*
和PAGEIOLATCH_*的即为Buffer Latch的等待。

SELECT wt.session_id, wt.wait_type
, er.last_wait_type AS last_wait_type
, wt.wait_duration_ms
, wt.blocking_session_id, wt.blocking_exec_context_id, resource_description
FROM sys.dm_os_waiting_tasks wt
JOIN sys.dm_exec_sessions es ON wt.session_id = es.session_id
JOIN sys.dm_exec_requests er ON wt.session_id = er.session_id
WHERE es.is_user_process = 1
AND wt.wait_type <> 'SLEEP_TASK'
ORDER BY wt.wait_duration_ms desc 

 

询问再次来到列的表明:

说明
Session_id
task所属的session id
Wait_type
当前的等待类型
Last_wait_type
上次发生等待时的等待类型
Wait_duration_ms
此等待类型的等待时间总和(毫秒)
Blocking_session_id
当前被阻塞的session id
Blocking_exec_context_id
当前task的ID
Resource_description
具体等待的资源

脚的查询重返Non-Buffer Latch的消息

select * from sys.dm_os_latch_stats where latch_class <>
‘BUFFER’ order by wait_time_ms desc

回到列的认证:

说明
Latch_class
Latch类型
Waiting_requests_count 
当前Latch类型发生的等待次数
Wait_time_ms
当前Latch类型发生等待的时间总和
Max_wait_time_ms
当前Latch类型发生等待最长时间

 

Latch争用的广阔现象

尾页的数目插入争用

以聚集索引表上,假若聚集索引的首列(leading
key)是一个一成不变增长的排列(如打增列和Datetime),则可能导致Latch争用。

这种情状被表除了以归档时丰盛少执行删除和更新操作;日常表很酷,行于狭小。

安插入一久数据到目录中的步骤:

  1. 查找B树,定位到新行将要被储存的页

2.
以此页上添加排他Latch(PAGELATCH_EX),防止其他操作而修改是页。然后在装有非叶级页中添加共享Latch(PAGELATCH_SH).

    有时也碰面在非叶页获取排他Latch,如页拆分时直吃震慑的非叶页。

  1. 为日志文件写一久记下,表示此行已经深受改动

  2. 通向页中形容着新行并记为脏页

  3. 释放具有Latch.

如表底目是遵照有序增长的键,则新行都会师为插入到B树的最后一页,直到就页存满。高并发负载下,就会造成聚集和不聚集索引B树被最为左侧页为争用。常常这种连发争用生在为插队操作为主且页密度较生的目录上。通过sys.dm_db_index_operational_stats的得考察到页Lath争用和B树中非叶级页Latch争用的情况。

推个例子:

线程A和线程B同时于尾页(比如1999页)插入新行。逻辑上,两者都得又取得尾页中针对应行的行级排他锁。可是,为掩护内存完整性一破单可以暴发一个线程获取到页上革除异Latch。假诺A拿到取EX
Latch,则B就需要等。则B就相会在
sys.dm_os_waiting_tasks表现有等类型为PAGELATCH_EX的等待。

 

具非聚集索引的小表上举行任意插入导致的Latch争用

   
将表当做临时队列结构使,平常会产出这种景观。在满意上面的条件时,就可能出现Latch争用(包括EX及SH):

  • 高并发的INSERT,DELETE,UPDATE和SELECT操作
  • 页密度较充裕,行于狭窄
  • 表的行数较少,所以B树也比肤浅,索引深度在2~3级。

比肤浅的B树及进行大气随意的INSERT极可能引致页拆分。执行页拆分时,需要在B树有层级上获取SH
Latch和在数码暴发修改的装有页上获取EX
Latch。在老高并发的INSERT和DELETE情状,还极可能造成B树ROOT页发生拆分。ROOT页拆分会招Non-Buffer
Latch:ACCESS_METHODS_HBOT_VIRTUAL_ROOT。

可以通过下的脚本观看表的目深度:

图片 1图片 2

select o.name as [table], i.name as [index], 
indexProperty(object_id(o.name), i.name, 'indexDepth')
+ indexProperty(object_id(o.name), i.name, 'isClustered') as depth, --clustered index depth reported doesn't count leaf level 
i.[rows] as [rows], i.origFillFactor as [fillFactor],  
case (indexProperty(object_id(o.name), i.name, 'isClustered'))
when 1 then 'clustered'
when 0 then 'nonclustered'
else 'statistic'
end as type
from sysIndexes i
join sysObjects o on o.id = i.id
where o.type = 'u'
and indexProperty(object_id(o.name), i.name, 'isHypothetical') = 0 --filter out hypothetical indexes 
and indexProperty(object_id(o.name), i.name, 'isStatistics') = 0 --filter out statistics 
order by o.name

view index depth

 

PFS页的Latch争用

   
这属于分红瓶颈的平栽意况。PFS记录在数据页的上空利用情形。PFS页上动1独字节(Byte)表示一个页的接纳状态。一个PFS页可以代表8088单数据页,所以各8088单数据页就会晤发一个PFS页。一个数据文件的亚独页就是PFS页(PageID=2)。

    当需要分配空间为新目标或数操作,SQL Server会在PFS页上得SH
Latch查看目的区区是否发可用的页。即便有,则会于PFS上获 UP
Latch,并改进对应页的半空中应用消息。类似之过程吧会暴发在SAM,GSAM页上。在多CPU的系及,文件组中就出深少的数据文件,过多之PFS页请求,则恐造成Latch争用。边种场景在Tempdb中谋面相相比常表现数。

   
当在Tempdb的PFS或者SGAM页上冒出较多PATHLATCH_UP等时,可以运用下的不二法门消除Latch争用:

  • 搭Tempdb的数据文件个数,个数=CPU的主旨数
  • 启用跟踪标记TF 1118

 

Tempdb中的表值函数导致的Latch争用

   
这一个有的由跟PFS的Latch争用一样。每一次调用多报句的表值函数总要开创同去表变量,当于一个询问的反复引用多报句之表值函数时就是可能会生很多底表变量创设和去操作,从而导致Latch争用。

 

缓解不同格局下之Latch争用

运都有些列将数据分布到拥有的索引键区间

 
在银行ATM系统场景被考虑以ATM_ID将本着交易表的INSERT操作分布及有键值区间。因为一个用户同一时间只会于应用同样贵ATM。这样虽然拿销售类此外现象中,可以设想以Checkout_ID列或者Store_ID列。这种措施欲以唯复合索引。这多少个复合索引的首键平时以被选定的标识列或者某些列经过Hash的总计列,然后再一次结合其他列共同整合。使用Hash总计列会较佳,因为标识列的唯一值会太多,造成键值区间过多,也汇合让表数据的大体构造变差。即使Hash总结列的寻找引键值区间相比较少,但对于散落INSERT负载收缩Latch争用而言,已经够了。比如销售系统受到,可以用Store_ID对CPU核数取模做啊Hash值。

 
这种模式会加大索引碎片,降低范围扫描的习性。可能还索要修改以架构,例如语句的WHERE需要遵照新的目结构举行调。

演示:在一个32核心的体系受到之交易表

原表结构:

图片 3图片 4

create table table1
(
TransactionID   bigint   not null,
UserID   int   not null,
SomeInt   int   not null
)
go
alter table table1
add constraint pk_table1
primary key clustered (TransactionID, UserID)
go
方式1.:使用UserID做为索引首键,将INSERT操作分布到所有数据页上。需要注意的是:索引修改后,所有SELECT的WHERE等式中需要同时指定UserID和TransactionID。
create table table1
(
TransactionID   bigint   not null,
UserID   int   not null,
SomeInt   int   not null
)
go
alter table table1
add constraint pk_table1
primary key clustered (UserID, TransactionID)
go
方式2:使用TransactionID对CPU核数取模做为索引首键,将INSERT操作较均匀分布到表上。
create table table1
(
TransactionID bigint not null,
UserID int not null,
SomeInt int not null
)
go
-- Consider using bulk loading techniques to speed it up
ALTER TABLE table1
ADD [HashValue] AS (CONVERT([tinyint], abs([TransactionID])%(32))) PERSISTED NOT NULL
alter table table1
add constraint pk_table1
primary key clustered (HashValue, TransactionID, UserID)
go

View Code

 

以GUID列做啊索引首键

   
那多少个自己我很勿确认,所以啊非思分析了,只是为着保障完整性加于此间。并发负载和Latch争用严重到需要GUID这种极端的措施来分散负载时,分表、分区或者使NoSQL类产品才是重好之不二法门。

 

动用总括列对表举行Hash分区

  表分区能压缩Latch争用。使用总括列对表举行Hash分区,一般的手续:

  1. 新建或用现有的文书组承载各分区
  2. 设选取新文件组,需要考虑IO子系统的优化和文件组中数据文件的合理布局。假使INSERT负载占比较高,则文件组的数据文件个数指出呢大体CPU核心数之1/4(或者1/2,或者当,视情状而早晚)。
  3. 用CREATE PARTITION
    FUNCTION将表分成N个分区。N值等于上同样步之数据文件的个数。
  4. 下CREATE PARTITION
    SCHEME绑定分区函数到文件组,然后重新补充加一个smallint或者tinyint类型的Hash列,再计出合适的Hash分布值(例如HashBytes值取模
    或者取Binary_Checksum值)。

演示代码:

图片 5图片 6

--Create the partition scheme and function, align this to the number of CPU cores 1:1 up
to 32 core computer
-- so for below this is aligned to 16 core system
CREATE PARTITION FUNCTION [pf_hash16] (tinyint) AS RANGE LEFT FOR VALUES
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
CREATE PARTITION SCHEME [ps_hash16] AS PARTITION [pf_hash16] ALL TO ( [ALL_DATA] )

-- Add the computed column to the existing table (this is an OFFLINE operation)
-- Consider using bulk loading techniques to speed it up
ALTER TABLE [dbo].[latch_contention_table]
ADD [HashValue] AS (CONVERT([tinyint], abs(binary_checksum([hash_col])%(16)),(0)))
PERSISTED NOT NULL
--Create the index on the new partitioning scheme
CREATE UNIQUE CLUSTERED INDEX [IX_Transaction_ID]
ON [dbo].[latch_contention_table]([T_ID] ASC, [HashValue])
ON ps_hash16(HashValue)

View Code

 

分区后,逻辑上INSERT依旧集中到表尾部,不过Hash分区将INSERT分散到每分区的B树的尾。所以能抽Latch争用。如图:

图片 7

 图片 8图片 9

 

以Hash分区消除INSERT的Latch争用,需要权衡的事项:

  • 一般性SELECT语句之询问称词有用改,使其蕴含Hash分区排。这会招致查询计划之分区消除无可用。
  • 某些其他查询(如按照范围查询的表格)也非克采纳分区消除。
  • 当分区表和另外一个表JOIN时,若是采用分区消除,则此外一个讲明需在平的键上实现Hash分区并且Hash键需要包括于JOIN条件里。
  • Hash分区会如滑动窗口归档和分区归档功用不可用。

总结

  1. 本文为SQLCAT的Latch Contention白皮书为底蕴,结合保罗(Paul)Randal关于Latch的博文,以及本人的更而成。

  2. Buffer Latch Contention较易定位及拍卖。Non-Buffer Latch
    是相比困难的,因为十分少关于此类型Latch的表达材料,造成有时一定到项目,也非了解她是什么意思,无从出手。

 

叠加代码:

 LatchContentionScripts.7z

相关文章