学习SQL SERVER SPINLOCK诊断

来源:这里教程网 时间:2026-03-02 12:57:06 作者:

可能有朋友会有疑惑, S QLSERVER 是跑在 WINDOWS 平台上的,也会有 S PINLOCK 吗?事实上,只要是现代的多核心 C PU 环境中,多路服务器环境中,要实现高并发环境下的串行化访问, spinlock 就必定会存在的,只是 Windows和Linux在自旋锁的实现代码上不同而已。

正是因为 s pinlock 的存在, S QLSERVER 也会和 Oracle、MySQL、P G 等数据库一样因为 s pinlock 较为严重而导致 O LTP 系统的性能问题。因为 spinlock主要出现在高并发应用环境下,因此在O LTP 系统中出现严重问题的可能性远高于并发量不大的 O LAP 系统。

Linux上类似,s pinlock 是一个原子操作的轻量级锁,用于保护 S QLSERVER 的共享内存,使之能够通过串行化访问以确保其一致性。不仅 S QLSERVER 数据库需要使用 s pinlock, 当操作系统需要排他性访问共享内存结构时,也会使用 s pinlock

当某个线程试图获取自旋锁的线程无法获得访问权限时,它会循环尝试,定期检查以确定资源是否可用,而不是立即让出 C PU 。一段时间后,等待 s pinlock 的线程无法获取资源后失败,而不直接让出 C PU 给其他线程使用,这是因为切换 C PU 的上下文是比较高开销的操作,持续持有 C PU 并不断测试在短期内能够获取资源的情况下,更为高效。虽然这种设计在大多数场景下是没问题的,不过对于某些场景,可能会出现某些线程为了获得某个 s pinlock 的访问权而把持了 C PU 资源,从而导致 C PU 资源的使用效率降低,从而导致性能问题。

分析 S QLSERVER spinlock问题的最好接口是 sys.dm_os_spinlock_stats ,其中十分重要的几列在下面描述。

 

 

上面的查询可以显示出 S QLSERVER spinlock 的统计信息,如果我们在某个固定的时间区间内去采集这些数据,并通过 d elta 计算增量差值,就可以掌握最近一个采样周期内各种 s pinlock 的情况,从而从中推测数据库可能存在的并发方面的问题。

我们应该特别关注系统处于重负载下的特定时期内发生的冲突、 s pin backoff事件的数量。当一个线程试图访问一个受s pinlock 保护的资源时,就会发生冲突。当发生冲突时,冲突计数会增加,线程将开始 spin并定期检查资源是否可用。每次线程spin时,spins计数都会增加。

s pins_per_collision 是线程持有 spinlock时发生的spins的度量,它会告诉您在线程持有s pinlock 时发生了多少次自旋。每次冲突的 s pins 很小,但是冲突计数器很高,这就意味着 s pinlock 下会发生少量自旋,有许多线程在竞争它。大量的 spins意味着s pinlock 相关的代码中 s pin 所花费的时间相对较长 ,而spinlock是用来保护内存结构的,这些代码都十分简单,花费时间长往往出在代码正在遍历的哈希桶中有比较多的条目。这种情况我以前在Oracle数据库中也遇到过,当某些数据块的c r block 比较多的时候,再加上一些其他因素,会出现 H ASH CHAINS 上的 H OT CHAINS ,当 C ACHE BUFFERS CHAINS 出现 H OT CHAINS 的时候,扫描这条链的时间就会增加,从而引发严重的 c ache buffer chains 闩锁等待。而这时候通过调整 b uckets 数量或者较大的增加减少 D B CACHE 的大小,就可以打破 H OT CHAINS ,解决这个问题。在《 Oracle  DBA 优化日记》中国的海尔的那个优化案例就是采用这个方法去临时解决的。

从另外一个角度看,随着系统并发争用的增加, spinlock 的冲突计数计数也会增加,同时 s pins 的数量也会增加。

退避( b ackoff )是 s pinlock 的一种补偿性设计,如果一直 s pin 无法获取 l ock ,那么一个 C PU 就会被持续占用,导致 C PU 资源的浪费。为提高 CPU 的工作效率 s pinlock在可以访问持有的资源之前不会无限期地继续 spin 。为确保 s pinlock不会过度使用 CPU 资源, spinlock在达到s pin 的轮数限制后会退出 或停止 s pin并“休眠” 无论它们是否曾经获得目标资源的所有权。这样做是为了允许在 CPU 上调度其他线程 提高 C PU 的使用效率

引擎的默认行为是在执行 b ackoff之前先 spin 一个恒定的时间间隔。尝试获取 s pinlock需要保持高速缓存并发状态,这是一个 CPU 密集型操作。  SQL Server 中,某些 s pinlock(例如:LOCK_HASH)通过利用以指数方式增加的尝试获取 spinlock (达到一定限制) 从而避免对 CPU 性能 产生较大影响

S QLSERVER s pi nlock的实现与Oracle的L ATCH 是十分相似的,我在阅读上面这段描述的时候,甚至对 Oracle的L ATCH 的认知也更加清晰了一些。如果了解 Oracle  LATCH 冲突分析的 D BA ,想要处理 S QL SERVER s pinlock ,也是比较容易的。只需要了解 S QL SERVER 的每个 s pinlock 保护的数据结构属于哪方面的就可以了。

R DBMS 是一个经过精心设计的处理高并发复杂业务的软件系统,虽然经过精心设计,但是某些情况可能也在软件开发人员的意料之外。 Spinlock 就是充满了意外的部分, spinlock争用无法通过R DBMS 代码优化或者应用开发人员精心设计数据库结构而避免。 s pinlock R DBMS 内部的不同数据结构进行访问,所以 s pinlock 争用的表现方式与缓冲区锁存争用不同,后者可以通过优化数据库设计和 S QL 来解决,而 s pinlock 不能。

根据微软官方文档的描述, s pinlock 的严重争用 已在 C PU 线程数  >= 24 的系统上观察到,并且最常见于 >= 32 CPU内核的系统上。对于负载很大的高并发 OLTP 系统, 存在一定的 s pinlock 冲突,休眠 是正常的 观察 任何给定自旋锁类型的大量 s pins并不足以 说明系统存在并发方面的问题 不过以下几种症状的组合可能表明 s pinlock 存在较为严重的争用:

 

对于特定的自旋锁类型,观察到大量的自旋和回退;

系统  CPU 使用率过高或 出现  CPU 消耗量激增的情况。在繁重的 CPU 场景中,您会在 SOS_SCHEDULER_YEILD 上看到高信号等待(由 DMV sys.dm_os_wait_stats 报告)

数据库的并发量很高;

CPU 使用率和 s pins 的增长与数据库系统的 吞吐量不成比例地增加。

上述条件都成立,是系统出现 spinlock 性能问题的典型征兆,不过即使上述每个条件都成立,高  CPU 消耗的根本原因仍然可能在其他地方。事实上,在绝大多数情况下,CPU 的增加是由于自旋锁争用以外的原因。导致 CPU 消耗增加的一些更常见的原因包括:

由于基础数据的增长导致需要对内存驻留数据执行额外的逻辑读取,查询随着时间的推移变得更加昂贵;

S QL 执行计划的更改导致更大的资源开销。

 

针对 s pinlock 异常问题的发现,一个比较典型的场景是数据库负载的增加比例与  CPU 使用率 的增加比例存在显著的 差异。 OLTP 工作负载在(吞吐量/系统上的用户数)和 CPU 消耗之间都有 相对固定的 关系。观察到 较高的 spins以及 CPU 使用率 数据库负载之间 的差异可能表明 s pinlock争用 增加了  CPU 开销。这里 需要 注意的一点是, 当某些 S QL 执行计划变坏时,数据库逻辑读的增加,也会引发类似的现象。在分析 s pinlock 问题时,排除其他更常见的高  CPU 原因至关重要。

下面我们来看看如何诊断 s pinlock 的问题。诊断首先要看工具。

 

微软官方文档给出的工具十分简单,其中后两个是比较复杂的,需要专业的人员进行。而前两个是 D BA 十分常用的工具。如果要做现场分析,则可以通过性能监视器来观察系统资源情况,如果你已经采集了 O S C PU ,内存, I O 等数据,则十分有利于现场分析或者事后分析。

S pinlock状态的D MV 视图是十分重要的工具,我们可以根据这个信息发现哪些 s pinlock 出现了问题,从而进行根因定位。分析的过程也很清晰。

 

只不过后两个步骤对于一般的 D BA 来说过于专业,而且必须在具有符号调试器的 S QLSERVER 环境中才能进行。不过我们在大多数情况下不必要如此兴师动众。通过第二步发现的 s pinlock 种类,查出存在问题的 s pinlock 后,根据 s pinlock 的种类基本上就能够对出问题的地方进行猜测了。比如 b uff_free_list 是缓冲池空闲列表 s pinlock, s pinlock 出现严重问题一般来说和缓冲区申请有关,如果 S QL 出现了大量的逻辑读,而缓冲区申请过于频繁,缓冲内存不足等,就会导致该 s pinlock 争用变得严重。

似乎很简单,实际上不是的, s pinlock 分析是 S QLSERVER 性能分析的深水区,往往会遇到灰色地带,这些地带是你的知识积累锁不能达到的。比如微软官网的这个白皮书里提到的一个案例。

 

L OCK_HASH 这个 s pinlock 我们还是比较容易理解的,当很多并发进程访问某个锁 H ASH 的同一个桶的时候,很容易出现此等待,比如大量的并发会话都在争用某几个具体的行数据的时候,就会发生。当然这只是其中一种情况,更多的情况还需要微软官方的介绍或者我们积累更多的案例与运维知识。

而对于最严重的 S OS_CACHESTORE ,我们就一头雾水了。微软官方都无法说清楚这个 s pinlock 到底会出现在多少个场景中,因此必须通过符号调试器去获得扩展事件,从而获得 C ALL STACK 的信息,才能进一步分析。

 

恐怕没有 D BA 能够看出这个 C ALL STACK 代表什么含义,微软后台的服务专家可以根据源代码分析出这是因为权限不足导致的问题,把访问的用户权限提升后,这个 s pinlock 就消失了。

上面是我今天学习 S QLSERVER spinlock分析的白皮书的笔记,实际上以我以往的知识,对s pinlock 的理解并不困难,分析诊断的思路与其他数据库的类似问题并无太大区别。最主要的还是 S QLSERVER 的运维知识的积累还是不足,对于不同的 s pinlock 可能出现在哪些场景中的归纳还是太欠缺。而积累这方面的知识,仅仅依靠看书是无法获得的。必须汇聚和分析大量的案例才能实现。我们在 Oracle上已经有了2 0 多年的此类积累,而 S QLSERVER 上的积累还太薄弱。

我们准备节后再 D -SMART 社区版中增加对 S QLSERVER spinlock 的数据采集。有兴趣的朋友可以下载我们的 D -SMART 社区版,去采集一些数据。并通过 h ola 分享给我们。我们会用来积累运维经验,并将这些运维经验发布到社区版中,免费给大家使用。

相关推荐