为什么我们需要一个旋转锁

使用锁来同步多线程之间的数据结构的访问,它是把锁在每个共享数据结构意义。锁存器与此密切相关的:当你不能锁(因为别人已经有了一个不兼容的锁),查询将等待进入挂起(悬浮)状态。查询等在杭的状态直到锁可以得到,然后是进入可运行状态,查询执行只要没有可用的CPU,查询总是在执行(运行)状态,一旦CPU是免费的,查询进入运行(运行)状态,最后得到锁存器的成功,用它来保护共享数据结构的访问。下图显示了STA对于基于协调线程调度实现TE机。





因为有太多相关的锁存器,它是用锁保护的数据结构等无意义的忙碌。SQL Server实现自旋锁(自旋锁),自旋锁是锁存器,和一个轻量级的同步对象的存储引擎使用的是用于同步线程访问共享数据的锁存器和结构。锁存器的主要区别是,你正在等待自旋锁-不要让CPU。在自旋锁等待的总是发生在CPU上运行(运行)状态,转动你的闭环直到你获得自旋锁。这就是所谓的忙碌的一个旋转锁等。最大的优点是上下文切换是不参与查询时等待自旋锁上。另一方面,我们忙于等待CPU周期的浪费,而其他查询可能更有效地使用它们。

为了避免太多的CPU周期损耗,SQL Server 2008 R2和后续版本的补偿机制实施所谓的指数(指数退避机制),在线程停止转动CPU一些冬眠后。在线程进入休眠,增加试图获得自旋锁超时。这种行为会降低CPU性能的影响。

(附加说明:自旋锁的中国可以被称为自旋锁。它是一个轻量级的,用户喜欢的同步对象,类似于临界区,但比前者。更小的尺寸,它主要是用来保护某些特定的内存对象,多线程并发访问的Spinlock是唯一的,只有一个线程可以在一时间拥有的。

对自旋锁的设计目标是非常快速和有效的。如何自旋锁在里面工作吗它首先试图获得一个对象的锁,如果目标被其他线程占用,它在某一段时间内(自旋),如果你没有得到锁,睡眠将是一个小的会议,然后继续旋转。

自旋锁和故障排除

对于自旋锁故障主要DMV是行sys.dm_os_spinlock_stats.each返回该DMV是SQL Server的一个自旋锁。SQL Server 2014实现了262种不同的自旋锁。让我们在详细阐述的DMV列看起来。

名称:自旋锁的名称

碰撞:试图访问受保护的数据结构时被自旋锁阻塞的线程数。

自旋:试图在循环中获得自旋锁的自旋锁线程的个数。

spins_per_collision:旋转和碰撞之间的比率

sleep_time:由于该退避线程休眠时间

补偿:线程的退避时间,其他线程继续CPU

在这个DMV最重要的列是补偿。对于一个特定类型的自旋锁,本栏告诉你退避的频率。频率高的退避会屈服于自旋锁的竞争(自旋锁争)的CPU消耗引起的SQL Server。我见过的32个核心SQL Server服务器,和CPU运行在100%不做任何事情-典型的自旋锁竞争的症状。

为了解决旋转锁的问题,你可以使用扩展事件提供sqlos.spinlock_backoff。当退避(退避)时,这个扩展的事件触发。如果你抓住这一事件,你必须确保你使用的很好的选择谓词,因为SQL Server中的退避会经常发生的。一个好的谓词可以是一个特定类型的自旋锁,你通过刚才提到的DMV看到。下面的代码显示了如何创建这样一个扩展事件会话。






检索的lock_hash自旋锁的类型值。
价值是由未来的XEvent会话使用
SELECT * FROM sys.dm_xe_map_values
name = 'spinlock_types
和map_value = 'lock_hash


spinlock_backoff事件追踪
创建事件会话spinlockcontention服务器
添加事件sqlos.spinlock_backoff
(
行动
(
package0.callstack
)
哪里
(
{从前一个查询中的类型} = 129 -
)
)
添加目标package0.histogram
(
集源= 'package0调用堆栈,source_type = 1。
)




正如你所看到的代码,我用柱状图(直方图)调用堆栈上的目标(调用堆栈)来bucktize。所以对于一个特定的自旋锁,你可以去最大退避(补偿)在SQL Server生成的代码路径。你甚至可以使用3656追溯标签识别电话栈(跟踪标志)。在这里你可以看到从扩大会议的输出:

sqldk.dll!XeSosPkg::::spinlock_backoff发布+ 0x138

sqldk.dll!SpinlockBase::睡眠+ 0xc5

sqlmin.dll!Spinlock::spintoacquirewithexponentialbackoff + 0x169

sqlmin.dll!lck_lockinternal + 0x841

sqlmin.dll!XactWorkspaceImp::getshareddblockfromlockmanager + 0x18d

sqlmin.dll!XactWorkspaceImp::getdblocklocal + 0x15b

sqlmin.dll!XactWorkspaceImp::getdblock + 0x5a

sqlmin.dll!lockdb + 0x4a sqlmin.dll!DBMgr::opendb + 0x1ec

sqlmin.dll!sqlusedb + 0xeb

sqllang.dll!usedb + 0xb3

sqllang.dll!loginusedbhelper::usebymddatabaseid + 0x93

sqllang.dll!loginusedbhelper::fdeterminesessiondb + 0x3e1

sqllang.dll!FRedoLoginImpl + 0xa1b

sqllang.dll!FRedoLogin + 0x1c1

sqllang.dll!process_request + 0x3ec

sqllang.dll!process_commands + 0x4a3

sqldk.dll!sos_task::::执行+ 0x21e参数

sqldk.dll!sos_scheduler::runtask + 0xa8

sqldk.dll!sos_scheduler::processtasks + 0x279

sqldk.dll!schedulermanager::workerentrypoint + 0x24c

sqldk.dll!系统::runworker + 0x8f

sqldk.dll!SystemThreadDispatcher::processworker + 0x3ab

sqldk.dll!schedulermanager::threadentrypoint + 0x226

使用调用堆栈,不难发现,自旋锁竞争发生。在指定的堆栈在俑的竞争发生在lock_hash自旋锁的类型,它是保护哈希表的锁管理器。每次锁管理器被锁定或解锁,自旋锁必须在获得相应的哈希桶。你可以看到,在调用堆栈中,自旋锁时获得的getshareddblockfromlockmanager功能是由xactworkspacelmp类调用。这意味着,当数据库竞争,共享数据库锁是试图得到。最后,在lock_hash自旋锁具有非常高的补偿,这种屈服于自旋锁竞争。

以上是本文的全部内容,希望能对大家有所帮助。