# INSTANT DDL次数上限

| 版本 | 位宽 | 可表示范围 | INSTANT DDL 上限 | 每行额外开销 |
|------|------|-----------|-----------------|-------------|
| MySQL 8.0 | 6 bit | 0–63 | 64 次 | 实际占 1 字节 |
| MySQL 9.1.0 | 8 bit | 0–255 | 256 次 | 同样 1 字节 |


### 一、MySQL8 INSTANT DDL可用次数上限为什么不能超过63次

这个限制来自 InnoDB 物理行格式的存储设计。

#### 1. 根本原因：行版本号（Row Version）的位宽限制

MySQL 8.0 的 INSTANT DDL 能快速执行的核心原理是：**不重建整张表，只在数据字典里记录新列的元数据**，然后在每行物理记录里额外存一个很小的"行版本号"（Row Version），用来告诉引擎"这行数据是按哪一个版本的表结构写入的"。

读取行时，InnoDB 拿行里的版本号对比数据字典里每一列的"加入版本"，就知道这行有没有该列、默认值是什么，不需要真正改过历史数据。

#### 2. 为什么偏偏是 64

行版本号被设计为 **6 位整数**，可表示的范围是 `0–63`，共 **64 个不同的值**：

- 版本 `0` 表示"建表时的原始结构"
- 每做一次 INSTANT DDL，版本号 +1
- 版本号到 `63` 后，6 位已经用尽，再往上就溢出了

MySQL 选 6 位是在行头空间开销和可用次数之间的权衡——放在 extra bytes 里，尽量小，但又能支持合理次数的变更。

#### 3. 超出后怎么办

执行一次表重建，版本号归零：

```sql
-- 任选一种，都会触发全表重建并重置版本计数器
ALTER TABLE order_info FORCE;
-- 或
ALTER TABLE order_info ALGORITHM=INPLACE;
-- 或
OPTIMIZE TABLE order_info;
```

重建之后所有历史行都被改写成当前最新结构，版本号重置为 0，64 次 INSTANT DDL 的配额重新开始计算。

#### 小结

| 关键点 | 说明 |
|--------|------|
| 版本号位宽 | 6 bit → 最大值 63 → 64 种状态 |
| 每次 INSTANT DDL | 版本号 +1 |
| 达到上限后 | 不报错建议，直接拒绝 INSTANT，需手动重建 |
| 重置方式 | `ALTER TABLE ... FORCE` 或等价重建操作 |



### 二、MySQL9 INSTANT DDL可用次数上限为什么不能超过255次

同样是行版本号的位宽变化，不过这次的逻辑更直接。

#### 1. 从 6 位扩展到 8 位（一个完整字节）

MySQL 8.0 用 6 位存储行版本号，能表示 0–63 共 64 个值，0 是建表原始状态，所以 INSTANT DDL 上限是 63 次。

9.1.0 把行版本号扩展到 **8 位（1 个字节）**，可表示 0–255 共 256 个值，0 保留给原始结构，所以 INSTANT DDL 可用次数变成 **255 次**。

#### 2. 为什么偏偏是 256，而不是 128 或 512？

关键在于存储对齐。

6 位在内存里并不能单独存放——实际上还是要占用一个完整的字节边界空间。既然无论如何都要"用掉"一个字节，MySQL 9.1.0 干脆把版本号字段对齐到 **1 字节**，把 6 位扩展到 8 位，**零额外存储成本，版本号容量直接翻 4 倍**。

选 8 位而不是 16 位，是因为：
- 8 位已经足够绝大多数业务场景（正常表很少会 INSTANT DDL 超过 255 次而不做一次重建）
- 每行多存一个字节和多存两个字节，对大表的总存储影响差异可观
- 1 字节是最自然的字节对齐单位，实现最简洁

#### 小结

本质上是一次"既不花存储成本、又让上限翻倍"的改进——以前 6 位就已经占了一个字节的空间，那 2 位就别浪费了。