当前位置:主页 > 查看内容

快速掌握 PolarDB-X 拆分规则变更能力!

发布时间:2021-04-24 00:00| 位朋友查看

简介:作者 姑胥 背景 PolarDB-X作为一款云原生分布式关系型数据库[6] 支持通过拆分规则将一张逻辑表的数据分布在多个数据节点中。同时 也支持变更拆分规则 将数据进行重新分布[9]。数据重新分布 Repartition 的能力是分布式数据库的核心能力之一。当业务规模扩大……

作者 姑胥



背景


PolarDB-X作为一款云原生分布式关系型数据库[6] 支持通过拆分规则将一张逻辑表的数据分布在多个数据节点中。同时 也支持变更拆分规则 将数据进行重新分布[9]。数据重新分布 Repartition 的能力是分布式数据库的核心能力之一。当业务规模扩大时 它可以将数据分布到更多的节点 以达到水平扩展 ScaleOut 的目的。当业务规则剧烈变化时 它也可以将数据以新的拆分规则分布 从而提高查询的性能 更好地适应新的业务规则。下图展示如何将一张单表 通过一条简单的DDL语句online地变更为一个分库分表。


1.png



数据如何重新分布


其实早在分布式数据库中间件大行其道的年代 开发人员就尝试过各种方式来解决数据Repartition的问题。但是无一例外 这些方案都非常复杂和危险 开发人员通常只能在半夜流量低峰期做Repartition 前前后后需要准备好详细的设计、论证、回滚方案。并且基于分布式的特点 在新老数据切换的那一段时间 几乎总是得在数据一致性和可用性之间做一个痛苦的选择。所以很多方案会有一个停写阶段或数据比对阶段 或两者皆有。


PolarDB-X充分论证了分布式数据库Repartition的各项技术细节后 能够做到Repartition过程中数据强一致、高可用、对业务透明、一条简单DDL语句搞定。



数据Repartition的关键问题


本文讨论的主要是表级别的数据Repartition。比如 将一张拆分表的数据按照新的规则重新分布到不同的数据节点。但是重新分布的过程需要时间 这段时间内又会有新的增量数据进入系统。当所有的数据都重新分布完成后 要将新老数据进行切换 最后停止双写并删掉所有的老数据。仔细研究上述过程的细节后 可以将其概括为这3个关键子问题 后续我们将按照这3个子问题评估各个实现方案


如何进行存量、增量的数据同步
比如 应用层双写、数据同步中间件、数据库触发器等。
如何进行流量切换
主要考虑读写流量在新老数据中的迁移。
如何控制Repartition的整体流程
主要考虑如何组合各个步骤 比如什么时候开启/停止增量数据同步 什么时候进行流量切换。
如果故障了怎么恢复或回滚等。



传统的Repartition方案


2.png


一个传统的Repartition流程大概是这样的


应用集群初始状态的读写流量都在老的数据表上。
增加新表并开启双写。双写方式可能是应用层双写 或基于binlog数据同步 或数据库触发器实现双写等。但这个步骤 如果没有事务保证的话就很容易造成数据不一致。
启动存量数据同步。
可能会启动一个数据比对服务 如果有数据不一致的话 也需要人工介入处理。
当存量数据同步完成 增量数据追平后 将读流量切到新表 保持双写一段时间。
撤走老表的写流量 完成Repartition


但是如果我们仔细推敲一下 发现很多步骤根本就没办法保障数据一致性


上述第2步 开启双写时 分布式系统无法保证所有节点都一起开启双写。所以一定有一段时间只有部分节点开启了双写。那就会出现Orphan Data Anomaly的问题 后文将说明 [2][8] 造成新老表的数据不一致。传统的Repartition方案要解决这个问题的话 只能停写。
同理 上述第6步撤走老表的写流量时 也会出现数据不一致问题。
上述第2步 增量数据的双写一致性难以保障。


除了数据一致性问题外 依然存在很多繁琐但依然十分重要的问题


整个流程基本由人工串联 多个环节需要人工介入。所以前期需要学习和理解成本 过程中也需要参与成本。
传统方案如果要避免数据不一致问题 在关键节点需要停写 这会对业务造成干扰。
整个流程十分脆弱地粘合在了一起 容错性非常差。



PolarDB-X的Repartition方案


PolarDB-X 是一款存储与计算分离的分布式数据库 所以在架构中会分为计算节点 CN 和存储节点 DN 以及一个元数据库 GMS 。CN负责SQL的parse、optimize、execute DN负责数据存储 GMS负责存储元数据。为了性能考虑 每个CN节点都会缓存一份元数据。


3.png


如何进行存量、增量数据同步


增量数据双写


在分布式的增量数据双写场景中 双写的2端经常会分布在不同的DN节点中 所以自然地单机事务无法使用。而前文已经论证过 XA事务、binlog同步、触发器都无法保证双写的2端数据强一致。PolarDB-X使用了内置的基于TSO的分布式事务[7] 感兴趣的同学可以点击直达文章 实现增量数据同步 从而保证了Repartition过程中 任意时刻读流量切换的数据的强一致性。


值得注意的是 如果一行数据的拆分列的值被修改了 那这行数据可能会路由到另一个数据节点[1]。所以这时候执行的其实是 原数据节点的delete操作 新数据节点的insert操作。在Repartition过程中 因为前后的拆分规则不同 所以一行数据的update操作 可能会演变成4个数据节点参与的分布式事务 如果有全局二级索引会更多 。PolarDB-X会处理好所有这些工作 用户无需任何感知 可将PolarDB-X视为一个单机数据库操作。


存量数据同步


在存量数据同步时 PolarDB-X会分段进行同步。每一段同步过程中 PolarDB-X会在TSO事务中 先尝试获取源端数据的S锁 然后再写入目标端。如果目标段已经有相同数据 则表明增量双写阶段已经将这些数据同步过了 忽略即可。


分布式事务与单机事务一样 会产生死锁。当存量同步而给原表某段数据加S锁时 如果业务Update流量较大 可能导致分布式死锁的发生 PolarDB-X有一个分布式死锁检测模块可以解决这个问题。死锁解除后 存量同步模块会重试。


如何进行流量切换


Online Schema Change


首先我们来看一下前文提到的Orphan Data Anomaly问题[2]是怎么发生的 当开启增量数据双写时 PolarDB-X的CN节点内存中的元数据不是同时刷新的 而是有一个先后顺序 所以一定存在一段时间 某些CN节点开启了增量双写 而另一些没有。于是会出现这样的情况


计算节点CN0已开启双写 并在老表和新表分别写入了3条数据 如下图
计算节点CN1未开启双写 执行了delete语句 删除了id为3的一条数据 但是未删除新表的数据
新表和老表出现了数据不一致


4.png


这一问题在Google的Online, Asynchronous Schema Change in F1这篇论文中有过详细的论证。PolarDB-X引入了Online Schema Change 感兴趣的同学可以点击直达文章 以解决此类的问题 在此不再赘述。对于Repartition来说 引入下图中的这些状态 以此保证任意2个相邻状态都是兼容的 不会发生数据一致性问题。我们可以具体来看看其中几个最关键的状态


target_delete_only和target_write_only 如上所述 当我们有多个CN节点时 直接开启增量双写会导致Orphan Data Anomaly问题。所以在开启双写之前 我们先让所有的CN节点都达到target_delete_only状态 然后再达到target_write_only状态 就是双写状态 。CN节点在target_delete_only下 只会执行delete语句 如果是update 则转换成delete执行 。代入上图中的例子 CN1会先达到target_delete_only状态 所以即便未开启双写 也能够删除新表中id为3的数据 从而保证了数据的一致性。
source_delete_only和source_absent 前文也论述过 如果直接停掉老表的双写 会造成数据不一致。所以我们在source_absent 也就是停止了双写的状态 之前引入了一个source_delete_only状态。它也保证了老表在下线过程中不会产生Orphan Data Anomaly问题。


5.png


如何控制整体流程并保证稳定性


PolarDB-X以DDL的形式为用户提供拆分规则变更 也就是Repartition 的能力 DDL也需要保障ACID特性 但数据重新分布可能需要花费较长时间 系统因断电等原因而故障在所难免。


DDL引擎


所以PolarDB-X也实现了一套稳定的DDL执行框架 它会将DDL分成很多个步骤 每个步骤都是幂等的。这样就可以保证DDL任务可以随时中断 然后恢复继续运行或者回滚。通过DDL将所有的步骤串联 也将所有的人工操作排除在外 开发人员再也不需要做Repartition的方案设计 再也不需要半夜三更手动执行数据库的高危操作了。


分布式MDL死锁检测


MySQL在5.7版本引入Online DDL能力后 使得DDL能够更好地与读写事务并行 相比于之前的版本有了很大的改善。Online DDL的基本原理是只在关键的时刻获取MDL锁 而不是在整个DDL阶段都持有MDL锁。PolarDB-X在执行Repartition时也会分多个阶段获取多次MDL锁 从而允许事务达到更高的并发度。但MDL锁有个危险的特性是 它是一个公平锁 并且可能造成MDL死锁。


多次获取MDL锁提高了性能的同时 实际上增加了MDL死锁产生的概率 而MDL死锁一旦发生 会导致后续所有的读写事务都被阻塞 MySQL的MDL默认超时时间1年 危害远大于普通的数据死锁。因此PolarDB-X也提供了一个分布式MDL死锁检测模块 用于在关键时刻解除分布式MDL死锁。



总结


灵活的拆分规则变更能力是分布式数据库的重要特性。PolarDB-X中有3种表类型 单表、广播表、分库分表。使用拆分规则变更能力 用户可以将数据表进行任意的表类型转换 从而更好地适应业务发展。PolarDB-X在提供拆分规则变更能力的同时 保证了数据的强一致、高可用、对业务透明、并且使用起来非常方便。本文简单阐述了PolarDB-X实现拆分规则变更过程中使用到的各项技术点 如读者可见 我们之所以将拆分规则变更能力集成到数据库内核中 是因为很多数据一致性问题非此不可解决。这也是分布式数据库区别于分布式数据库中间件的重要特性之一。


拆分规则变更能力只是PolarDB-X诸多特性中的一个。想要了解更多内容 欢迎关注公众号内的其他文章。



参考文献


Asymmetric-Partition Replication for Highly Scalable Distributed Transaction Processing in Practice
Online, Asynchronous Schema Change in F1What’s Really New with NewSQL
https://dev.mysql.com/doc/refman/5.6/en/innodb-online-ddl.html
https://dev.mysql.com/doc/refman/5.6/en/metadata-locking.html
https://zhuanlan.zhihu.com/p/289870241
https://zhuanlan.zhihu.com/p/329978215
https://zhuanlan.zhihu.com/p/341685541
https://zhuanlan.zhihu.com/p/346026906



【相关阅读】

子查询漫谈

探索 | PolarDB-X 实现高效灵活的分区管理

分布式数据库如何实现 Join

PolarDB-X 让“Online DDL”更Online


本文转自网络,原文链接:https://developer.aliyun.com/article/783732
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!

推荐图文

  • 周排行
  • 月排行
  • 总排行

随机推荐