揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL

概述
【揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL】X-Engine是阿里自研的数据库存储引擎 , 以插件的方式接入到MySQL生态 , 支持行锁 , 事务 , MVCC等OLTP场景的核心功能 。
X-Engine的核心优势是低成本 , 高性价比 , 尤其适用于历史库场景 , 目前阿里巴巴内部的核心交易历史库(原来是Hbase) , 钉钉消息历史库(原来是MySQL(InnoDB)) , 淘宝商家的图片空间等业务均通过X-Engine解决了成本问题 。
同时 , X-Engine也赋能阿里云数据库服务 , 作为云上RDS-MySQL的存储引擎 , 对外售卖 , 让更多的用户享受到新技术带来的红利 , 有关X-Engine的详细介绍 , 请移步2019年10月的数据库内核月报 。 本文主要介绍X-Engine引擎的一个核心功能 , OnlineDDL 。
OnlineDDL毫无疑问是MySQL生态的一个重要的功能 , 想当初MySQL5.6以前 , DBA执行DDL变更时 , 为了保证7*24小时服务 , 只能采用最老土的主备切换的方式来进行 。 数据库存储引擎区别于NoSQL引擎的一种重要指标就是是否支持SQL , 是否有schema(数据字典) 。 有了schema , 还需灵活地支持在线变更 , 这样才能从容应对业务快速变化的需求 。 MySQL生态中这么多存储引擎只有InnoDB完整地支持了OnlineDDL , X-Engine作为MySQL生态的新成员 , 虽然采用了完全不同于InnoDB的存储架构 , 但OnlineDDL给用户的体验是一样的 。
整体流程
X-Engine采用类LSM的分层架构 , 数据按照时序逻辑分成多层 , 每一层数据有序 , 新数据在较高的层次 , 最老的历史数据在最底层 。 对于X-Engine来说 , 每个主表和二级索引数据都是一棵分层的LSM-tree结构 , 内部称之为Subtable 。 每个Subtable分为4层 , Memtable , L0 , L1和L2 , 每一层都保持有序 , 数据按新旧顺序依次往更深的层次迁移 , 其中Memtable在内存中 , 其它几层按需可以在不同的存储介质上 。 OnlineDDL功能实现充分利用了X-Engine的数据组织特点 , 将新build数据分为两部分 , 基线数据和增量数据 。 基线数据是指变更开始时 , 通过拿snapshot能遍历得到的数据;增量数据是指 , 变更开始后 , 用户写入的新数据 。 拿Snapshot过程需要短时间禁写 , 因为我们强依赖这个一致性位点 , 确保基线+增量数据的完整性 。
OnlineDDL总共包括了4个阶段 , 包括Prepare阶段 , Inplace-build阶段 , Commit阶段和Post-ddl阶段 。 1.Prepare阶段 , 这个阶段主要是准备数据字典 , 构建底层存储数据的Subtable , 为后续的增量写入做准备 。
2.Inplace-build阶段 , 这个阶段OnlineDDL的核心阶段 , 一方面通过Snapshot获取基线 , 另一方面还需要实时维护增量数据 , 利用X-Engine的数据组织的append-only特性 , 将基线和增量合并 , 即完成了OnlineDDL新数据构建的过程 。 这个过程的详细逻辑会在下一个小节详细介绍 。
3.Commit阶段 , 这个阶段是OnlineDDL引擎层变更生效阶段 , 如果整个OnlineDDL过程中没有出现异常或错误 , 那么Commit阶段会生效新的数据字典 , 生效新的数据 。
4.Post-ddl阶段 , 这个阶段是OnlineDDL真正生效阶段 , 这个阶段完成后 , 才会返回给用户DDL成功 。 这个阶段的引入 , 主要是因为MySQL通过Server层+引擎层这样的一个二层结构实现扩展 , 而每一层都有自己的数据字典 , 在Commit阶段只能保证引擎层的数据和数据字典是完整的 , 为了保证DDL变更的原子性(Server层和引擎层数据字典保持一致) , 引入Post-ddl阶段做清理和善后工作 , 有关DDL原子性的讨论会在下面的章节详细介绍 。
核心逻辑
OnlineDDL的核心逻辑在于如何做到执行DDL变更时 , 不堵塞用户对该表的DML和SELECT操作 。 X-Engine实现OnlineDDL有两个关键点 , 第1 , 利用X-Engine的数据组织append-only特点 , 增量维护在Memtable , L0 , L1中 , 而基线数据维护在L2中;第2 , 维护增量时 , 采用双写 , 同时维护old-table和new-table中的数据 。 与InnoDB引擎类似 , 根据DDL是否涉及到数据记录格式变更 , 将DDL变更分为Inplace-rebuild和Inplace-norebuild两种类型 。 对于X-Engine来说 , 两者本质是一样的 , 区别在于维护的索引个数 。 Inplace-rebuild类型的DDL需要同时维护new-table中所有索引;而Inplace-norebuild类型的DDL只需要维护new-table的新增的索引 。
揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL
文章图片
整个Inplace-Build按照时间序有3个关键节点 , t0时刻是获取快照的时间点 , t1时刻是build基线完成的时间点 , t2时刻是唯一约束检查完成的时间点 。 那么两个阶段的主要逻辑如下:t0-->t1主要工作是在build新表的基线 , 通过将old-table的数据结合new-table的数据字典生成新的记录 , 最终写入新表对应的L2层;在build新表基线的过程中 , 产生的增量写入到新表的(Mem,L0,L1) 。 DDL过程中 , 需要对后台的Compaction任务做一定的控制 , 确保不执行合并到L2的Compaction任务 。 t1-->t2是唯一性校验阶段 , 确保新增的主键或者唯一索引的唯一性 , t2时刻将(Mem,L0,L1,L2)中的数据合并 , 最终得到new-table的全量数据 。 记录转换的过程如下:
揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL
文章图片
其中 , DDL事务表示DDL线程 , 它的任务是扫描基线 , 生成新表的基线数据;DML事务表示DDL过程中 , 并发的DML事务 , 它们的任务是 , 通过双写机制同时维护新表和老表的增量 。
对比InnoDB实现逻辑
虽然X-Engine与InnoDB的OnlineDDL都是采用基线+增量的方式实现 , 但具体逻辑是不同的 , 主要是因为InnoDB采用的的是原地更新操作并且通过row-log机制来维护增量 , 而X-Engine是一个append-only的存储引擎 , 天然地支持数据的多版本存储 , 可以实时维护增量数据 , 在基线建立完成后只需要将基线与增量数据合并 , 即使基线中的数据在增量中被修改 , 但增量中数据的版本比基线数据版本更新 , 从而在合并时会覆盖基线中老版本的数据 。 下图是InnoDB引擎OnlineDDL过程 。
揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL
文章图片
可以看到InnoDB引擎的OnlineDDL也包括3个关键时间点 , 与X-Engine引擎的区别在于 , t1-->t2是InnoDB追row-log过程 , 而对应X-Engine是唯一约束检查的过程 。 当然对于X-Engine来说 , t1-->t2不是必需的 , 因为DDL变更可能并不涉及唯一索引操作 。
Instant-DDL
与MySQL8.0(InnoDB)类似 , X-Engine同样也支持Instant-DDL 。 在所有支持的OnlineDDL中 , 若DDL操作只涉及修改表的属性信息 , 或只是做了加列操作 , 不需要修改记录格式 , 也不需要新增索引 , 那么这些OnlineDDL操作可以优化成Instant-DDL 。 这些DDL操作可以“极速”完成 , 用户基本无感知 。 由于Instant-DDL执行时 , 并没有真正涉及引擎数据的修改 , 为了后续查询结果和DDL操作的正确性 , 需要对于引擎的记录格式做一定的调整 , 加一些控制元信息 。 新增一个1字节来标示生成这个记录时 , 表是否执行过instant-ddl 。 同时 , 生成记录时 , 还需要记录有多少个列是已有的 , 以及有多少个null列等;在读取解析记录时 , 根据字典信息 , 就能知道有多少个列是需要根据instant列信息来补充 , 确保instant-DDL后 , 返回查询结果的正确性 。
DDL原子性保证
从OnlineDDL的整体流程中我们了解到 , OnlineDDL最后一个阶段是Post-ddl阶段 。 MySQL8.0以前 , Server层的元数据都是通过文件来存储 , 比如frm文件 , par文件以及trg文件等 。 一个DDL操作修改 , 涉及到文件修改 , 引擎数据修改以及引擎字典的修改 , 这些操作无法做成一个事务 , 必然导致整个DDL操作无法做到原子性 。 若DDL过程中出现异常 , 就可能会导致Server层和引擎层数据不一致 , 以及残余的垃圾没有清理等问题 。 MySQL8.0将Server层的所有字典信息统一存储在DD(DataDictionary)中 , 并且通过InnoDB引擎存储 , 那么DDL过程中 , 我们只要保证Server层数据字典的修改 , 以及引擎层数据字典的修改封装成一个事务即可 。
对于InnoDB引擎而言 , DD数据字典操作 , InnoDB引擎数据字典操作都是通过InnoDB引擎存储 , 通过InnoDB事务特征来保证原子性 。 对于X-Engine引擎而言 , DD数据字典操作 , X-Engine引擎数据字典操作分别采用InnoDB引擎和X-Engine引擎 , 除了依赖于InnoDB和X-Engine自身是事务引擎特征 , 还需要借助于内部的2PC协议来保证整个事务的原子性 。 如果MySQL开启了binlog , 那么就是binlog , X-Engine , InnoDB三者一起通过2PC协议保证事务的原子性 。 而Post-ddl阶段就是做善后和清理工作 , 如果最终整个事务提交 , Post-ddl阶段负责真正清理old-table数据;如果最终整个事务回滚 , 那么Post-ddl阶段负责清理临时产生的new-table数据 , 确保DDL变更前后 , 数据库的状态是一致的 。
揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL
文章图片
使用体验
X-Engine作为MySQL的一个新引擎 , 在语法使用层面完全与MySQL(InnoDB)相同 , 通过algorithm_option指定Online类型 , 通过lock_option指定DDL过程中 , 是否允许其它并发的DML和SELECT操作 。 通常情况下 , 这两个选项都不用特别指定 , 采用默认值即可 , MySQL内部会优先选择Instant类型和Inplace类型 , 对于不支持Online的DDL操作 , 选择Copy类型 。 在功能层面也与MySQL(InnoDB)相同 , 目前X-Engine暂时还不支持全文索引 , 虚拟列 , 外键等功能 , 因此与这些功能相关的DDL操作会不支持 , 其它DDL操作与MySQL(InnoDB)相同 。 常用的DDL操作分类如下:
揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL
文章图片
后续工作
X-Engine作为一个新的数据库存储引擎 , 通过集团业务场景的打磨 , 已经体现了它的价值 , 我们希望通过云上RDS场景 , 让更多用户享受到新技术带来的红利 。 当然 , 目前X-Engine还有一些不足 , 尤其是相对于传统成熟的MySQL(InnoDB)和Oracle , 所以X-Engine引擎在优化自身的稳定性和性能同时 , 会持续不断地丰富数据库功能 , 包括支持外键 , 全文索引 , 虚拟列等 。 除了公有云的RDS输出 , 基于X-Engine的一体化分布式数据库PolarDB-X也是一个重要方向 , 我们会以专有云形式输出 , 服务更多对分布式数据库有强需求的用户 。


    推荐阅读