## 模块 4: 在线表结构变更

在线表结构变更在 SaaS 环境中尤为重要，因为 SaaS 需要保证不间断运行。在本模块中，您将执行 DDL 操作，向现有表添加新列以支持业务变更，同时在同一表上进行 DML 插入操作。你将观察到 DDL 操作不会被正在进行的 DML 阻塞，并且运行 DML 的 Java 应用程序被设计为能够感知 TiDB 的在线 DDL 功能。它被编程为能够无缝处理表结构变更。

### 步骤 1. 查看 sysbench 创建的表结构

+ 在 [终端 1](ssh://monitor1-ssh#1)  上，连接到 TiDB 服务器实例。

  ```bash
  mysql --prompt="mysql [\d] > " -h ${HOST_DB1_PRIVATE_IP} -P 4000 -u root
  ```

  ```output
  mysql [(none)] >
  ```

+ 删除所有行，并检查表 `sbtest1000.sbtest9` 的定义。表中有四个列，`id` 是主键。

  ```sql
  DELETE FROM sbtest1000.sbtest9;
  DESC sbtest1000.sbtest9;
  ```

  ```output
  +-------+-----------+------+------+---------+----------------+
  | Field | Type      | Null | Key  | Default | Extra          |
  +-------+-----------+------+------+---------+----------------+
  | id    | int       | NO   | PRI  | NULL    | auto_increment |
  | k     | int       | NO   | MUL  | 0       |                |
  | c     | char(120) | NO   |      |         |                |
  | pad   | char(60)  | NO   |      |         |                |
  +-------+-----------+------+------+---------+----------------+
  4 rows in set (0.00 sec)
  ```

### 步骤 2. 准备在线表结构变更

+ 关闭[元数据锁](https://docs.pingcap.com/tidb/stable/system-variable-reference/#tidb_enable_metadata_lock)，以便 DDL 语句不会被同一表上正在进行的 DML 事务阻塞。

  ```sql
  SET GLOBAL tidb_enable_metadata_lock = OFF;
  SHOW VARIABLES LIKE "tidb_enable_metadata_lock";
  EXIT
  ```

+ 查看提供的 Java 程序，它执行简单的插入操作，并且最重要的是，在遇到 `8028` 错误时，它能够重试事务。简而言之，它与 TiDB 的在线表结构变更行为兼容。错误代码 `8028`，消息为 Information schema is changed，当程序尝试提交事务但检测到与事务开始时不同的 schema 定义时发生。

  ```bash
  cat InsertJob.java | egrep -A1 -B1 8028
  ```

  ```output
          } catch (SQLException e) {
              if (e.getErrorCode() == 8028) {
                  System.out.println("Schema mutation encountered, retry ...");
  ```

### 步骤 3. 启动插入工作负载

+ 在 [终端 1](ssh://monitor1-ssh#1)  上，运行以下命令启动插入工作负载，它不会停止。保持它运行。继续下一步。

  ```bash
  rm -f InsertJob.class
  javac -cp .:mysql-connector-java-5.1.36-bin.jar InsertJob.java
  sleep 1
  java -cp .:mysql-connector-java-5.1.36-bin.jar InsertJob ${HOST_DB1_PRIVATE_IP} 4000 t1000
  ```

  ```output
  Inserting 0
  Inserting 1
  Inserting 2
  Inserting 3
  Inserting 4
  Inserting 5
  Inserting 6
  Inserting 7
  ...
  ```

### 步骤 4. 执行在线表结构变更

+ 根据某些业务需求，你决定向表 `sbtest1000.sbtest9` 添加新列，即 `ed VARCHAR(10) DEFAULT 'N/A'`。

+ 切换到 [终端 2](ssh://monitor1-ssh#2) ，连接到 TiDB 服务器实例。

  ```bash
  mysql --prompt="mysql [\d] > " -h ${HOST_DB1_PRIVATE_IP} -P 4000 -u root
  ```

  ```output
  mysql [(none)] >
  ```

+ 检查表两次，确认插入工作负载正在工作，行数在增加。

  ```sql
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  SELECT SLEEP(2);
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  ```

  ```output
  +----------+
  | COUNT(1) |
  +----------+
  |       14 |
  +----------+
  1 row in set (0.00 sec)

  +----------+
  | SLEEP(2) |
  +----------+
  |        0 |
  +----------+
  1 row in set (2.00 sec)

  +----------+
  | COUNT(1) |
  +----------+
  |       18 |
  +----------+
  1 row in set (0.00 sec)
  ```

+ 执行 DDL 向 `sbtest1000.sbtest9` 添加新列。

  ```sql
  ALTER TABLE sbtest1000.sbtest9 ADD COLUMN (ed VARCHAR(10) DEFAULT 'N/A');
  ```

  ```output
  Query OK, 0 rows affected (0.34 sec)
  ```

+ 切换回 [终端 1](ssh://monitor1-ssh#1) ，观察输出，我们期望看到 `Schema mutation encountered, retry ...` 的输出，并且工作负载没有被中断。

  ```output
  ...
  Inserting 50
  Inserting 51
  Schema mutation encountered, retry ...
  Inserting 52
  Inserting 53
  Inserting 54
  Inserting 55
  Inserting 56
  ...
  ```

+ 返回 [终端 2](ssh://monitor1-ssh#2) ，确认行数仍在增加。

  ```sql
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  SELECT SLEEP(2);
  SELECT COUNT(1) FROM sbtest1000.sbtest9;
  ```

  ```output
  +----------+
  | COUNT(1) |
  +----------+
  |       71 |
  +----------+
  1 row in set (0.01 sec)

  +----------+
  | SLEEP(2) |
  +----------+
  |        0 |
  +----------+
  1 row in set (2.00 sec)

  +----------+
  | COUNT(1) |
  +----------+
  |       74 |
  +----------+
  1 row in set (0.00 sec)
  ```

+ 验证表定义已更改。注意列 `ed varchar(10)` 是新列。

  ```sql
  DESC sbtest1000.sbtest9;
  ```

  ```output
  +-------+-------------+------+------+---------+----------------+
  | Field | Type        | Null | Key  | Default | Extra          |
  +-------+-------------+------+------+---------+----------------+
  | id    | int         | NO   | PRI  | NULL    | auto_increment |
  | k     | int         | NO   | MUL  | 0       |                |
  | c     | char(120)   | NO   |      |         |                |
  | pad   | char(60)    | NO   |      |         |                |
  | ed    | varchar(10) | YES  |      | N/A     |                |
  +-------+-------------+------+------+---------+----------------+
  5 rows in set (0.00 sec)
  ```

+ 此时，你已经见证了 TiDB 的在线 DDL 功能，它允许表结构变更而无需停止任何操作。转到 [终端 1](ssh://monitor1-ssh#1) ，按 <kbd>CTRL-C</kbd>。然后，退出两个终端。
