前言
TiDB 的最新 LTS 版本 v7.5.0 于2023 年 12 月 1 日正式发版,包含很多期待已久的新特性。比如:
-
支持并行运行多个
ADD INDEX
语句 -
可以使用 SQL 实现 Lightning 物理导入
- 大大简化了数据导入的操作效率,极大改善了易用性
-
DDL 支持暂停和恢复。
- 升级时更平滑,切再也不担心超大表加索引要加好几天而影响业务的情况了,可以对大表加索引错峰执行。
-
TiDB Dashboard 性能分析支持 TiKV 堆内存分析。
- 通过 TiDB Dashboard 我们就可以直接获取TiKV 的火焰图和调用图,非常方便!
此外还有优化全局排序、资源管控支持自动管理后台任务、支持管理资源消耗超出预期的查询等功能。
还有一点需要注意,从 v7.5.0 开始不再支持 TiDB Binlog 数据同步功能,强烈建议使用 TiCDC 实现高效稳定的数据同步。
背景
在以前的版本中,遇到过两个与 DDL 执行相关的痛点问题:
- 升级时有正在执行的 DDL,当时遗漏了检查,没有取消它就直接升级了导致升级后表数据异常。
- 对一个 50 亿行的大表添加索引,加了 3 天导致影响在线业务。
在 v7.2.0 中引入的 DDL 任务的暂停和恢复功能,v7.5.0 成为正式功能 (GA)。该功能允许临时暂停资源密集型的 DDL 操作(如创建索引),以节省资源并最小化对在线流量的影响。当资源允许时,可以无缝恢复 DDL 任务,而无需取消和重新开始。DDL 任务的暂停和恢复功能提高了资源利用率,改善了用户体验,并简化了 schema 变更过程。
现在,结合 v6.5 版本后添加索引加速的功能特性,现在可以说根本解决了我们之前遇到的这两个痛点问题。
本文对此进行原理了解和测试验证。
TiDB DDL基本原理
TiDB 采用在线异步变更的方式执行 DDL 语句,从而实现 DDL 语句的执行不会阻塞其他会话中的 DML 语句。
逻辑及物理 DDL
按照是否需要操作表的数据来划分,DDL 语句可以划分为:
-
逻辑 DDL 语句
- 称为 General DDL,只修改元数据,不动表的存储数据。
- 如改表名或列名,耗时通常在 1s 左右,不会影响线上业务。
-
物理 DDL 语句
- 称为 Reorg DDL,会同时修改元数据和表数据。
- 如建索引,不仅需要变更表的定义,也要做一次全表扫描以构建新索引。物理 DDL 只包含加索引以及有损列类型变更(如从
INT
转成CHAR
)这两种类型,通常耗时较长,且执行时间与表数据量、机器配置及业务负载有关。
执行物理 DDL 对业务有影响主要在于两个方面,一方面需要从 TiKV 中读出和回填全表数据,可能会消耗较多的 CPU 及 I/O。另一方面,DDL Owner 所在的 TiDB 节点需要进行相应的计算,因此会消耗更多的 CPU 资源。所以,根据上面的特点,我们需要重点关注的是物理 DDL 造成的影响。
DDL Owner
TiDB 通过 etcd 选举出一个节点来担任 Owner ,Owner 节点会主动续约自己的任期。当 Owner 节点宕机后,其他节点可以通过 etcd 感知到并重新选举出新的 Owner,在集群中继续担任 DDL 任务执行者的角色。我们可以通过 ADMIN SHOW DDL
语句查看当前 DDL owner 节点所在的 tidb-server。
TiDB 在线变更 DDL 通过多个小版本演进的方式,确保多个 TiDB 节点能够正确同步 DDL 变更前后的元数据,并保证期间执行用户事务更改数据的正确性与一致性。如下图是对于 add index 或者 add column DDL 的状态变更,对于 drop index 或者 drop column 则是完全相反的过程。
执行框架
在 TiDB v6.2.0 前,DDL 执行:
- TiKV 集群中只有
general job queue
和add index job queue
两个队列,分别处理逻辑 DDL 和物理 DDL。 - DDL Owner 总是以先入先出的方式处理 DDL Job。
- DDL Owner 每次只能执行一个同种类型(逻辑或物理)的 DDL 任务。
在 v6.2.0 及之后,引入了并行 DDL 处理流程:
-
DDL Owner 能够并行执行 DDL 任务。
-
改善了 DDL Job 队列先入先出的问题。DDL Owner 不再是FIFO,而是选择当前可以执行的 DDL Job。
-
因为 TiDB 中的 DDL 都是在线变更,通过 Owner 即可对新的 DDL Job 进行相关性判断,并根据相关性结果进行 DDL 任务的调度,从而使分布式数据库实现了和传统数据库中 DDL 并发相同的效果。
如下图,并行 DDL 的设计是将 add index job
放到新增的 add index job queue
中去,其它类型的 DDL job 还是放在原来的 job queue。相应的,也增加一个 add index worker
来处理 add index job queue
中的 job。新的并发 DDL 执行框架的实现进一步加强了 DDL 语句的执行能力,并更符合用户的使用习惯。
DDL 暂停与恢复
在实际的生成环境中,添加索引可能会消耗大量资源并影响在线流量,如果是非常敏感的金融场景就更需要注意了。
即使在资源组中进行了限制,或对标记的节点进行了隔离,仍然可能需要在紧急情况下暂停这些任务。
所以,从 v7.2.0 开始支持同时暂停任意数量的后台任务,释放所需的资源,无需取消或重启任务,时候再继续以“断点续传”的方式继续 DDL 操作。这个功能在 v7.5.0 版本正式 GA,可以说是非常期待的。
Pause DDL
操作命令
ADMIN PAUSE DDL
语句用于暂停当前正在运行的 DDL 作业,可以通过 ADMIN SHOW DDL JOBS
语句获取 DDL 作业的 job_id
。
ADMIN PAUSE DDL JOBS job_id [, job_id] ...;
该语句可用于暂停已经发起但未执行完成的 DDL 任务,可以同时暂停多个作业,用逗号隔开任务 id。暂停后,执行 DDL 任务的 SQL 语句不会立即返回,会继续停留在当前界面表现为正在执行。
暂停后的任务,直到被 ADMIN RESUME DDL JOBS
恢复,否则会一直处于暂停状态,如果不及时恢复可能会阻塞后续相同表上的物理 DDL 操作。
验证测试
升级到最新的 v7.5.0 版本并登录集群确认版本:
新建一个表 t ,导入数据最后表行数约为 3300万,然后给其添加索引和验证暂停。
create table t (id bigint(11), name varchar(255));
开启两个命令行窗口,分别添加索引和执行 DDL 暂停操作,观察 DDL 执行情况。
事务1:
添加索引 `alter table t add index name (name)`,可以看到其 1min54s 就可以完成索引的添加。
删掉该索引,重新建立索引。
事务2:
在事务 1 重新建立索引后,在事务 2 执行 Pause 暂停操作,如下图会返回 successful 关键字。
而且,查看 DDL 队列 能够确认添加索引的任务已经被正常暂停,正处于 paused 的状态,如下图:
然后等待了 3min,也已经超过 1min54s ,该时间是前面验证得到的在没有执行 pause 时添加索引需要的实际时间。
观察 DDL 后台任务状态也没有运行,一直处于 paused 的状态。
此时观察集群的内存、IO、网络带宽消耗等指标,没有明显波动处于正常水平,说明此时 DDL 任务真的被暂停了。
在操作中,如果我们暂停的是一个已经完成的 DDL 任务,会看到类似 [ddl:8224]DDLJob:112 not found
的错误,这表示该任务 job-id = 112 已从tikv 存储层中的 DDL 等待队列中被移除。
小结
关于暂停 DDL 任务,总结几点注意内容:
- 手动 pause 操作可以暂停 DDL 作业。
- 只能暂停正在运行 running 或正在等待 queueing 的任务,否则会报错。
- 集群自动暂定的场景,只有版本升级时才会自动触发,其他情况不会自动暂停 DDL 作业。
- 在版本升级时,正在运行的 DDL 会被暂停,在升级过程中发起的 DDL 也将被暂停。升级结束后,所有已暂停的 DDL 作业将自动恢复执行。
- 正式执行中或排队中的 DDL 任务,可以直接暂停,任务暂停后不消耗集群资源。
- DDL pause 命令支持同时批量暂停多个 DDL 任务。
Resume DDL
操作命令
通过 ADMIN SHOW DDL JOBS
可以先获取 DDL 作业的 job_id,再通过 resume 操作恢复当前处于暂停中的 DDL 作业。成功恢复后,执行 DDL 任务的 SQL 语句会一直表现为正在执行。
如下,可以通过 ADMIN RESUME DDL JOBS
语句恢复当前处于暂停中的 DDL 作业,并返回对应作业是否恢复执行:
ADMIN RESUME DDL JOBS job_id [, job_id] ...;
如果恢复失败,会显示失败的具体原因。
验证测试
通过 ADMIN SHOW DDL JOBS
命令获取到当前处于 paused 的任务 job_id=112,然后执行 resume 恢复,开看到会返回 successful 标识,说明已经支持恢复后台暂停的作业。
在执行 resume 恢复命令后,再一次查看 DDL 作业的状态,如下图所示,会由 paused 转为 running,证明已经正常恢复暂停的作业,且作业正在后台执行。
等待一会后,resume 恢复的 DDL 任务正常执行完毕。如下图,在启动 add index 任务后我们暂停时等待了 3min 左右,加上加索引需要耗时约 1min52s,所以总的耗时在 5min左右,符合预期。
这个时间,也可以通过 admin show ddl jobs 后的输出内容进行二次确认,如下,结果同样符合预期。
只能对 paused 状态的的任务执行 resume,如果尝试恢复已经完成的 DDL 任务,会在 RESULT
列看到类似 [ddl:6224] DDL Job:112 not found
的错误,表示该任务已从 DDL 等待队列中被移除。
小结
- 处于非暂停状态中的作业无法被恢复,操作将失败。
- 只能恢复已被暂停的 DDL 作业。如果重复恢复同一个 DDL 作业,会报错。
- 可以同时恢复多个 DDL 作业。
- 版本升级时正在运行的 DDL 作业将被自动暂停,同时在升级过程中发起的 DDL 作业也将被自动暂停。升级结束后,所有已暂停的 DDL 作业将自动恢复执行。
总结
-
v7.5.0 新版本明确支持升级时如果有正在执行的 DDL,会直接后台自动暂停并在升级后自动恢复,避免了由于管理员的操作遗漏检查,导致因为没有取消升级过程的 DDl 而出现的问题。
-
对一个数十亿行及以上的大表添加索引,可以根据业务运行周期,在业务高峰期暂停加索引的 DDL,避免消耗过多资源影响影响在线业务。在业务低谷期重新恢复该任务,可以达到提高集群系统的可用性和保证用户体验。
-
v7.5.0 正式 GA 了 DDL 任务的暂停和恢复操作,结合 v6.5 版本后添加索引加速的功能特性,从根本解决了业务添加索引的痛点问题。
-
优化建议:
- 从易用性的角度来看,DDL 语句创建以后,建议直接提交异步任务到后台执行,前台返回成功,不用阻塞当前操作界面。如果用户想查看任务执行情况,可以通过 admin show ddl 命令查看进度。