设为首页 收藏本站
查看: 1755|回复: 0

[经验分享] SQL Serer闩锁 和 闩锁超时故障排除

[复制链接]

尚未签到

发表于 2018-10-21 12:05:19 | 显示全部楼层 |阅读模式
  SQL Serer闩锁 和 闩锁超时故障排除
  翻译自:https://mssqlwiki.com/2012/09/07/latch-timeout-and-sql-server-latch/
  在一个多线程的进程里,当一个线程在内存里更新一个数据或索引页,而另一个线程正在读取相同的页,将会发生什么?
  当第一个线程在内存里读取一个数据或索引页,而第二个线程正在从内存里释放相同的页,将会发生什么?
  答案是:我们将获得数据或数据结构不一致的结果。为了避免不一致,SQL Server使用同步机制像锁(Locks)、闩锁(Latches)和自旋锁(Spinlocks)。
  在这篇博文里,我们将讨论关于闩锁的一些关键点和如何排除闩锁超时dump故障。
  什么是闩锁(Latch)?
  它们通过多线程控制对数据页和结构的并发访问。闩锁提供数据页的物理数据一致性,并提供数据结果的同步。闩锁不可以像锁一样被用户控制。
  闩锁的类型:
  Buffer(BUF) Latch
  用于同步访问BUF结构和它们相关的数据库页。
  Buffer "IO" Latch
  Buffer Latch的一个子集,用于当BUF和相关的数据/索引页正在一个IO操作(从磁盘读取页或者写入页到磁盘)中间时。
  Non-Buffer(Non-BUF) Latch
  这些闩锁被用于同步一般的内存中数据结构,这些结构通常被并行线程、自动增长操作和收缩操作等查询/任务执行所使用。
  闩锁模式:
  Keep(KP) Latches
  用于确保当页面正在使用时,不会从内存释放。
  Shared(SH) Latches
  用于对数据结构的只读访问,和阻止其他线程的写访问。
  这个模式允许共享访问。
  SH可兼容于KP、SH和UP。应该注意的是,尽管通常SH表明了只读访问,但不总是这样。对于Buffer Latches,SH是为了读取一个数据页的最小模式要求。
  Update(UP) Latches
  允许对数据结构(兼容于SH和KP)的读访问,但是阻止其他EX-latch访问。
  当页分裂检测关闭并且当AWE没有启用时用于写操作。
  Exclusive(EX) Latches
  阻止发生在被闩锁结构上的读取活动。EX只兼容于KP。
  当页分裂检测开启或者AWE启用时在读IO和写IO期间。
  Destroy(DT) Latches
  用于当从Buffer Pool移除BUFS,要么通过添加它们到空闲列表,要么取消映射AWE Buffers。
  闩锁兼容性:
  KP     SH     UP     EX     DT
  KP     Y     Y     Y     Y     N
  SH     Y     Y     Y     N     N
  UP     Y     Y     N     N     N
  EX     Y     N     N     N     N
  DT     N     N     N     N     N
  如何识别闩锁争用?
  闩锁争用可以通过在sysprocesses里的等待类型来识别。
  PAGEIOLATCH_*:
  这个sysprocesses里的等待类型表明SQL Server正在等待一个Buffer Pool页的物理I/O完成。
  1.PAGEIOLATCH_*通常通过调优查询来解决,该查询会执行大量的IO操作(通常通过添加、修改和移除索引或统计信息来介绍物理IO数量)。
  2.识别是否有磁盘瓶颈并修复它们(PAGEIOLATCH等待时间(例如大于30ms))。
  PAGELATCH_*:
  这个sysprocesses里的等待类型表明SQL Server正在等待访问一个数据库页,但是该页没有经历物理IO。
  1.这个问题通常由在同一时间试图访问相同物理页的大量会话导致。你有应该查看spid的等待资源。这个wait_resource是被访问的页号(格式是dbid:file:pageno)。
  2.我们可以使用DBCC PAGE来识别对象或者发生争用的页类型。它也帮助我们确定是否争用是用于分配、数据或文本。
  3.如果SQL Server最频繁等待的页面在tempdb数据库,在dbid为2对于一个页号检查等待资源列。你可能面临着在这里提到的tempdb闩锁争用:http://support.microsoft.com/kb/328551
  4.如果页在一个用户数据库,检查是否表在一个单调键像标识列上有一个聚集索引,所有的线程正在争用表末尾的相同页。在这种情况下,我们需要选择一个不同的聚集索引键,将工作分散到不同的页。
  LATCH_*:
  Non-buf闩锁等待可以被各种事情导致。我们可以在sysprocesses里使用这个等待资源列来确定包含的闩锁类型(KB 822101)。
  1.一个非常普通的LATCH_EX等待是由于运行一个Profiler跟踪或者sp_trace_getdata参考KB 929728获取更多信息。
  2.自动增长和自动收缩。
  当一个闩锁被线程请求,并且如果由于其他线程在相同的页或数据结构上持有一个不兼容的闩锁,而这个闩锁不能被立即授予,那么这个请求者必需等待闩锁可被授予。如果等待间隔达到5分钟(waittime 300),类似以下的一条警告信息在SQL Server错误日志中输出,并且所有线程的一个mini dump被捕获。警告信息对buffer和non-buffer latches有所区别。
844: Time out occurred while waiting for buffer latch — type %d, bp %p, page %d:%d, stat %#x, database id: %d, allocation unit id: %I64d%ls, task 0x%p : %d, waittime %d, flags 0x%I64x, owning task 0x%p.  Continuing to wait.  
846: A time-out occurred while waiting for buffer latch — type %d, bp %p, page %d:%d, stat %#x, database id: %d, allocation unit Id: %I64d%ls, task 0x%p : %d, waittime %d, flags 0x%I64x, owning task 0x%p. Not continuing to wait.
  
847: Timeout occurred while waiting for latch: class ‘%ls’, id %p, type %d, Task 0x%p : %d, waittime %d, flags 0x%I64x, owning task 0x%p. Continuing to wait.
  
  拆分以上警告
  类型(type):
  当前闩锁获取请求的闩锁模式。这个一个使用如下匹配的numerical值:
  0 – NL (not used); 1 – KP; 2 – SH; 3 – UP; 4 – EX; 5 – DT.
  任务(task):
  我们尝试得到闩锁的任务。
  等待时间(Waittime):
  等待闩锁获取请求的以秒为单位的总时间。
  拥有的任务(owning task):
  可用的拥有闩锁的任务地址。
  bp(只有Buffer latches):
  与Buffer latch对应的BUF结构的地址。
  page(只有Buffer latches):
  对于当前包含在BUF结构中的页的页ID。

  database>  对于在BUF里的页的数据库ID。
  像排除SQL Server里的阻塞问题一样,当有一个闩锁争用或者超时dump,识别闩锁的所有者并故障排除为什么闩锁被该所有者长时间持有。
  当有闩锁超时dump,你可以看到类似以下一个的警告信息。在dump是非常重要的用于找到闩锁的所有者线程之前,警告错误信息打印在SQL Server错误日志里。
2012-01-18 00:52:03.16 spid69      A time-out occurred while waiting for buffer latch — type 4, bp 00000000ECFDAA00, page 1:6088, stat 0x4c1010f, database id: 4, allocation unit Id: 72057594043367424, task 0x0000000006E096D8 : 0, waittime 300, flags 0x19,  
owning task 0x0000000006E08328. Not continuing to wait.
  
spid21s     **Dump thread – spid = 21, PSS = 0x0000000094622B60, EC = 0x0000000094622B70
  
spid21s     ***Stack Dump being sent to E:\Data\Disk1\MSSQL.1\MSSQL\LOG\SQLDump0009.txt
  
spid21s     * *******************************************************************************
  
spid21s     * BEGIN STACK DUMP:
  
spid21s     *   02/28/12 00:32:03 spid 21
  
spid21s     * Latch timeout
  
Timeout occurred while waiting for latch: class ‘ACCESS_METHODS_HOBT_COUNT’, id 00000002D8C32E70, type 2, Task 0x00000000008FCBC8 : 7, waittime 300, flags 0x1a, owning task 0x00000000050E1288. Continuing to wait.
  
Timeout occurred while waiting for latch: class ‘ACCESS_METHODS_HOBT_VIRTUAL_ROOT’, id 00000002D8C32E70, type 2, Task 0x00000000008FCBC8 : 7, waittime 300, flags 0x1a, owning task 0x00000000050E1288. Continuing to wait.
  从以上错误信息,我们可以很容易理解,我们正尝试在数据库ID为4,页1:6088(第一个文件的6088页)请求闩锁,并且因为任务0x0000000006E08328(在警告信息中拥有任务0x0000000006E08328)正在它上面持有一个闩锁而超时。
  注意:任务只是被线程执行的一个工作请求。(就像system task、login task和ghost cleanup task等)。执行这个任务的线程将按需持有需要的闩锁。
  让我们看看如何分析闩锁超时dump和使用拥有任务0x0000000006E08328获取闩锁的拥有线程。
  去分析dump,从这里http://sdrv.ms/MO6ytG下载和安装Windows Debugger。
  步骤1:
  打开Windbg。选择“File”菜单,选择“Open crash dump”,选择“Dump file”(SQLDump000#.mdmp)。
  步骤2:
  在命令行窗口输入
  .sympath srv*c:\Websymbols*http://msdl.microsoft.com/download/symbols;
  步骤3:
  输入.reload /f并回车。这将强制debugger立即加载所有的符号。
  步骤4:
  通过使用debugger命令lmvm验证是否符号被SQL Server加载。
  0:002> lmvm sqlservr
  start             end                 module name
  00000000`01000000 00000000`03679000   sqlservr T (pdb symbols)          c:\websymbols\sqlservr.pdb\21E4AC6E96294A529C9D99826B5A7C032\sqlservr.pdb
  Loaded symbol image file: sqlservr.exe
  Image path: C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe
  Image name: sqlservr.exe
  Timestamp:        Wed Oct 07 21:15:52 2009 (4ACD6778)
  CheckSum:         025FEB5E
  ImageSize:        02679000
  File version:     2005.90.4266.0
  Product version:  9.0.4266.0
  File flags:       0 (Mask 3F)
  File OS:          40000 NT Base
  File type:        1.0 App
  File date:        00000000.00000000
  Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4
  步骤5:
  使用以下命令来搜索线程堆栈来识别与拥有的任务相关的线程,并且它是拥有闩锁的线程。在你的错误日志里使用拥有的任务替代0X0000000006E08328
  ~*e .echo ThreadId:; ?? @$tid; r? @$t1 = ((ntdll!_NT_TIB *)@$teb)->StackLimit; r? @$t2 = ((ntdll!_NT_TIB *)@$teb)->StackBase; s -d @$t1 @$t2 0X0000000006E08328
  ThreadId:
  unsigned int 0x93c
  ThreadId:
  unsigned int 0x9a0
  ThreadId:
  unsigned int 0x9b4
  00000000`091fdaf0  06e08328 00000000 00000000 00000000  (……………
  00000000`091fdcb8  06e08328 00000000 091fdd70 00000000  (…….p…….
  00000000`091fded0  06e08328 00000000 06e0e798 00000000  (……………
  00000000`091fdf38  06e08328 00000000 00000002 00000000  (……………
  00000000`091fec60  06e08328 00000000 0168883a 00000000  (…….:.h…..
  00000000`091ff260  06e08328 00000000 000007d0 00000000  (……………
  00000000`091ff2d0  06e08328 00000000 00000020 00000000  (……. …….
  00000000`091ff5f8  06e08328 00000000 800306c0 00000000  (……………
  00000000`091ff6c0  06e08328 00000000 00000000 00000000  (……………
  00000000`091ff930  06e08328 00000000 00000000 00000001  (……………
  00000000`091ff9b8  06e08328 00000000 00000000 00000000  (……………
  00000000`091ffa38  06e08328 00000000 00000000 00000000  (……………
  00000000`091ffc10  06e08328 00000000 03684080 00000000  (……..@h…..
  00000000`091ffc90  06e08328 00000000 00000000 00000000  (……………
  ThreadId:
  unsigned int 0x9b8
  ThreadId:
  unsigned int 0x9bc
  ThreadId:
  unsigned int 0x9c0
  ……………
  …………..
  步骤6:
  从以上输出,我们可以看到线程0x9b4与拥有的任务的指针相关,并且它是拥有闩锁的线程。让我们切换到线程(0x9b4),它正在执行拥有的任务,然后浏览这个堆栈来查看为什么这个线程长时间持有闩锁。
  步骤7:

  ~~[0x9b4]s      ==> Switching to the thread (Replace 0x9b4 with your thread>  ntdll!ZwWaitForSingleObject+0xa:
  00000000`77ef047a c3              ret
  步骤8:
  0:002> kC  ==>  Print the stack
  Call Site
  ntdll!ZwWaitForSingleObject
  kernel32!WaitForSingleObjectEx
  sqlservr!SOS_Scheduler::SwitchContext
  sqlservr!SOS_Scheduler::Suspend
  sqlservr!SOS_Event::Wait
  sqlservr!BPool::FlushCache
  sqlservr!checkpoint2
  sqlservr!alloca_probe
  sqlservr!ProcessCheckpointRequest
  sqlservr!CheckpointLoop
  sqlservr!ckptproc
  sqlservr!SOS_Task::Param::Execute
  sqlservr!SOS_Scheduler::RunTask
  sqlservr!SOS_Scheduler::ProcessTasks
  sqlservr!SchedulerManager::WorkerEntryPoint
  sqlservr!SystemThread::RunWorker
  sqlservr!SystemThreadDispatcher::ProcessWorker
  sqlservr!SchedulerManager::ThreadEntryPoint
  msvcr80!endthreadex
  msvcr80!endthreadex
  从以上堆栈,我们可以理解,拥有闩锁的线程正执行检查点并刷新缓存(脏页)到磁盘。如果刷新缓存到磁盘(检查点)花费很长时间,那么显然是有磁盘瓶颈。
  类似的,对于其他闩锁超时问题,首先识别闩锁的拥有者线程,读取拥有者线程的堆栈,来理解拥有者线程执行的任务,并排除由拥有者线程执行的任务引起的性能故障。
  如果你想查看等待的线程的堆栈,那么在错误日志里从闩锁超时警告信息获取任务(任务0x0000000006E096D8)代替拥有者任务(任务0x0000000006E08328),并使用在步骤5中提到的命令。
  我希望这篇博文将帮助你学习和排除闩锁超时故障。



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-624464-1-1.html 上篇帖子: MySQL:常用基本SQL语句小结 下篇帖子: mysql-community-server installaton
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表