分布式数据库Schema 变更 in F1 TiDB
分布式数据库Schema 变更 in F1 & TiDB
【转载】TiDB 源码阅读系列文章(十七)DDL 源码解析 | PingCAP
上述文章主要叙述了从DDL语句发起到执行的过程,简单介绍了弄一套相同的模式来后台处理数据回填,从而提高DDL的并发度的一个方案。
Google F1
【转载】分布式 Schema 变更在 Google F1 的实践 - 知乎 (zhihu.com)
【转载】谷歌 F1 Online DDL的关键点:状态间兼容性 - 知乎 (zhihu.com)
TiDB
【转载】builddatabase/f1/schema-change-implement.md at master · ngaut/builddatabase (github.com)
新定义
- 元数据记录:system database 和 system table 记录异步变更schema的元数据。
- State:根据F1,引入中间状态,none | delete-only | write-only | write reorganization | public(和F1基本一样),删除操作的状态与它的顺序相反,write reorganization 改为 delete reorganization,虽然都是 reorganization 状态,但是由于可见级别是有很大区别的,所以将其分为两种状态标记。
- Lease
- Job:每个单独的 DDL 操作可看做一个 job,放到job queue,等此操作完成时,会将此 job 从 job queue 删除,并在存入 history job queue,便于查看历史 job。
- Worker:每个节点都有一个 worker 用来处理 job。
- **Owner:**整个系统只有一个节点的 worker 能当选 owner 角色,每个节点都可能当选这个角色,当选 owner 后 worker 才有处理 job 的权利。owner 这个角色是有任期的,owner 的信息会存储在 KV 层中。worker定期获取 KV 层中的 owner 信息,如果其中 ownerID 为空,或者当前的 owner 超过了任期,则 worker 可以尝试更新 KV 层中的 owner 信息(设置 ownerID 为自身的 workerID),如果更新成功,则该 worker 成为 owner。在租期内这个用来确保整个系统同一时间只有一个节点在处理 schema 变更。
- Background operations:用于 delete reorganization 的优化处理,引入了 background job, background job queue, background job history queue, background worker 和 background owner,它们的功能跟上面提到的角色功能一一对应。
变更流程
介绍 TiDB SQL 层中处理异步 schema 变更的流程
MySQL client 发起change;
- Load schema:是在每个节点(这个模块跟之前提到的 worker 一样,便于理解可以这样认为)启动时创建的一个 gorountine, 用于在到达每个租期时间后去加载 schema,如果某个节点加载失败 TiDB Server 将会自动挂掉。此处加载失败包括加载超时。
- start job:是在 TiDB SQL 层接收到请求后,给 job 分配 ID 并将之存入 KV 层,之后等待 job 处理完成后返回给上层,汇报处理结果。
- worker:每个节点起一个处理 job 的 goroutine,它会定期检查是否有待处理的 job。 它在得到本节点上 start job 模块通知后,也会直接去检查是否有待执行的 job 。
- owner:可以认为是一个角色,信息存储在 KV 层,其中包括记录当前当选此角色的节点信息。
- job queue:是一个存放 job 的队列,存储在 KV 层,逻辑上整个系统只有一个。
- job history queue:是一个存放已经处理完成的 job 的队列,存储在 KV 层,逻辑上整个系统只有一个。

基本流程:
假设系统中只有两个节点,TiDB Server 1 和 TiDB Server 2。其中 TiDB Server 1 是 DDL 操作的接收节点, TiDB Server 2 是 owner。如下图 2 展示的是在 TiDB Server 1 中涉及的流程,图 3 展示的是在 TiDB Server 2 中涉及的流程。


- MySQL Client 发送给 TiDB Server 一个更改 DDL 的 SQL 语句请求。
- 某个 TiDB Server 收到请求(MySQL Protocol 层收到请求进行解析优化),然后到达 TiDB SQL 层进行执行。这步骤主要是在 TiDB SQL 层接到请求后,会起个 start job 的模块根据请求将其封装成特定的 DDL job,然后将此 job 存储到 KV 层, 并通知自己的 worker 有 job 可以执行。
- 收到请求的 TiDB Server 的 worker 接收到处理 job 的通知后,判断自身是否处于 owner 的角色,如果处于 owner 角色则直接处理此 job,如果没有处于此角色则退出不做任何处理。图中我们假设没有处于此角色,那么其他的某个 TiDB Server 中肯定有一个处于此角色的,如果那个处于 owner 角色节点的 worker 通过定期检测机制来检查是否有 job 可以被执行时,发现了此 job,那么它就会处理这个 job。
- 当 worker 处理完 job 后, 它会将此 job 从 KV 层的 job queue 中移除,并放入 job history queue。
- 之前封装 job 的 start job 模块会定期去 job history queue 查看是否有之前放进去的 job 对应 ID 的 job,如果有则整个 DDL 操作结束。
- TiDB Server 将 response 返回 MySQL Client。
详细流程:以在 Table 中添加 column 为例详细介绍 worker 处理 job 的整个流程

TiDB server1:
-
start job 给 start worker 传递了 job 已经准备完成的信号。
-
worker 开启一个事务,检查自己是否是 owner 角色,结果发现不是 owner 角色(此处跟先前的章节保持一致,假设此节点 worker 不是 owner 角色),则提交事务退出处理 job 的循环,回到 start worker 等待信号的循环。如果是owner,则是TiDB Server2的任务。
- start job 的定时检查触发后,会检查 job history queue 是否有之前自己放入 job queue 中的 job(通过 jobID)。如果有则此 DDL 操作在 TiDB SQL 完成,上抛到 MySQL Protocol 层,最后返回给 Client, 结束这个操作。
TiDB server2:
- start worker 中的定时器到达时间。
- 开启一个事务,检查发现本节点为 owner 角色。
- 从 KV 层获取队列中第一个 job(假设就是 TiDB Server 1 之前放入的 job),判断此 job 的类型并对它做相应的处理。
- 此处 job 的类型为 add column,然后流程到达图中 get column information 步骤。
- 取对应 table info(主要通过 job 中 schemaID 和 tableID 获取),然后确定添加的 column 在原先的表中不存在或者为不可见状态。
- 如果新添加的 column 在原先表中不存在,那么将新 column 信息关联到 table info。
- 在前面两个步骤中发生某些情况会将此 job 标记为 cancel 状态,并返回 error,到达图中 returns error 流程。比如发现对应的数据库、数据表的状态为不存在或者不可见(即它的状态不为 public),发现此 column 已存在并为可见状态等一些错误,这里就不全部列举了。
- schema 版本号加 1。
- 将 job 的 schema 状态和 table 中 column 状态标记为 delete only, 更新 table info 到 KV 层。
- 因为 job 状态没有 finish(即 done 或者 cancel 状态),所以直接将 job 在上一步更新的信息写入 KV 层。
- 在执行前面的操作时消耗了一定的时间,所以这里将更新 owner 的 last update timestamp 为当前时间(防止经常将 owner 角色在不同服务器中切换),并提交事务。
- 循环执行步骤 2、 3、 4.a、5、 6、 7 、8,不过将6中的状态由 delete only 改为 write only。
- 循环执行步骤 2、 3、 4.a、5、 6、 7 、8,不过将6中的状态由 write only 改为 write reorganization。
- 循环执行步骤 2、 3、 4.a、5,获取当前事务的快照版本,然后给新添加的列填写数据。通过应版本下需要得到的表的所有 handle(相当于 rowID),出于内存和性能的综合考量,此处处理为批量获取。然后针对每行新添加的列做数据填充,具体操作如下(下面的操作都会在一个事务中完成):
- 用先前取到的 handle 确定对应行存在,如果不存在则不对此行做任何操作。
- 如果存在,通过 handle 和 新添加的 columnID 拼成的 key 获取对应列。获取的值不为空则不对此行做任何操作。
- 如果值为空,则通过对应的新添加行的信息获取默认值,并存储到 KV 层。
- 将当前的 handle 信息存储到当前 job reorganization handle 字段,并存储到 KV 层。假如 12 这个步骤执行到一半,由于某些原因要重新执行 write reorganization 状态的操作,那么可以直接从这个 handle 开始操作。
- 将调整 table info 中 column 和 index column 中的位置,将 job 的 schema 和 table info 中新添加的 column 的状态设置为设置为public, 更新 table info 到 KV 层。最后将 job 的状态改为 done。
- 因为 job 状态已经 finish,将此 job 从 job queue 中移除并放入 job history queue 中。
- 执行步骤8,与之前的步骤一样 12, 13, 14 和 15 在一个事务中。
优化:
原本删除操作是:public → write only → delete only → delete reorganization → none,优化的处理是去掉 delete reorganization 状态,并把此状态需要处理的元数据的操作放到 delete only 状态时,把具体删除数据的操作放到后台处理,然后直接把状态标为 none。
好处:对删除数据库,删除数据表等减少一个状态,即 2 倍 lease 的等待时间。
实现:
在其处于 delete only 状态时,就会把元数据删除,而对起表中具体数据的删除则推迟到后台运行,然后结束 DDL job。
到后台运行的任务的流程跟之前处理任务的流程类似,详细过程如下:
- 在图 4 中判定 finish 操作为 true 后,判断如果是可以放在后台运行(暂时还只是删除数据库和表的任务),那么将其封装成 background job 放入 background job queue, 并通知本机后台的 worker 将其处理。
- 后台 job 也有对应的 owner,假设本机的 backgroundworker 就是 background owner 角色,那么他将从 background job queue 中取出第一个 background job, 然后执行对应类型的操作(删除表中具体的数据)。 如果执行完成,那么从 background job queue 中将此 job 删除,并放入 background job history queue 中。 注意步骤2和步骤 3需要在一个事务中执行。
相关文章:
分布式数据库Schema 变更 in F1 TiDB
分布式数据库Schema 变更 in F1 & TiDB 【转载】TiDB 源码阅读系列文章(十七)DDL 源码解析 | PingCAP 上述文章主要叙述了从DDL语句发起到执行的过程,简单介绍了弄一套相同的模式来后台处理数据回填,从而提高DDL的并发度的一…...
图形库篇 | EasyX | 图像处理
图形库篇 | EasyX | 图像处理 图像类型 IMAGE表示图像,用于定义一个图像变量,与导入的图片资源一一对应。 IMAGE img;加载与绘制图像 函数功能函数加载图像void loadimage(IMAGE* pDstImg,LPCTSTR pImgFile,int nwidth = 0,int nHeight = 0,bool bResize = false)绘制图像v…...
AWTK UI 自动化测试工具发布
AWTK UI 自动化 提供了兼容 Appium 的接口,可以使用 Appium 的工具来进行 UI 自动化测试。但是使用起来有点麻烦,用的人不多,所以最终决定开发一个 AWTK 专用的 UI 自动化测试工具。相比 Appium,这个工具有下列特点: …...
Java后端开发——JDBC入门实验
JDBC(Java Database Connectivity)是Java编程语言中用于与数据库建立连接并进行数据库操作的API(应用程序编程接口)。JDBC允许开发人员连接到数据库,执行各种操作(如插入、更新、删除和查询数据)…...
LCA
定义 最近公共祖先简称 LCA(Lowest Common Ancestor)。两个节点的最近公共祖先,就是这两个点的公共祖先里面,离根最远的那个。 性质 如果 不为 的祖先并且 不为 的祖先,那么 分别处于 的两棵不同子树中&#…...
ts学习02-数据类型
新建index.html <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </h…...
javaSE的发展历史以及openjdk和oracleJdk
1 JavaSE 的发展历史 1.1 Java 语言的介绍 SUN 公司在 1991 年成立了一个称为绿色计划(Green Project)的项目,由 James Gosling(高斯林)博士领导,绿色计划的目的是开发一种能够在各种消费性电子产品&…...
【入门Flink】- 10基于时间的双流联合(join)
统计固定时间内两条流数据的匹配情况,需要自定义来实现——可以用窗口(window)来表示。为了更方便地实现基于时间的合流操作,Flink 的 DataStrema API 提供了内置的 join 算子。 窗口联结(Window Join) 一…...
【Python Opencv】图片与视频的操作
文章目录 前言一、opencv图片1.1 读取图像1.2 显示图像1.3 写入图像1.4 示例代码 二、Opencv视频2.1 从相机捕获视频获取摄像头一帧一帧读取显示图片VideoCapture 中的get和set函数示例代码 2.2 从文件播放视频示例代码 2.3 保存视频示例代码 总结 前言 在计算机视觉和图像处理…...
【从入门到起飞】JavaAPI—System,Runtime,Object,Objects类
🎊专栏【JavaSE】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 🎄欢迎并且感谢大家指出小吉的问题🥰 文章目录 🍔System类⭐exit()⭐currentTimeMillis()🎄用…...
【Git】的分支和标签的讲解及实际应用场景
目录 讲解 环境讲述 分支标签的区别 分支 命令 场景应用 标签 命令 标签规范 讲解 环境讲述 当软件从开发到正式环境部署的过程中,不同环境的作用 开发环境:用于开发人员进行软件开发、测试和调试。在这个环境中,开发人员可以快速地…...
修改django开发环境runserver命令默认的端口
runserver默认8000端口 虽然python manage.py runserver 8080 可以指定端口,但不想每次runserver都添加8080这个参数 可以通过修改manage.py进行修改,只需要加三行: from django.core.management.commands.runserver import Command as Ru…...
kubeadm安装k8s高可用集群
目录 一、环境规划 二、注意事项: 三、环境准备: 1. 关闭防火墙规则,关闭selinux,关闭swap交换: 2. 修改主机名 3. 所有节点修改hosts文件: 4. 所有节点时间同步: 5. 所有节点实现Linux的资…...
来看看电脑上有哪些不为人知的小众软件?
电脑上的各类软件有很多,除了那些常见的大众化软件,还有很多不为人知的小众软件,专注于实用功能,简洁干净、功能强悍。 1.桌面停靠栏工具——BitDock BitDock是一款运行在Windows系统中的桌面停靠栏工具,功能实…...
一个进程最多可以创建多少个线程?
前言 话不多说,先来张脑图~ linux 虚拟内存知识回顾 虚拟内存空间长啥样 在 Linux 操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同位数的系统,地址空间的范围也不同。比如最常见的 32 位和 64 位系统&am…...
ElasticSearch文档分析
ElasticSearch文档分析 包含下面的过程: 将一块文本分成适合于倒排索引的独立的 词条将这些词条统一化为标准格式以提高它们的“可搜索性”,或者 recall 分析器执行上面的工作。分析器实际上是将三个功能封装到了一个包里: 字符过滤器 首先&a…...
Xilinx FPGA平台DDR3设计详解(一):DDR SDRAM系统框架
DDR SDRAM(双倍速率同步动态随机存储器)是一种内存技术,它可以在时钟信号的上升沿和下降沿都传输数据,从而提高数据传输的速率。DDR SDRAM已经发展了多代,包括DDR、DDR2、DDR3、DDR4和DDR5,每一代都有不同的…...
Spring Data JPA方法名命名规则
最近巩固一下JPA,网上看到这些资料,这里记录巩固一下。 一、Spring Data Jpa方法定义的规则 简单条件查询 简单条件查询:查询某一个实体类或者集合。 按照Spring Data的规范的规定,查询方法以find | read | get开头&…...
【Leetcode Sheet】Weekly Practice 15
Leetcode Test 2586 统计范围内的元音字符串数(11.7) 给你一个下标从 0 开始的字符串数组 words 和两个整数:left 和 right 。 如果字符串以元音字母开头并以元音字母结尾,那么该字符串就是一个 元音字符串 ,其中元音字母是 a、e、i、o、u…...
人力资源社会保障部办公厅关于推行专业技术人员职业资格电子证书的通知
(人社厅发〔2021〕97号) 各省、自治区、直辖市及新疆生产建设兵团人力资源社会保障厅(局),中共海南省委人才发展局,国务院有关部门、直属机构人事部门,有关协会、学会: 为贯彻落实…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道
文/法律实务观察组 在债务重组领域,专业机构的核心价值不仅在于减轻债务数字,更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明,合法债务优化需同步实现三重平衡: 法律刚性(债…...
