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

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介: 本文简单阐述了PolarDB-X实现拆分规则变更过程中使用到的各项技术点。

作者:姑胥



背景


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


1.png



数据如何重新分布?


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


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



数据Repartition的关键问题


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


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



传统的Repartition方案


2.png


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


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


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


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


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


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



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节点开启了增量双写,而另一些没有。于是会出现这样的情况:


  1. 计算节点CN0已开启双写,并在老表和新表分别写入了3条数据,如下图
  2. 计算节点CN1未开启双写,执行了delete语句,删除了id为3的一条数据,但是未删除新表的数据
  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诸多特性中的一个。想要了解更多内容,欢迎关注公众号内的其他文章。



参考文献


  1. Asymmetric-Partition Replication for Highly Scalable Distributed Transaction Processing in Practice
  2. Online, Asynchronous Schema Change in F1
  3. What’s Really New with NewSQL
  4. https://dev.mysql.com/doc/refman/5.6/en/innodb-online-ddl.html
  5. https://dev.mysql.com/doc/refman/5.6/en/metadata-locking.html
  6. https://zhuanlan.zhihu.com/p/289870241
  7. https://zhuanlan.zhihu.com/p/329978215
  8. https://zhuanlan.zhihu.com/p/341685541
  9. https://zhuanlan.zhihu.com/p/346026906



【相关阅读】

子查询漫谈

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

分布式数据库如何实现 Join?

PolarDB-X 让“Online DDL”更Online

相关实践学习
快速体验PolarDB开源数据库
本实验环境已内置PostgreSQL数据库以及PolarDB开源数据库:PolarDB PostgreSQL版和PolarDB分布式版,支持一键拉起使用,方便各位开发者学习使用。
目录
相关文章
|
4月前
|
SQL 存储 关系型数据库
PolarDB-X 原生无锁变更,比 gh-ost 更快、更稳定
无论是单机数据库还是分布式数据库,无锁变更都是非常重要的能力。PolarDB-X 无锁变更技术能够极大提升数据库在线操作的灵活性与安全性,它允许在不影响业务连续性的情况下,对表结构进行修改,如增加列、变更列类型等,这对于全天候无间断服务的业务方来说是至关重要的。
|
5月前
|
存储 关系型数据库 分布式数据库
PolarDB产品使用问题之如何变更存储配置
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
5月前
|
关系型数据库 MySQL 分布式数据库
PolarDB产品使用问题之如何对集群变更配置
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
SQL Cloud Native 算法
PolarDB-X 分区建列类型变更
作为一款分布式数据库,变更表的列类型,无论变更的是否是分区键,都需要保证各个分片以及元数据的一致性,因此对于非分区键的列类型变更也不只是简简单单的下推执行就可以的,后续会有一篇文章做详细的阐述,本文主要阐述的是如何对分区键的列类型做变更。
|
SQL 关系型数据库 分布式数据库
分布式关系型数据库服务DRDS——拆分键
分布式关系型数据库服务DRDS——拆分键自制脑图, 即分库/分表字段。 DRDS 根据拆分键的值将数据表水平拆分到后端的每一个 RDS 分库里。DRDS 里除了可以定义分库键以外,每一张逻辑表都可以定义自己的拆分键。拆分键暂时只能是单个字段。如果分库键与分表键相同,那么在插入时只需要指定该分库/分表键。如果分库键与分表键不同则在插入时需要同时指定分库键和分表键。
659 0
分布式关系型数据库服务DRDS——拆分键
|
SQL 机器学习/深度学习 算法
PolarDB-X拆分键推荐
PolarDB-X 2.0提供了透明分布式的能力,默认进行主键的哈希拆分,让用户无感知的从单机数据库迁移到分布式数据库。拆分键的选择是学术界和工业界研究已久的问题,一个重要选型是tp优先还是ap优先,两者难以同时兼顾。
PolarDB-X拆分键推荐
|
Cloud Native 分布式数据库 数据安全/隐私保护
直播预告 | PolarDB-X 动手实践系列——PolarDB-X 的表组与分区变更
为了追求极致的性能,在AUTO模式下,PolarDB-X 引入了表组的概念,可以维持表之间的关联查询稳定的下推关系,消除分布式事务。同时提供丰富的分区管理能力(例如热点处理,数据隔离等),满足业务上的不同需求。本期分享将对上述功能以及背后的设计逻辑展开介绍。
直播预告 | PolarDB-X 动手实践系列——PolarDB-X 的表组与分区变更
|
存储 监控 Cloud Native
PolarDB-X 1.0-用户指南-监控与报警-管理报警规则
您可以通过PolarDB-X控制台创建和管理阈值报警规则,帮助您及时了解PolarDB-X实例、数据库和存储资源的各项监控数据是否出现异常并快速进行处理。本文将以实例监控为例介绍如何管理报警规则。
214 0
PolarDB-X 1.0-用户指南-监控与报警-管理报警规则
|
SQL Cloud Native 算法
PolarDB-X 1.0-用户指南-数据表管理-调整拆分键
您可以在PolarDB-X控制台上管理数据表,调整数据表的拆分键。
205 0
PolarDB-X 1.0-用户指南-数据表管理-调整拆分键
|
存储 SQL 关系型数据库
PolarDB-X 1.0-用户指南-自定义控制指令-查看规则和节点拓扑类语句
本文汇总了PolarDB-X支持的查看规则和节点拓扑类的语句。
219 0

相关产品

  • 云原生分布式数据库 PolarDB-X
  • 云原生数据库 PolarDB