---
title: 'TiDB v7.5.0 DDL 启停特性分析'
date: '2023-12-22T22:06:58+08:00'
status: publish
permalink: /blog/start-stop-characteristics-tidb-ddl
author: '周, 盛虎'
excerpt: 'DDL 任务的暂停和恢复功能，允许临时暂停资源密集型的 DDL 操作（如创建索引），以节省资源并最小化对在线流量的影响。当资源允许时，可以无缝恢复 DDL 任务，而无需取消和重新开始。本文对此进行原理了解和测试验证。'
type: post
id: 6031
category:
    - 案例实践
tag:
    - '在线 DDL'
post_format: []
profile:
    - 李文杰
---
> 李文杰：数据架构师，TUG 广州地区活动组织者

前言
--

TiDB 的最新 LTS 版本 v7.5.0 于 2023 年 12 月 1 日正式发版，包含很多期待已久的新特性。比如：

- 支持并行运行多个 ADD INDEX 语句
- 可以使用 SQL 实现 Lightning 物理导入 
  - 大大简化了数据导入的操作效率，极大改善了易用性
- DDL 支持暂停和恢复。 
  - 升级时更平滑，切再也不担心超大表加索引要加好几天而影响业务的情况了，可以对大表加索引错峰执行。
- TiDB Dashboard 性能分析支持 TiKV 堆内存分析。 
  - 通过 TiDB Dashboard 我们就可以直接获取 TiKV 的火焰图和调用图，非常方便！

此外还有优化全局排序 、 资源管控支持自动管理后台任务 、 支持管理资源消耗超出预期的查询等功能。

还有一点需要注意，从 v7.5.0 开始不再支持 [TiDB Binlog](https://docs.pingcap.com/zh/tidb/stable/tidb-binlog-overview "TiDB Binlog") 数据同步功能，强烈建议使用 [TiCDC](https://docs.pingcap.com/zh/tidb/stable/ticdc-overview "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 则是完全相反的过程。

![在线变更 DDL](https://img1.pingcap.com/files/2023/12/20240101142848283.png)### 执行框架

在 TiDB v6.2.0 前，DDL 执行：

- TiKV 集群中只有 `general job queue` 和 `add index job queue` 两个队列，分别处理逻辑 DDL 和物理 DDL。
- DDL Owner 总是以先入先出的方式处理 DDL Job。
- DDL Owner 每次只能执行一个同种类型（逻辑或物理）的 DDL 任务。

![执行框架](https://img1.pingcap.com/files/2023/12/20240101142948764.png)在 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](https://img1.pingcap.com/files/2023/12/20240101143018755.png)### 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 版本并登录集群确认版本：

![版本升级](https://img1.pingcap.com/files/2023/12/20240101143118547.png)新建一个表 t，导入数据最后表行数约为 3300 万，然后给其添加索引和验证暂停。

```
create table t (id bigint(11), name varchar(255));
```

开启两个命令行窗口，分别添加索引和执行 DDL 暂停操作，观察 DDL 执行情况。

**事务 1**

添加索引 `alter table t add index name (name)`，可以看到其 1min54s 就可以完成索引的添加。

删掉该索引，重新建立索引。

![重新建立索引](https://img1.pingcap.com/files/2023/12/20240101143151242.png)**事务 2**

在事务 1 重新建立索引后，在事务 2 执行 Pause 暂停操作，如下图会返回 successful 关键字。

![返回 successful 关键字](https://img1.pingcap.com/files/2023/12/20240101143231708.png)而且，查看 DDL 队列能够确认添加索引的任务已经被正常暂停，正处于 paused 的状态，如下图：

![查看 DDL 队列](https://img1.pingcap.com/files/2023/12/20240101143326893.png)然后等待了 3min ，也已经超过 1min54s ，该时间是前面验证得到的在没有执行 pause 时添加索引需要的实际时间。

观察 DDL 后台任务状态也没有运行，一直处于 paused 的状态。

此时观察集群的内存、IO、网络带宽消耗等指标，没有明显波动处于正常水平，说明此时 DDL 任务真的被暂停了。

在操作中，如果我们暂停的是一个已经完成的 DDL 任务，会看到类似 `[ddl:8224]DDL Job:112 not found` 的错误，这表示该任务 job-id = 112 已从 tikv 存储层中的 DDL 等待队列中被移除。

![任务移除](https://img1.pingcap.com/files/2023/12/20240101143410191.png)### 小结

关于暂停 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 标识，说明已经支持恢复后台暂停的作业。

![验证测试](https://img1.pingcap.com/files/2023/12/20240101143449986.png)在执行 resume 恢复命令后，再一次查看 DDL 作业的状态，如下图所示，会由 paused 转为 running ，证明已经正常恢复暂停的作业，且作业正在后台执行。

![查看 DDL 作业状态](https://img1.pingcap.com/files/2023/12/20240101143523963.png)等待一会后，resume 恢复的 DDL 任务正常执行完毕。如下图，在启动 add index 任务后我们暂停时等待了 3min 左右，加上加索引需要耗时约 1min52s，所以总的耗时在 5min 左右，符合预期。

![DDL 恢复](https://img1.pingcap.com/files/2023/12/20240101143600539.png)这个时间，也可以通过 `admin show ddl jobs` 后的输出内容进行二次确认，如下，结果同样符合预期。

![二次确认](https://img1.pingcap.com/files/2023/12/20240101143637272.png)只能对 paused 状态的的任务执行 resume，如果尝试恢复已经完成的 DDL 任务，会在 RESULT 列看到类似 `[ddl:6224] DDL Job:112 not found` 的错误，表示该任务已从 DDL 等待队列中被移除。

![任务移除](https://img1.pingcap.com/files/2023/12/20240101143706710.png)### 小结

- 处于非暂停状态中的作业无法被恢复，操作将失败。
- 只能恢复已被暂停的 DDL 作业。如果重复恢复同一个 DDL 作业，会报错。
- 可以同时恢复多个 DDL 作业。
- 版本升级时正在运行的 DDL 作业将被自动暂停，同时在升级过程中发起的 DDL 作业也将被自动暂停。升级结束后，所有已暂停的 DDL 作业将自动恢复执行。

总结
--

- v7.5.0 新版本明确支持升级时如果有正在执行的 DDL，会直接后台自动暂停并在升级后自动恢复，避免了由于管理员的操作遗漏检查，导致因为没有取消升级过程的 DDl 而出现的问题。
- 对一个数十亿行及以上的大表添加索引，可以根据业务运行周期，在业务高峰期暂停加索引的 DDL，避免消耗过多资源影响影响在线业务。在业务低谷期重新恢复该任务，可以达到提高集群系统的可用性和保证用户体验。
- v7.5.0 正式 GA 了 DDL 任务的暂停和恢复操作，结合 v6.5 版本后添加索引加速的功能特性，从根本解决了业务添加索引的痛点问题。
- 优化建议： 
  - 从易用性的角度来看，DDL 语句创建以后，建议直接提交异步任务到后台执行，前台返回成功，不用阻塞当前操作界面。如果用户想查看任务执行情况，可以通过 admin show ddl 命令查看进度。