DM 分库分表 DDL “悲观协调” 模式介绍丨TiDB 工具分享

2024-04-08 02:08

本文主要是介绍DM 分库分表 DDL “悲观协调” 模式介绍丨TiDB 工具分享,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

TiDB 作为分库分表方案的一个 “终结者”,获得了许多用户的青睐。在切换到 TiDB 之后,用户告别了分库分表查询和运维带来的复杂度。但是在从分库分表方案切换到 TiDB 的过程中,这个复杂度转移到了数据迁移流程里。TiDB DM 工具为用户提供了分库分表合并迁移功能,在数据迁移的过程中,支持将分表 DML 事件合并迁移,并一定程度支持上游分表进行 DDL 变更。

本文以及后续文章主要介绍分库分表合并迁移时,各分表 DDL 变更的协调。DM 的分库分表 DDL 协调可配置为 “悲观协调” 和 “乐观协调” ,本文主要介绍 TiDB DM 分库分表 DDL 协调的 “悲观协调” 模式。后续文章会介绍 “乐观协调” 模式。

分库分表 DDL 的问题(简略版)

本节首先以一个例子粗略介绍分库分表 DDL 对数据迁移的影响,然后就这个问题给出更加正式的定义。

假设在两个上游有两个分表 t1、t2,下游表为 t。

t1 接下来的同步事件是 INSERT (3,3),t2 接下来的同步事件是 DROP COLUMN c2。如果 DROP COLUMN c2 先被同步到下游,在同步到 INSERT (3,3) 时就会因为缺少 c2 列而报错。
因此我们要对 DDL 同步事件进行特殊处理。

分库分表合并迁移的定义

接下来我们尝试使用更正式一点的语言来描述这个问题,从而引出如何正确解决这个问题。

从用户的角度来讲,数据库的用途主要的是查询,在分库分表合并前后,查询的结果应该是相同的(不考虑 LIMIT 等算子以及不确定性查询)。也就是说,各分表查询结果的并集应当等于迁移后的查询结果。容易得到一个满足此要求的充分条件:各分表数据的并集应当等于迁移后的表数据。考虑到同步延迟的影响,也就是当前时刻下游表的数据等于各分表在过去某时刻数据的并集。
对于这个定义而言,数据迁移就是让各分表的同步时刻不断向前推进。如果在同步某事件前,下游表与各分表满足定义,那么我们将一个分表的同步事件以相同影响的方式应用到下游,就将该分表的同步时刻正确地推进了。

分库分表 DDL 的问题(正式版)

从上面的定义来看,DDL 会造成两个方面的问题。

首先是 DDL 可能会变更表结构。参照之前的例子,如果 t1、t2 都有 DROP COLUMN c2 事件,DM 先同步到了 t2 的该事件,而同步事件需要以相同影响的方式应用到下游,我们应该只将下游 t2 对应的数据 DROP COLUMN。显然下游 t1、t2 的数据共享一个表结构,无法完成这个操作。因此 t2 的该事件暂时不能被同步。
另一个问题是,部分 DDL 即使不影响表结构,也会产生对数据产生影响。例如 ALTER TABLE DROP COLUMN c, ADD COLUMN c DEFAULT xx,会将一个分表的 c 列全部修改为 xx。目前 DM 的实现同样无法将这个事件以相同影响的方式应用到下游

解决方法

对于上述 DDL 引入的问题并基于前文对于同步正确性的定义,我们可以得到一个满足要求的充分条件:当某分表出现 DDL 同步事件时,我们将其同步暂停;直到所有分表都出现该 DDL 同步事件时,我们将 DDL 应用到下游并恢复所有分表的同步。此时我们可以保证下游表的 DDL 产生的影响等于所有分表都进行了 DDL(不考虑非确定性 DDL,例如 DDL 新增列默认值为 current_timestamp)。

悲观协调例子

我们仍然以两张表 t1、t2 的合并迁移为例,观察 binlog 同步进度

左图中,当分表 t1 遇到 DDL 时,t2 同步事件还没有到这条 DDL,因此 t1 同步应当被暂停。当进展到右图时,t1、t2 分表都出现了相同的 DDL,因此此时可以将这条 DDL 应用到下游并恢复 t1、t2 的同步。\

在某些情况下,t1、t2 可能位于一个 binlog 流之中,因此上图中看似独立的流的暂停与恢复,实际实现为在同一个 binlog 流中跳过事件及回滚同步位置。

如上图,我们需要在事件 1、2、4、5、6 之后同步事件 3,因此在 binlog 流中首次遇到事件 3 时跳过,并在事件 6 完成之后重新从事件 3 开始同步,并跳过已经同步的 4、5、6 事件。

悲观协调模式限制

可以看到这种协调模式解决方法有如下的限制:

  • 出现 DDL 同步事件时分表会暂停,会导致同步延迟增加。这可能会导致恢复同步时,上游 binlog 已经被清理
  • 不支持只变更部分分表以进行灰度测试时的场景。灰度期间其余分表的同步会暂停。此外如果灰度测试结果是回滚时,无法恢复同步
  • 要求所有分表以相同的顺序出现 DDL 同步事件
  1. 如果分表由于误操作而进入 DDL 不一致的状态,修复操作较为复杂
  2. 对于 DM 的使用者而言,可能无法控制上游 DDL 的发起从而无法满足条件\

因为悲观协调模式的种种限制,DM 也提供了新的乐观协调模式,我们将在后续的文章中具体介绍,希望大家能够在深入了解两种协调模式的原理和使用限制后,根据场景选择合适的模式进行分库分表的合并迁移。

这篇关于DM 分库分表 DDL “悲观协调” 模式介绍丨TiDB 工具分享的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/884295

相关文章

MySQL大表数据的分区与分库分表的实现

《MySQL大表数据的分区与分库分表的实现》数据库的分区和分库分表是两种常用的技术方案,本文主要介绍了MySQL大表数据的分区与分库分表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. mysql大表数据的分区1.1 什么是分区?1.2 分区的类型1.3 分区的优点1.4 分

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

jvm调优常用命令行工具详解

《jvm调优常用命令行工具详解》:本文主要介绍jvm调优常用命令行工具的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一 jinfo命令查看参数1.1 查看jvm参数二 jstack命令2.1 查看现场堆栈信息三 jstat 实时查看堆内存,gc情况3.1

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程

Python实现html转png的完美方案介绍

《Python实现html转png的完美方案介绍》这篇文章主要为大家详细介绍了如何使用Python实现html转png功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 1.增强稳定性与错误处理建议使用三层异常捕获结构:try: with sync_playwright(

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir