4. 多版本DD:DDL & DML的MVCC能力
4.1 功能概述
不管是Non-Block DDL还是Preemptive DDL,都是在有互斥锁的场景下,尽可能最优地满足用户的DDL变更需求。然而,用户在部分场景下依然要感知MDL锁的存在,例如在极限场景下,用户依然需要手动触发Preemptive DDL,来解决DDL饥饿的问题。我们一直在探索,是否可以实现DDL与DML更细粒度的并发控制,类似于InnoDB MVCC能力。然而,如前文所述,DDL是个复杂操作,其执行过程涉及文件操作/表数据变更/元信息变更/表缓存处理等一系列流程。因此,考虑到MySQL代码的强耦合性,我们对这一目标做了切分,在控制代码切口和稳定性风险的情况下,逐步支持这一能力。在第一阶段,我们优先支持线上高频DDL与DML的MVCC能力,即按照statement维度,满足Instant Add Column与DML的MVCC能力(用户文档待新版本802上线)。为了兼容MySQL的默认表现,我们不仅支持DDL和未提交事务的并发,而且支持DD的readview,使得跨越了DDL的DML事务可以选择以RC或者RR的隔离级别读取表结构信息,从而让用户自行决定使用新或者旧的表定义。
4.2 测试效果
具体的效果如下:
步骤一:开启会话A,创建一个新的表t1并插入一些数据;随后开启一个新事务,在事务中进行数据的插入和更新操作,但事务不提交:
步骤二(DDL不会被未提交的事务所堵塞):开启一个新的会话B,查询performance_schema,此时t1的MDL正被会话A中未提交的事务持有。进行DDL操作(add column),该操作可以立即完成,而不会被未提交的事务阻塞。
步骤三(跨DDL的事务可以选择访问表时使用的隔离级别):回到第一个会话A,将表访问的隔离级别参数table_def_isolation设置REPEATABLE-READ,因为DDL的执行在该事务之后,因此新增的列c不可见,该事务将始终看到与事务开始时一致的表定义。
将table_def_isolation设置为READ-COMMITTED,因为DDL已经提交,列c将对该事务可见
提交事务后,DD的readview随之释放,随后将只能看到最新的表结构。
5. 全链路优化的分布式MDL锁(多节点数据同步问题)
目前的云原生数据库,不论是PolarDB,或者是Aurora等其它厂商的数据库,都以“存算分离”+“共享存储”的形态提供一写多读的能力。对这类架构感兴趣的读者,可以阅读我们之前的相关月报(PolarDB 物理复制解读,PolarDB 物理复制热点页优化)。对这类针对存算分离场景下IO优化感兴趣的读者,可以阅读我们去年发表在VLDB上的相关论文(CloudJump: Optimizing Cloud Databases for Cloud Storages)。简单来说,云原生数据库依赖物理复制(Redo日志)完成不同节点之间的数据同步,而DDL触发的元数据/表数据/文件变更同样随着物理复制完成多节点的同步,这三者之间依赖分布式MDL锁提供 实时&一致性 的保证。然而,当MDL锁和物理复制相耦合时,会产生一系列的问题,尤其是日志流 / 锁同步 / 文件操作这三者之间的一致性问题。这里,我们介绍与用户密切相关的两类问题:
5.1 异步元数据锁同步
高频DDL场景下分布式MDL锁的稳定性&实时性。尤其是在MDL锁被堵塞时,不能影响正常物理日志的进行。为了解决这个问题,PolarDB设计了全新的分布式MDL锁机制(用户文档,已默认开启),主要体现在以下两个方面:
- 异步MDL锁复制:将分布式MDL锁与物理复制相互解耦,实现了即使在等待MDL锁时,只读节点仍能继续解析并应用物理日志,保证了物理复制的实时性;
- 并行MDL锁:为了优化高频DDL场景下分布式MDL锁的性能,我们采用一组线程池来并发响应MDL锁的需求。即使某个MDL锁被堵塞,也不会影响其它线程去获取MDL锁,并且这部分线程池会随着DDL的情况动态调整,保证了MDL锁同步的高并发。
5.2 DDL物理复制优化
高压力DDL场景下物理复制的稳定性&实时性。PolarDB中的数据是通过B-Tree来维护索引的,然而大部分Slow DDL操作(如增加主键或二级索引、Optimize Table等)往往需要重建或新增B-Tree索引,导致大量物理日志的产生。而针对物理日志进行的操作往往出现在DDL执行的关键路径上,增加了DDL操作的执行时间。此外,物理复制技术要求只读节点解析和应用这些新生成的物理日志,DDL操作而产生的大量物理日志可能严重影响只读节点的日志同步进程,甚至导致只读节点不可用等问题。 针对上述问题,PolarDB提供了DDL物理复制优化功能(用户文档,已默认开启),主要体现在以下两个方面:
- 主节点加快DDL写日志速度:在主节点写物理日志和只读节点应用物理日志的关键路径上做了全面的优化,使得主节点在执行创建主键DDL操作的执行时间最多可减少20.6%;
- 只读节点加快物理复制速度:只读节点解析DDL的复制延迟时间最多约可减少至原来的0.4%,并且明显降低了CPU / Memory / IO的硬件开销。以下面测试数据为例,在主节点上不论执行1个DDL还是8个DDL,只读节点非常稳定,没有明显抖动。
6. 总结
DDL是PolarDB所有SQL操作中最繁重的一种,DDL的易用性是PolarDB良好使用体验非常重要的一环。本文总结介绍了PolarDB 在全链路MDL锁治理的经验和进展,把简单留给客户,把复杂留给自己,持续优化用户的使用体验。后续将总结介绍PolarDB在Fast DDL方面的工作,PolarDB内核团队将始终如一地为用户打造最佳的云原生关系型数据库。