高并发场景下优化spinlock

数据库并发依靠闩锁(Latch、Spinlock、Rwlock、LWLock等最终的机制原理都十分相近,我们就取Oracle的Latch来做此类锁的统称)和队列锁机制来实现必要的串行化。串行化是为了一致性访问某个资源所必须承受的并发环境下的代价。队列锁最为典型的就是行锁表锁等应用级的锁,不在我们今天讨论的范围内,今天我们重点讨论的是闩锁。闩锁的目的是为了一致性访问数据库内存中的共享内存结构,避免内存结构因为不一致的读写而导致混乱。不管是核心态的数据还是用户态的共享内存,都需要通过串行化控制来保证其一致性的读写。为了确保这种一致性,这些串行化的控制都需要至少一次全局串行化来达到最终的目的。这个全局串行化,一般来说就是使用的是和CPU直接相关的原子操作,自旋锁SPINLOCK。

前阵子我发过一个关于闩锁与SPINLOCK的帖子,其中简单的提到了CPU的PAUSE指令对SPINLOCK的影响。当自旋锁无法获得SPINLOCK的时候,已经占用了CPU的线程没必要放弃CPU进入休眠,等待再次尝试获得SPINLOCK,因此会在该CPU上自旋。不过对于争用比较严重的系统,自旋多次后,有可能仍然无法获得SPINLOCK。因为自旋时该线程需要持有CPU的串行锁,因此,对于具有多个线程(比如至强芯片是双线程的)的CPU来说,这种长时间自旋而无法获得SPINLOCK的情况会导致另外一个CPU线程也被锁死,无法执行。为了解决这个问题,LINUX针对SPINLOCK的算法做了一些优化。

上面是通过perf工具跟踪的结果,我们可以看到_raw_qspin_lock首先通过lock cmpxchg来获得spinlock,如果当前SPINLOCK无人持有,则直接获得SPINLOCK并返回,否则就调用queued_spin_lock_slowpath来继续自旋。今天篇幅有限,我们不讨论详细的细节。直接看queued_spin_lock_slowpath的汇编代码。

今天我们不讨论LINUX qspinlock的实现机理,这方面的文章通过百度或者谷歌都可以搜索到不少,写的都不错,建议有兴趣的可以去查查。当然看C代码比看汇报要舒服的多。在这段代码中,就可以看到多出存在PAUSE指令的地方。通过PAUSE,该线程可以暂停几个时钟周期,让该CPU中的其他线程可以获得CPU的公共资源来执行其他任务。类似Pause命令在所有的多线程的CPU上都有,这个命令既让核心态的线程继续持有这个CPU(一个线程可以持续持有10毫秒的CPU而不需要退出,对于现在GHZ级别的CPU来说,10毫秒包含的时钟周期就很多了)而不用返回用户态,又可以让出CPU的核心执行硬件让其他CPU线程执行其他任务。

上回我也说过,美团团队的一篇论文中探讨了不同的X86 CPU因为PAUSE的时钟周期的不同对SPINLOCK性能会产生影响。发现这个问题的原因是他们的MYSQL数据库在更换了新的SKYLAKE服务器之后,QPS反而下降了15%。

正好今天我也在分析一个MYSQL数据库的问题,通过perf发现ut_delay的比例比较高,这一点与美团遇到的问题十分相近。

我们来看一下ut_delay的汇编代码:

ut_delay是Mysql中轻量级锁、读写锁做自旋时,用于产生一个pause暂时让出CPU,避免SPINLOCK引擎严重的CPU性能问题的一个公共函数。在MYSQL的闩锁类的锁自旋时会通过这个函数来暂时让出CPU。如果spinlock相关参数设置的不合理,那么就会出现ut_delay休眠的时间过长引起性能问题的情况出现,因为不同的CPU执行pause的时候,暂停的CPU周期数量并不相同,有的是3、40个周期,有的是100个周期,而E8一代是140个周期。搞清楚这个问题,那么我们就可以去检查一下innodb的spin相关参数了。

可以看出,这两个参数都是用的缺省值,一般情况下,也不要轻易去调整这个参数,因为调整的不合适,可能会导致更为严重的性能问题。我查了一下,网上也有一些关于优化ut_delay问题的文章,一般都是建议把delay调整为40,loops调整为100。实际上这是不够科学的,因为针对不同的CPU,调整是有讲究的。因为E8 V1 pause命令暂停的时钟周期设置为140,远远大于以前的CPU,因此减少相关自旋的轮数可能可以提高整体的QPS。根据美团论文的描述,针对第一代SKYLAKE CPU,delay调整为3,Loops调整为20性能最佳。于是我直接尝试了一下这个调整:

从下面的图可以看出,调整后,ut_delay减少了20%左右,从系统上看,QPS也提升了13%左右。看样子这个参数调整策略是有效的。

接下来我们按照网上的建议,把delay调整为40,Loops调整为100,再来看看perf的信息。Ut_delay的占比更为严重了。

上面的例子证明了美团的论文关于spinlock与不同CPU 的pause命令持续的时钟周期的分析是完全正确的。我们进一步减少相关的设置,看看效果如何。这回我们把delay设置为2毫秒,loops设置为10。

可以看出,ut_delay占的比例明显降低了,不过native_queued_spin_lock_slowpath的等待比例明显增加了。从QPS上来看,比3/20的组合还是要略微第一点。

从这方面来看,spinlock的优化不是那么简单的事情,与服务器的配置,CPU型号以及应用的类型都有关系。因此调整这两个参数一定要慎重。

通过这个例子我又突然想起来前阵子遇到的一个达梦的性能问题。当时发现存在大量的pthread_spin_lock。SYS CPU的比例偏高,QPS怎么也上不去。

于是检查了一下数据库参数,发现SPIN_TIME设置为500。于是考虑是不是把SPIN_TIME加大能够提升QPS。

于是把SPIN_TIME加大为20000,发现调整参数后,数据库的并发处理能力提升超过了10%。再来看perf的情况。

是不是看上去舒服了很多。这说明在这种高并发的场景下,调整达梦数据库的spin_time参数,也是有效的。现在我们已经看到了达梦的BUFFER CACHE中的LRU的争用了,要进一步优化并发性能,就可以通过数据库缓冲区的优化来实现了。

闩锁优化涉及十分底层的spinlock的问题,因此在调整spinlock相关的数据库配置的时候一定要十分谨慎。不同的服务器、不同的数据库版本、不同的应用场景可能会有不同的最佳调整。而且spinlock的调整就像我们摄影时的手动对焦一样,只有对准焦点才有最佳的效果,调的不到位或者过了,都不能准确对焦。

实际上,通过spinlock相关的优化来调整并发能力并不是高并发数据库优化中的唯一渠道,因为这种优化对专业性的要求比较高。数据库开发商可以通过优化相关代码对spinlock做自动化的优化,可以避免很多这方面的问题。我们可以看看Oracle数据库在高并发下的spinlock优化就理解为什么Oracle会成为最为流行的数据库系统了。

上面是Oracle 11.2.0.4的数据库实例中,在TPMC为接近70万的环境中的情况,可以看出,占用CPU比较多的是kcbgtcr这个执行一致性读的操作,而spinlock和其他同步锁的占比很低。

除了优化SPINLOCK外,实际上我们还可以从另外一个渠道去优化SPINLOCK,那就是优化我们的应用,有可能优化应用需要花的成本更高,但是优化应用可以减少这种必须十分专业人员才能做的好的调整,从而可以提高优化的成功率。

  • 26
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中的spinlock是一种自旋锁机制,用于保护对共享资源的访问,以防止同时访问导致的数据竞争问题。spinlock使用了一种称为自旋的技术,即当一个线程需要获取锁时,它会一直等待,直到锁被释放。这种等待是循环的,即线程会不断地检查锁的状态,直到锁被释放为止。 spinlock相比于传统的互斥量(mutex)和信号量(semaphore)等锁机制,具有更高的性能和灵活性。spinlock不需要使用内核调度器,因此不会产生额外的上下文切换开销。此外,spinlock可以用于任何需要保护的临界区代码,而不仅仅是用于进程之间的同步。 使用spinlock时,需要将其初始化为0,以便其他线程可以安全地访问共享资源。当一个线程需要获取锁时,它可以使用spin_lock函数来锁定spinlock。如果锁已经被其他线程占用,该线程将进入自旋状态,不断检查锁的状态。当该线程获取到锁时,它可以将共享资源置于临界区并执行相关操作。在操作完成后,该线程可以使用spin_unlock函数释放锁。 spinlock机制适用于一些简单的同步场景,例如在并发访问共享资源时保护临界区代码。然而,对于一些复杂的同步需求,可能需要使用更高级的同步机制,如读写锁(rwlock)或条件变量(condition variable)。 总之,spinlock是一种轻量级的自旋锁机制,适用于简单的同步场景,具有较高的性能和灵活性。它适用于任何需要保护的临界区代码,而不仅仅是用于进程之间的同步。在使用spinlock时,需要注意避免死锁和过度自旋等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值