多核处理器下数据库系统日志管理器优化技术探讨

摘要

传统数据库的设计假设磁盘为主要存储设备,其性能取决于基于I/O代价模型的优化。然而,当前数据库运行的平台已逐渐转移到由多核处理器、大内存和以闪存为代表的低延迟存储所构成的新型硬件平台上。在大多数情况下,工作数据集能够全部加载到内存或者闪存等高速存储器中。这样,数据库的性能瓶颈由传统的I/O转移到了CPU上。而传统数据库的加锁操作、闩锁竞争、日志管理以及缓冲区管理在设计时均未考虑到多核处理器的使用,因而成为了限制CPU利用率的明显瓶颈。改变传统数据库的优化重点以适应硬件的发展对应用而言是十分必要的。本文针对当前新的应用背景,主要围绕数据库中日志管理器、在多核平台下已有的优化技术进行详细介绍和归纳总结。

背景

摩尔定律揭示了集成电路上晶体管集成度的增长规律,摩尔定律首先表现为CPU主频的持续增长,然后又表现为CPU核心数量的持续增长。CPU的频率持续逐年增长,加上其指令执行的并行度也在增加,使得单线程的工作性能不断提高。早在2002年,CPU的频率就已经达到3GHZ。然而,高频率的CPU带来的是更多的能耗,现实应用中不一定会带来高的性价比,因而阻止CPU主频的进一步发展。这个问题使得近年来CPU的发展方向产生了转变。为了进一步优化CPU的性能,生产商倾向于在单个CPU上增加更多的处理核心。CPU开始转向使用并行多线程和片上多处理器技术,以替代单处理流的高度复杂CPU技术。生产商不再竞争速度而是转向提高并行度。按照最近多核的发展趋势,单个CPU上的核数基本每两年翻一倍。大部分研究人员预测,CPU的核数会继续增长一段时间。为了更好地利用新CPU的并行处理能力,工程师需要修改或重新设计软件系统。而基础软件,包括操作系统、web服务器和数据库等,则最先遇到复杂的多核架构带来的扩展性问题。

从一开始,数据库存储引擎的研究就是定位在高负载下的高性能和可靠性。一方面,数据的可靠性和一致性大都通过在共享数据结构上的封锁机制实现;另一方面,在现代多核架构下,性能的提升必须通过利用更高程度的并行性。如何解决这两者之间的矛盾是一个很大的挑战。现实中,关系数据库系统大部分是基于优化I/O和面向单处理器的陈旧设计思想,并不适合多核处理器的架构。

以事务处理为主的数据库系统多数依赖并发取得高吞吐率。并发是通过多线程或者多进程实现的。理想情况下,随着硬件上下文的增加他们应该具备良好的扩展性,特别是针对以读为主工作负载。实际上,为了维护一致性和持久性,数据库系统大量使用共享数据结构和同步原语。在高度并行的环境下,对共享数据进行频繁的原子更新不得不由线程串行地执行。因此,用于保护共享数据结构的同步原语会导致很大的同步开销。这些同步原语大量存在于共享内存缓冲区、锁表、索引与日志管理器中。此外,丰富的硬件上下文使得并行线程竞争硬件资源,例如,高速缓存的争用将降低缓存命中率从而增加内存访问延迟。另外,传统的封锁方式例如阻塞和忙等待策略在多核环境下效率低下。增加的并行度还会导致更大异构的工作负载,这给索引数据结构里的读写同步控制原语也造成更多的压力。总之,所有这些原因导致了运行在多核架构上的数据库系统的性能瓶颈。近年来,学者们提出许多不同的思想和方法,用于重新构建多核环境下的数据库系统。

本文主要对数据库的日志管理器的多核优化技术做整理归纳。

日志管理器优化技术

日志管理器是数据库系统的一个重要部件。几乎所有的数据库系统都使用集中式的预先写日志策略避免在系统崩溃的时候带来的数据的损坏和丢失已提交工作,以保证事务的持久性。由于其集中式的设计和对I/O的依赖使得它很容易成为性能瓶颈。较长的日志刷新时间、由日志带来的锁竞争、日志缓冲区上的竞争都会影响系统的扩展性。日志操作给系统带来的延迟主要是四类:1) 系统必须保证事务提交之前日志必须写到非易失性存储介质;因为磁盘的访问时间是毫秒级别的,日志刷盘通常为事务中执行时间最长的部分;此外,当很多小的I/O请求使得记录日志的设备例如SSD上达到饱和状态时,写日志的延迟为串行执行时间;2)在写日志的过程中,事务一直持有写锁,直到I/O结束;在很多工作负载中,这都会造成瓶颈,特别是锁竞争激烈的场景;3)日志刷盘除了I/O延迟以外还有其他开销;在等待I/O完成的过程中,事务不能继续执行,代理线程必须被挂起,直到I/O完成;与I/O延迟不同,上下文切换和调度决策会消耗CPU时间,不能重叠执行其它任务;多核硬件环境下同时运行的线程很多,这使得操作系统调度器会负载过重;4)除了锁的竞争和上下文切换的开销,许多线程同时想要执行日志插入操作;集中式的日志缓冲区有明显的临界区,其上的竞争显然也会影响系统的扩展性。

为了提高日志管理器的扩展性,必须设法提高日志的I/O效率,并且减少日志临界区的数量和缩短关键路径的长度。

成组提交技术通过将许多小的日志刷盘的请求组合到单个I/O操作减缓磁盘的压力,减少I/O等待时间。成组提交技术能够减少磁盘访问次数和增大写入的磁盘块,进而减少磁头转动获得更好的响应时间。但是成组提交技术不能消除不必要的上下文切换开销,因为过多的事务会阻塞来自日志管理器的挂起通知。异步提交技术结合了成组提交技术的优点将许多日志刷盘的请求组合到一起,并且允许事务结束而不用等待刷新日志操作完成。这个优化将日志刷盘操作从关键路径上完全移除,但是牺牲了事务持久性这个特性,也就是说,已提交工作有可能由于系统崩溃而丢失。

有研究人员针对多核平台优化了的写日志操作。他们重新审视了提前锁释放技术(Early Lock Release,ELR),并在四个不同延迟的非易失性设备上测试了ELR的有效性。ELR技术是指事务在commit记录到达磁盘之前可以释放锁,减少持有锁的时间。在ELR技术下,只有提交事务需要等待I/O结束。不存在其它事务需要等待提交事务的锁而与提交事务一起等待I/O结束。实验结果表明在越慢的设备上,ELR获得收益越好。即使是在像SSD上的快速磁盘设备,ELR也可以获得比较好的收益(短事务的执行时间比I/O要短得多)。另外,数据的偏斜程度也对ELR造成影响。如果数据偏斜度小,对锁的竞争不激烈,ELR减缓锁的竞争效果不大。如果数据的偏斜度大,锁的竞争很激烈,即使在没有等待日志刷盘的情况下, ELR效果也不好。按照80%的访问都是集中在20%的数据上的原则,对应数据的偏斜度为0.85左右,这时候锁的竞争程度刚好是ELR的优化点。针对多核平台上过多的调度而导致的系统瓶颈和上下文切换带来的开销,流水线方式写日志是一种新的记录日志的方法。流水线刷新日志类似异步提交方式不用在等待I/O的时候将线程挂起,从而不会有上下文切换的开销。但是不同于异步提交方式,流水线刷新不将结果返回给客户端,而是转而执行其它事务。守护进程在完成日志刷盘动作以后,通知代理线程/进程返回继续执行后续事务。实验表明,流水线刷新和ELR组合的效果能够达到异步提交的性能,而且在系统崩溃的时候具有可恢复性。

为减缓多核情况下日志缓冲区上的竞争,技术人员设计了三种新的日志缓冲区管理方法。传统的写日志缓冲区需要以下三个步骤:1)首先获取写日志缓冲区上的排他锁。如果当时正好有其它的服务子线程在写日志缓冲区,则此子线程必须等待直到获得写日志缓冲区上的写锁; 2)线程将日志记录复制到相应的日志缓冲区 3)释放缓冲区上的锁。由于它的简单性,这种方法具有吸引力。这个方法的缺点是:即使是缓冲区从来不会重叠的情况下,填充日志缓冲区的操作也是串行化执行。图1(B)展示了由于单一的长日志记录会给后续的线程造成比较大的延迟。日志记录由一个头部加上任意长度的值组成。日志记录结构体空间的申请是可复合的,也就是两个连续的日志记录的缓冲区申请也可以由一个头部加上任意长度的属性值组成。可以利用这种空间的可复合性将线程对日志缓冲区的填充按组进行。每一个组都有一个组织者。

一个组只有组织者才要竞争缓冲区上的锁,且一个组只有最后离开的线程需要等待锁的释放。组织者在等待互斥变量的时候,后面到来的请求可以“回退”到一个数组将他们的请求组合到一起。如图1(C)所示,组内日志缓冲区的填充可以并行执行,但组之间仍然串行执行。由于日志缓冲的填充不具有串行特征,只要满足以LSN的顺序将日志写回即可。因此,线程申请的锁可以在获得缓冲区以后马上释放。因此将缓冲区填充与锁的持有解耦合。缓冲区的填充可以按流水线的方式进行:下一个缓冲区的填充可以立刻开始,只要线程获得日志缓冲区空间。如图1(D)所示,将锁的持有与缓冲区的填充解耦合可以消除长日志记录对缓冲区填充的影响。前面讨论的两种方法具有互补性,因此将两种方法组合 一起日志缓冲区上的竞争限制在某一个常数下,同时也消除日志记录的大小对刷新日志的影响。

<small>图1. 日志记录写入日志缓冲区算法图示<big>

总结

优化日志管理器可以显著提升数据库系统的性能。有相应的实验表明,经过以上优化,数据库系统的性能可以提升10%-30%左右。

值得一提的是随着像固态硬盘的新闪存产品开始进入主流的计算机市场,闪存技术被认为是磁盘技术的替代。最近也有相关研究利用闪存技术提高数据库系统性能。固态硬盘可以显著提高事务写日志的性能。他们的做法主要关注以下四点1)突破带宽限制,利用多闪存设备,并行化I/O提高刷新日志操作性能;2)解决由于设备擦除操作而引起的不同的写延迟;3)高效的系统恢复处理;4)将闪存设备与磁盘设备结合起来获得更好的日志操作和系统恢复性能。

文章来自:腾讯架构师

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java思维导图

分布式之消息队列复习精讲

庆幸的是两位朋友都很有上进心,于是博主写这篇文章,帮助他们复习一下关于消息队列中间件这块的要点

913
来自专栏java思维导图

让分布式消息队列不再难懂

小A,工作于传统软件行业(某社保局的软件外包公司),每天工作内容就是和产品聊聊需求,改改业务逻辑。再不然就是和运营聊聊天,写几个SQL,生成下报表。又或者接到客...

672
来自专栏Linyb极客之路

分布式之消息队列复习精讲

庆幸的是两位朋友都很有上进心,于是博主写这篇文章,帮助他们复习一下关于消息队列中间件这块的要点

922
来自专栏xingoo, 一个梦想做发明家的程序员

[大数据之Yarn]——资源调度浅学

在hadoop生态越来越完善的背景下,集群多用户租用的场景变得越来越普遍,多用户任务下的资源调度就显得十分关键了。比如,一个公司拥有一个几十个节点的hadoo...

1775
来自专栏数据之美

linux 系统监控、诊断工具之 IO wait

1、问题: 最近在做日志的实时同步,上线之前是做过单份线上日志压力测试的,消息队列和客户端、本机都没问题,但是没想到上了第二份日志之后,问题来了: 集群中的某台...

28510
来自专栏友弟技术工作室

Nginx架构--nginx系列之二Nginx的架构详解

Nginx的架构详解 今天,回家,这篇文章在机场候机,原文来自这里 NGINX 在网络应用中表现超群,在于其独特的设计。许多网络或应用服务器大都是基于线程或者进...

4279
来自专栏皮振伟的专栏

[qemu][acpi]从虚拟化看ACPI

前言: windows7中电源操作的“睡眠”和“休眠”的差别是什么? virsh命令查看到的虚拟机的pmsuspend状态是什么状态? ACPI又是什么鬼? 百...

6065
来自专栏达观数据

干货分享丨达观数据提升 Web服务端性能的技术经验

随着互联网的不断发展,日常生活中越来越多的需求通过网络来实现,从衣食住行到金融教育,从口袋到身份,人们无时无刻不依赖着网络,而且越来越多的人通过网络来完成自己的...

3365
来自专栏京东技术

详解Android UI线程卡顿收集

作者:段云飞 京东前台产品研发部-资深Android工程师,主要负责手机京东Android端图片框架,性能优化,性能数据收集,对Android Framewor...

4256
来自专栏Java技术

分布式之消息队列复习精讲!

小A,工作于传统软件行业(某社保局的软件外包公司),每天工作内容就是和产品聊聊需求,改改业务逻辑。再不然就是和运营聊聊天,写几个SQL,生成下报表。又或者接到客...

532

扫码关注云+社区