背景
数据库的Crash Recovery时长关系到数据库的可用性SLA、故障止损时间、升级效率等多个方面。本文描述了针对X-Engine数据库存储引擎的一种Crash Recovery优化手段,在典型场景下可以显著缩短数据库实例的故障恢复时间,提升用户使用感受。
当前面临的问题
X-Engine是阿里自研的基于LSM-tree架构的数据库存储引擎,对X-Engine的数据更新是将变更数据并行插入无锁内存表(MemTable),同时为了防止MemTable过大影响数据查找和数据恢复的效率,系统会不定期将MemTable转换成不可修改的内存表(ImmutableMemTables),并将ImmutableMemTables整体Flush落盘;对无锁内存表的持久化通过WAL机制保证。关于LSM-tree结构和X-Engine的数据分层存储机制,可以阅读文章:X-Engine 一条数据的漫游
X-Engine存储引擎的故障恢复需要读取所有WAL文件并重新生成所有ImmutableMemTables 和MemTables。当前X-Engine的Crash Recovery流程分为以下两步:
-
X-Engine存储引擎元信息回放,恢复系统元信息数据到shutdown前状态
-
wal文件回放,逐条读取record,进行crc校验后,恢复所有的ImmutableMemTables和MemTables到shutdown前状态
当前MySQL(XEngine)线上实例有宕机重启缓慢的现象,通过对相关现场分析和线下试验,WAL的回放占据了X-Engine宕机重启的绝大部分时间。结合现有实现分析,当前X-Engine的WAL文件回放主要有以下几个问题:
-
使用同步IO接口读WAL文件,等待磁盘IO耗时较多
-
单线程恢复内存表,无法发挥多核优势
-
恢复过程中存在过多ImmutableMemTables Flush落盘操作。虽然这样可以释放被Flush的内存表,节省内存开销,但磁盘操作进一步加大了X-Engine存储引擎宕机重启的耗时
WAL Recovery性能优化方案
针对上述问题,我们制定了XEngine WAL Recovery的优化方案。总体思路如下:
-
由于X-Engine存储引擎的所有数据都是存储在线程安全的无锁内存表中的,该内存表支持多线程并发更新,因此可以将单线程恢复MemTable优化成多线程并行恢复,充分利用多核优势,让整个宕机恢复过程并行起来
-
将同步磁盘IO改成异步IO,缩减读文件等待耗
-
WAL恢复过程中尽可能减少ImmutableMemTables的Flush落盘操作,只有在超过内存配置阈值时,为了防止数据库实例OOM,才会触发Flush逻辑。实际上如果数据库重启前后没有内存相关的配置变更,那么宕机重启过程应该几乎没有Flush操作。
总体设计
Parallel WAL Recovery的总体设计如下:
Recovery主线程可作为回放任务的生产者,通过aio接口读WAL文件,构造WAL回放任务存入回放线程池的任务队列中;
回放任务的主要成员是从WAL文件中解析出的一条完整WAL Record,里面包含了一批连续的数据更新记录,每个更新记录都有各自对应的全局序、操作类型和操作值等。
回放线程池中的回放线程从任务队列中获取回放任务并发更新内存表。
上述回放过程,我们通过一个回放线程池将读WAL文件操作和回放内存表操作完全并行起来,可以有效提升CPU利用率,降低回放时长。但是在实际设计过程中,还需要考虑以下几个问题:
-
宕机恢复过程中LSM-Tree的SwitchMemtable过程
-
2PC事务的正确回放
-
防御OOM
以下三节将具体阐述X-Engine的Paralle Recovery机制