详解新硬件环境下日志模块的设计与演进
基于这一点我们可以猜测很多数据库系统,特别是开源系统,在临界区上的设计上是比较随意的,面临一些扩展性的问题。从当前的研究实践看来,MySQL、PostgreSQL等性能瓶颈主要还是聚焦在日志模块。因此在当前硬件环境下,重构日志模块是具有很重要的意义的。 三、面向新型硬件的日志子系统设计 这部分我们来看一下面向新型硬件的日志子系统设计。主要分成三个部分,一部分是针对大并发场景优化日志子系统,通常做法是缩短临界区。第二种面向NVM优化日志子系统,但还是在现有的框架下去优化这个系统。第三部分是比较激进的,或许也是趋势,从头开始构建一个日志子系统,颠覆以前的认知。 1、大并发场景优化之一:预留空间 第一种优化方法叫做预留空间。下面是事务日志提交步骤:(1)事务只需在临界区内简单计算日志记录所占长度,然后即可释放锁。事务之间可以并行拷贝日志。(2)追踪日志记录填充情况。事务提交时刻需要确保之前的事务已经完成日志拷贝。 针对多核处理器,大并发场景下的优化方法主要是缩短临界区。如图a所示,之前通常的做法是在临界区内拷贝日志。这将日志记录大小的引入事务执行的关键路径,其实只需要简单地预留空间以后即可释放锁。预留空间就是计算事务日志应该在缓冲区中的占据的位置。事务之间的日志拷贝可以同时进行,如图(b)所示。 但是这个做法有个有个挑战,就是怎么样去追踪日志空洞,因为事务拷贝日志是并发执行的,有些事务执行的比较快,有些事务执行的比较慢。如果先执行的事务还没有拷贝完日志,而后来执行的事务却先于之前的事务完成日志拷贝,那么会导致在日志缓冲区上存在日志空洞,系统怎样去追踪空洞,这是一个很重要的研究课题。 其中这一类做法(预留空间)的典型代表是ELEDA算法(ELEDA,Express Logging Ensuring Durable Atomicity),它把系统分成三类线程:工作线程、日志状态追踪线程以及日志刷盘线程。这样子分工主要是为了达到无锁化设计的目的(回想一下InnoDB的log_sys->mutex以及log_sys->write_mutex的作用)。工作线程在提交时刻利用原子操作申请LSN,预留空间,然后将日志记录拷贝到缓冲区。拷贝完成以后即可继续执行其它操作。如果是提交事务那么需要提供回调函数。追踪线程记录最大连续的日志缓冲区偏移SBL(Sequentially Buffered Log)。刷盘线程将小于等于SBL的日志写入持久存储,更新SDL(Storage Durable Log)为SBL。这时候就可以通知commit LSN小于SDL的事务提交成功。 怎样维护这些信息还是很有挑战的,特别是追踪日志空洞。在ELEDA中,每个工作线程维护一个共享LSN链表如图所示,日志追踪线程根据这些链表建立最小堆,堆的顶部就是SBL。同时,工作线程在日志拷贝完成以后会在跳表(Hopping Index)相应的区间进行累加。当拷贝的字节数等于设定的区间值,那么表明该区间的日志已经全部拷贝完成,可以执行刷盘动作(这种方式对比利用最小堆推进SBL可以起到加速作用)。 论文作者修改了MongoDB的存储引擎wiredTiger,在36核,488G内存上的服务器上测试。在YCSB的测试标准上,修改后的wiredTiger扩展性可以达到32核,对比没有修改的版本性能提升71X,可以充分利用底层硬件的处理能力。在TPCC的测试基准上,也有类似的表现。 2、大并发场景优化之二:PostgreSQL的做法 我们来看一下PostgreSQL的另一种做法。在9.4版本,PostgreSQL的开发者意识到了日志提交的问题,重构了日志提交算法。在之前的版本中,日志提交临界区代码段有300行左右,优化之后,该临界区代码段缩短为只有5行。PostgreSQL的设计者们利用8把锁追踪日志空洞,规定事务将日志写入共享日志缓冲区时刻需要申请这种类型的锁,在日志拷贝完成后即可将锁释放。事务提交时刻只需检查这8把锁的状态即可。 这个设计有一个技巧性,就是需要优先考虑事务提交请求,也即对于锁的排队需要考虑优先级。通过类TPC-B测试显示,这个实现在大并发场景下性能能够提升30%以上。 3、NVM上的日志管理器 另外一个就是针对非易失性内存而设计的日志子系统。这种方法更为激进,大概的思路是现在不再是维护一个集中式日志管理器了,而是把负载分散到N个不同的日志管理器上,去提高写日志的并发性,但是还是要去解决日志空洞问题与确定日志回放顺序问题。在提交技术上,多数采用passive group commit技术。 (编辑:温州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |