---
title: "体验在线 DDL - OceanBase 数据库 V5.0.1 | OceanBase 文档中心"
description: 体验在线 DDL 新增列、创建索引等表结构变更，是业务迭代中最常见的数据库变更操作。传统数据库执行此类 DDL 时可能需要长时间锁表，业务读写被迫中断，变更往往只能安排在业务低峰期进行。OceanBase 数据库的多数常见 DDL 为在线 DDL（Online DDL）：执行期间用户仍然可以对表进行读写操作。其中末尾…
image: https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*OSPzQ6GUQF4AAAAAQHAAAAgAeiGDAQ/original
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*P8CuR4UJ_FkAAAAAAAAAAAAADiGDAQ/original) OceanBase 数据库分布式版 - V 5.0.1

# 体验在线 DDL

更新时间：2026-10-27 15:56:46

[编辑](https://github.com/oceanbase/oceanbase-doc/edit/V5.0.1/zh-CN/200.quickstart/1000.experience-online-ddl.md)  

新增列、创建索引等表结构变更，是业务迭代中最常见的数据库变更操作。传统数据库执行此类 DDL 时可能需要长时间锁表，业务读写被迫中断，变更往往只能安排在业务低峰期进行。OceanBase 数据库的多数常见 DDL 为在线 DDL（Online DDL）：执行期间用户仍然可以对表进行读写操作。其中末尾加列等操作只需修改元数据；创建索引虽然需要重整数据、耗时与数据量有关，但执行期间业务读写可以继续。

本文将在一张具有持续读写业务的 100 万行测试表上执行新增列、创建索引等常见 DDL，观察 DDL 执行期间业务读写是否可继续，并验证变更后的表结构和查询结果。

本文将引导您完成以下体验：

- **准备持续读写的测试表**：创建测试租户，导入 100 万行样例数据，并用 Shell 循环模拟持续读写业务。
 - **体验在线加列**：在业务持续读写期间执行末尾加列，验证 DDL 期间业务不中断。
 - **体验在线创建索引**：在业务持续读写期间创建索引，观察数据重整期间业务可继续。
 - **验证在线性与变更结果**：通过 `table_id` 前后对比确认 DDL 为在线执行，并验证变更后的表结构与查询结果。

#### 说明

本文基于 OceanBase 数据库 V5.0.1（社区版）撰写，文中相关命令和功能说明均以 V5.0.1 版本为准。本文以 [部署 OceanBase](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006615008) 中使用 All in One 安装包部署的单节点环境为例，全部操作均可在单节点环境中完成。

## 前提条件

在开始本文操作前，确保满足以下条件：

| 项目 | 描述 |
| --- | --- |
| OceanBase 数据库 | 已使用 All in One 安装包完成部署并成功启动（部署名为 `demo`），参见 [部署 OceanBase](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006615008)。 |
| 机器资源 | 建议不低于 4 核 CPU、16 GB 内存，磁盘剩余空间不低于 5 GB。 |
| obclient | 已安装 obclient 并可正常连接 OceanBase 数据库。使用 All in One 安装包部署时已自动安装。 |
| sys 租户访问 | 可使用 root 用户登录集群的 sys 租户执行管理操作。sys 租户 root 用户的密码为部署时随机生成的密码，可在部署输出或 `obd cluster display demo` 命令的输出中查看。 |
| 多租户基础 | 本文会创建一个体验租户，租户与资源池的概念参见 [体验多租户与资源隔离](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000007140973)。 |

## 在线 DDL 简介

OceanBase 数据库将 DDL 操作分为在线与离线两类：

- **在线 DDL（Online DDL）**：执行 DDL 操作时，用户仍然可以访问数据库，包括读取和写入操作。对业务的影响小，支持并发访问和更少的锁定，能够减少停机时间。
 - **离线 DDL（Offline DDL）**：执行 DDL 操作时会锁定相关表，导致用户无法访问数据库的相关数据。执行期间会造成一定的停机时间，影响正常的查询和事务处理。

常见 DDL 操作的在线情况如下（摘自官方 MySQL 模式支持列表）：

| 操作 | 在线情况 | 耗时特点 |
| --- | --- | --- |
| 增加索引（`CREATE INDEX` / `ALTER TABLE ... ADD INDEX`） | 在线 | 与数据量有关，需要重整数据 |
| 末尾加列（`ALTER TABLE ... ADD COLUMN`，不指定位置） | 在线 | 只需要修改元数据 |
| 设列默认值、重命名表、重命名索引 | 在线 | 只需要修改元数据 |
| 修改列类型 | 离线 | 与数据量有关，需要重整数据 |
| 添加自增列 | 离线 | 与数据量有关，需要重整数据 |
| 添加/删除主键 | 离线 | 与数据量有关，需要重整数据 |

**离线 DDL 的原理**：离线 DDL 采用"重建表"的方式，新建一张对用户不可见的临时隐藏表，将原表数据迁移到新表后重命名并删除旧表。因此离线 DDL 完成后表的 `table_id` 会发生变化，且执行期间不允许 DML 操作。

**在线性判断方法**：在 DDL 操作前后查询 `table_id`，如果前后一致，说明该 DDL 为在线操作；如果发生变化，则为离线操作：

```sql
SELECT DISTINCT(table_id) FROM oceanbase.DBA_OB_TABLE_LOCATIONS WHERE table_name = 'xxx';

```

#### 说明

完整的在线/离线 DDL 操作列表及判断步骤请参见 [Online DDL 和 Offline DDL 操作](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006619473)。

## 创建测试租户

1. 使用 root 用户登录集群的 sys 租户（密码为部署时随机生成的密码，获取方式见上文前提条件）：

   ```shell
   obclient -h127.0.0.1 -uroot@sys -P2881 -p -A oceanbase

   ```
 2. 创建一个 2C4G 规格的资源单元、资源池和租户 `ddl_tenant`：

   ```sql
   obclient [oceanbase]> CREATE RESOURCE UNIT ddl_unit MAX_CPU=2, MIN_CPU=2, MEMORY_SIZE='4G';
   obclient [oceanbase]> CREATE RESOURCE POOL ddl_pool UNIT='ddl_unit', UNIT_NUM=1, ZONE_LIST=('zone1');
   obclient [oceanbase]> CREATE TENANT IF NOT EXISTS ddl_tenant CHARSET='utf8mb4', PRIMARY_ZONE='zone1', RESOURCE_POOL_LIST=('ddl_pool') SET ob_tcp_invited_nodes='%';

   ```

   租户创建需要数十秒（本文环境实测约 6 秒），期间命令行没有输出，属正常现象，等待命令返回即可。
 3. 登录 `ddl_tenant`。新创建的租户 root 用户默认密码为空，提示输入密码时直接回车即可，后续操作均在此租户中进行：

   ```shell
   obclient -h127.0.0.1 -uroot@ddl_tenant -P2881 -p -A test

   ```

## 准备测试表与样例数据

1. 在 `ddl_tenant` 的会话中创建测试表 `ddl_tbl`：

   ```sql
   obclient [test]> CREATE TABLE ddl_tbl (
       ->     id BIGINT PRIMARY KEY,
       ->     user_name VARCHAR(32),
       ->     city VARCHAR(32),
       ->     amount INT
       -> );

   ```
 2. 在部署 OceanBase 的机器上，执行以下命令生成 100 万行测试数据：

   ```shell
   seq 1 1000000 | awk '{printf "%d,user_%06d,city_%d,%d\n", $1, $1, $1%20, $1%1000}' > /tmp/ddl_sample.csv

   ```

   该命令生成包含以下字段的 CSV 文件：`id`（递增编号）、`user_name`（用户名）、`city`（20 个城市值循环取值）、`amount`（金额）。
 3. 检查数据文件：

   ```shell
   head -3 /tmp/ddl_sample.csv
   wc -l /tmp/ddl_sample.csv

   ```

   `head` 命令返回的前 3 行数据如下：

   ```shell
   1,user_000001,city_1,1
   2,user_000002,city_2,2
   3,user_000003,city_3,3

   ```

   `wc -l` 返回 `1000000` 表示数据行数正确。
 4. 在操作系统终端中执行以下命令，将数据导入 `ddl_tbl`。连接时通过 `--local-infile` 参数启用客户端本地文件导入，无需在服务端配置文件访问路径：

   ```shell
   obclient -h127.0.0.1 -uroot@ddl_tenant -P2881 --local-infile -A test -e "SET ob_query_timeout=3600000000; LOAD DATA LOCAL INFILE '/tmp/ddl_sample.csv' INTO TABLE ddl_tbl FIELDS TERMINATED BY ',';"

   ```
 5. 回到 obclient 会话，校验导入行数：

   ```sql
   obclient [test]> SELECT COUNT(*) FROM ddl_tbl;

   ```

   返回结果为 `1000000`，与源数据文件行数一致，表示数据完整入库：

   ```shell
   +----------+
   | COUNT(*) |
   +----------+
   |  1000000 |
   +----------+
   1 row in set

   ```

#### 说明

- `LOAD DATA LOCAL INFILE` 从客户端本地读取数据文件，与 [体验并行导入与高压缩](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000007140976) 中使用的服务端 `LOAD DATA INFILE`（需配置 `secure_file_priv`）不同，详细信息请参见 [使用 LOAD DATA 语句导入数据](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006627453)。
 - 命令中的 `SET ob_query_timeout=3600000000` 用于将本会话的语句超时时间临时调整为 1 小时，避免大批量导入耗时超过语句默认超时时间（10 秒）导致执行失败。
 - 新建租户的 root 用户默认密码为空，以上命令可直接执行；如已设置密码，请在命令中追加 `-p密码`（如 `-p'******'`）。

## 模拟持续读写业务

后续 DDL 体验需要一个对测试表持续读写的业务负载。本文使用 Shell 循环模拟：每秒向 `ddl_tbl` 写入一行新数据（INSERT），并按主键查询一次刚写入的数据（SELECT）。

1. 新开一个操作系统终端（下称**终端 A**），执行以下命令启动业务模拟循环：

   ```shell
   i=1000001
   while true; do
       obclient -h127.0.0.1 -uroot@ddl_tenant -P2881 -A test -e \
         "INSERT INTO ddl_tbl(id, user_name, city, amount) VALUES($i, 'user_live', 'city_live', 500); SELECT id, user_name, amount FROM ddl_tbl WHERE id = $i;" \
         > /dev/null 2>&1 \
         && echo "$(date '+%H:%M:%S') 读写成功 id=$i" \
         || echo "$(date '+%H:%M:%S') 读写失败 id=$i"
       i=$((i+1))
       sleep 1
   done

   ```

   命令说明：

      - `obclient -e`：执行语句并退出。循环中的每一轮通过一条 INSERT 完成写入、一条主键点查完成读取，语句均执行成功时输出"读写成功"，任一语句失败时输出"读写失败"。
      - 查询结果被重定向到 `/dev/null`，仅通过命令退出码判断读写是否成功，输出更简洁。
      - 循环从 `id=1000001` 开始写入，与已导入的 100 万行数据不冲突。
      - 需要停止业务模拟时，按 Ctrl+C 结束循环。

   循环启动后，终端 A 每秒输出一行结果（时间与编号以实际输出为准）：

   ```shell
   10:30:01 读写成功 id=1000001
   10:30:02 读写成功 id=1000002
   10:30:03 读写成功 id=1000003

   ```
 2. 保持终端 A 的循环持续运行，另开一个终端（下称**终端 B**）登录 `ddl_tenant`，后续 DDL 均在终端 B 的 obclient 会话中执行：

   ```

#### 说明

`obclient -e`、`--local-infile` 等命令行选项的详细说明请参见 [OBClient 命令行选项参考](https://www.oceanbase.com/docs/common-obclient-doc-cn-1000000006361909)。

## 体验在线加列

本节在终端 A 业务循环持续运行期间，为 `ddl_tbl` 执行末尾加列，验证 DDL 执行期间业务读写不中断。

### 操作步骤

1. 在终端 B 的会话中，查询并记录 DDL 执行前的 `table_id`：

   ```sql
   obclient [test]> SELECT DISTINCT(table_id) FROM oceanbase.DBA_OB_TABLE_LOCATIONS WHERE table_name = 'ddl_tbl';

   ```

   返回结果示例如下（`table_id` 为系统自动分配的对象编号，请记录实际返回值，本文以 `500002` 为例）：

   ```shell
   +----------+
   | table_id |
   +----------+
   |   500002 |
   +----------+
   1 row in set

   ```
 2. 确认终端 A 的读写循环持续运行中，在终端 B 的会话中执行末尾加列：

   ```sql
   obclient [test]> ALTER TABLE ddl_tbl ADD COLUMN remark VARCHAR(64) DEFAULT NULL;
   Query OK, 0 rows affected

   ```
 3. 观察 DDL 执行期间终端 A 的输出：从开始执行加列到命令返回的全过程中，读写循环持续输出"读写成功"，没有任何一轮出现"读写失败"，表明业务读写未中断。
 4. 在终端 B 的会话中再次查询 `table_id`，与第 1 步记录的值对比：

   ```

   返回结果仍为 `500002`（以实际记录值为准），与 DDL 执行前一致，说明数据表没有被重建，本次加列为在线执行。
 5. 查看变更后的表结构：

   ```sql
   obclient [test]> DESCRIBE ddl_tbl;

   ```

   返回结果如下，新增列 `remark` 已出现在表结构末尾：

   ```shell
   +-----------+-------------+------+------+---------+-------+
   | Field     | Type        | Null | Key  | Default | Extra |
   +-----------+-------------+------+------+---------+-------+
   | id        | bigint(20)  | NO   | PRI  | NULL    |       |
   | user_name | varchar(32) | YES  |      | NULL    |       |
   | city      | varchar(32) | YES  |      | NULL    |       |
   | amount    | int(11)     | YES  |      | NULL    |       |
   | remark    | varchar(64) | YES  |      | NULL    |       |
   +-----------+-------------+------+------+---------+-------+
   5 rows in set

   ```
 6. 查询存量数据的新列取值。末尾加列只修改元数据，存量行读取时按列默认值返回：

   ```sql
   obclient [test]> SELECT id, remark FROM ddl_tbl WHERE id = 500000;

   ```

   ```shell
   +--------+--------+
   | id     | remark |
   +--------+--------+
   | 500000 | NULL   |
   +--------+--------+
   1 row in set

   ```
 7. 验证新列对新的写入立即生效：向 `remark` 列写入数据并查询：

   ```sql
   obclient [test]> INSERT INTO ddl_tbl(id, user_name, city, amount, remark) VALUES(2000001, 'user_check', 'city_1', 100, 'online ddl works');
   obclient [test]> SELECT id, user_name, remark FROM ddl_tbl WHERE id = 2000001;

   ```

   ```shell
   +---------+------------+------------------+
   | id      | user_name  | remark           |
   +---------+------------+------------------+
   | 2000001 | user_check | online ddl works |
   +---------+------------+------------------+
   1 row in set

   ```

### 结果解读

| 观察项 | 预期现象 | 说明 |
| --- | --- | --- |
| **业务读写** | DDL 执行期间读写循环持续输出"读写成功" | 末尾加列为在线 DDL，只修改元数据，不重整数据、不阻塞业务 |
| **table_id** | DDL 前后不变 | 数据表没有被重建，符合官方在线性判断方法 |
| **表结构** | `remark` 列出现在表结构末尾 | 变更结果通过 `DESCRIBE` 验证 |
| **存量数据** | `remark` 均为默认值 `NULL` | 元数据级变更，存量行读取时按默认值返回 |
| **新写入** | 可为 `remark` 指定任意值 | 变更对新数据立即生效 |

#### 注意

- 本文演示的加列未指定列位置（默认追加到表末尾），为在线 DDL。带 `AFTER`/`BEFORE`/`FIRST` 的指定位置加列在不同版本中的在线情况不同，执行前请在 [Online DDL 和 Offline DDL 操作](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006619473) 中确认。
 - 业务 SQL 通过显式指定列名（`INSERT INTO ddl_tbl(id, user_name, city, amount) ...`）写入数据，加列后无需修改业务语句即可继续执行；使用 `INSERT INTO ddl_tbl VALUES(...)` 全列形式的语句在表列数变化后会执行失败。

## 体验在线创建索引

本节继续在业务循环运行期间，为 `ddl_tbl` 的 `city` 列创建索引。与末尾加列不同，创建索引需要重整数据、耗时与数据量有关，是观察"数据重整期间业务是否可继续"的典型场景。

### 操作步骤

1. 确认终端 A 的读写循环持续运行中，在终端 B 的会话中执行以下语句，为 `city` 列创建索引：

   ```sql
   obclient [test]> CREATE INDEX idx_ddl_city ON ddl_tbl(city);
   Query OK, 0 rows affected

   ```
 2. 观察 DDL 执行期间终端 A 的输出：索引创建需要将 100 万行存量数据重整为索引数据，耗时与数据量有关；从开始创建到命令返回期间，读写循环持续输出"读写成功"，业务读写未中断。

   如需再次确认在线性，可参照上一节的方法查询 `table_id`：创建索引前后，数据表的 `table_id` 保持不变。
 3. 查看变更后的表定义。在语句末尾使用 `\G` 可将查询结果按行垂直显示，便于阅读较长的建表语句：

   ```sql
   obclient [test]> SHOW CREATE TABLE ddl_tbl\G

   ```

   返回结果如下（建表语句中的表属性与集群配置相关，请以实际结果为准，重点关注新增的 `remark` 列与 `idx_ddl_city` 索引）：

   ```shell
   *************************** 1. row ***************************
          Table: ddl_tbl
   Create Table: CREATE TABLE `ddl_tbl` (
     `id` bigint(20) NOT NULL,
     `user_name` varchar(32) DEFAULT NULL,
     `city` varchar(32) DEFAULT NULL,
     `amount` int(11) DEFAULT NULL,
     `remark` varchar(64) DEFAULT NULL,
     PRIMARY KEY (`id`),
     KEY `idx_ddl_city` (`city`) BLOCK_SIZE 16384 LOCAL
   ) ORGANIZATION INDEX DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 1 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE ENABLE_MACRO_BLOCK_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
   1 row in set

   ```
 4. 验证查询结果。统计 `city` 列某个取值的行数：

   ```sql
   obclient [test]> SELECT COUNT(*) FROM ddl_tbl WHERE city = 'city_5';

   ```

   返回结果为 `50000`，与源数据分布一致（100 万行数据按 20 个城市值均匀分布，每个城市值 50000 行）：

   ```shell
   +----------+
   | COUNT(*) |
   +----------+
   |    50000 |
   +----------+
   1 row in set

   ```
 5. 在终端 A 按 Ctrl+C 停止业务模拟循环，回到终端 B 的会话统计循环期间写入的行数：

   ```sql
   obclient [test]> SELECT COUNT(*) FROM ddl_tbl WHERE id > 1000000;

   ```

   返回的行数即业务模拟循环写入的总行数与前文验证写入的 `id=2000001` 一行之和，与循环运行时长相关（本文实测循环运行约 100 秒写入 100 行，加上验证行共 101 行）。这些行在加列与创建索引两次 DDL 执行期间持续写入成功，进一步印证了 DDL 期间业务写入未中断。

### 结果解读

| 观察项 | 预期现象 | 说明 |
| --- | --- | --- |
| **业务读写** | 索引创建期间读写循环持续输出"读写成功" | 增加索引为在线 DDL，重整数据的过程不阻塞业务 DML |
| **DDL 耗时** | 与数据量有关 | 索引创建需要将存量数据重整为索引数据 |
| **索引结构** | 建表语句中出现 `KEY idx_ddl_city (city)` | 变更结果通过 `SHOW CREATE TABLE` 验证 |
| **查询结果** | `city_5` 行数为 50000 | 与导入数据的分布规律一致，数据重整未影响数据正确性 |
| **持续写入** | `id > 1000000` 的行数与循环时长相符 | 两次 DDL 期间业务写入持续成功 |

#### 注意

- 索引名称在数据库（Database）范围内不能重复。
 - 大数据量表创建索引的耗时与数据量有关，可在 `CREATE INDEX` 语句中通过 `parallel(N)` Hint 指定并行度加速索引创建（如 `CREATE /*+ parallel(4) */ INDEX ...`），详细信息请参见 [CREATE INDEX](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006619369)。
 - 本文仅演示普通索引的在线创建，唯一索引、函数索引、全文索引等更多索引类型的创建方法请参见 [创建索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006616348)。

## 下一步

完成本文的在线 DDL 体验后，您可以继续以下探索之旅：

1. **回顾快速上手系列**：至此，您已完成从部署、高并发 OLTP、HTAP、AI 场景、多租户、高可用、水平扩展、数据效率、数据保护到在线变更的完整体验，可从 [部署 OceanBase](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006615008) 开始回顾。
 2. **深入学习 DDL 管理**：完整的在线/离线 DDL 操作列表与在线性判断方法请参见 [Online DDL 和 Offline DDL 操作](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006619473)。
 3. **深入学习索引管理**：索引的查看与删除等操作请参见 [查看索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006616350)。

## 相关文档

- 在线/离线 DDL 操作的完整列表与在线性判断方法请参见 [Online DDL 和 Offline DDL 操作](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006619473)。
 - 增加列、修改列、删除列等表结构变更操作请参见 [更改表](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006616384)。
 - 各类索引的创建方法请参见 [创建索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006616348)。
 - `CREATE INDEX` 语句的完整语法请参见 [CREATE INDEX](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006619369)。
 - `ALTER TABLE` 语句的完整语法请参见 [ALTER TABLE](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006619416)。
 - 索引的查看方法请参见 [查看索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006616350)。
 - `LOAD DATA LOCAL INFILE` 的使用说明请参见 [使用 LOAD DATA 语句导入数据](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006627453)。
 - `DBA_OB_TABLE_LOCATIONS` 视图的字段说明请参见 [DBA_OB_TABLE_LOCATIONS](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000006617907)。
 - `obclient` 命令行选项的详细说明请参见 [OBClient 命令行选项参考](https://www.oceanbase.com/docs/common-obclient-doc-cn-1000000006361909)。

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
