
# RDS for PostgreSQL秒级加字段DDL最佳实践
#### 背景
在传统PostgreSQL版本（11之前）中，执行ALTER TABLE ADD COLUMN操作时，数据库需要为新添加的列对表中每一行数据进行填充默认值或NULL。当表数据量较大时（如亿级记录），该操作会导致以下问题：
- 长时间表锁：整个DDL执行期间需要对表持有ACCESS EXCLUSIVE锁，阻塞所有读写操作。
- 大量WAL日志：逐行写入产生海量WAL日志，导致主备延迟增大。
- 长时间事务：事务持续时间过长可能引发事务ID回卷风险。
- 业务中断：对于核心业务表，长时间的DDL操作可能导致业务不可用。
RDS for PostgreSQL基于PostgreSQL 11及以上版本，支持秒级加字段（Instant ADD COLUMN）特性，可有效解决上述问题。
#### 秒级加字段DDL原理
PostgreSQL 11引入了Instant ADD COLUMN特性，其核心原理如下：
- 元数据变更：添加列时仅修改表的元数据（系统目录），不再逐行重写表数据。
- 延迟物化：新列的值在首次被读取时才进行计算和物化，而非在DDL执行时写入。
- 默认值优化：对于带有VOLATILE默认值的列，默认值表达式被存储在元数据中，查询时动态计算。
- NOT NULL支持：PostgreSQL 11同时支持带默认值的NOT NULL约束的秒级添加，通过约束检查优化实现。
该特性使得无论表数据量多大，ADD COLUMN操作的执行时间均可控制在毫秒级，真正实现秒级加字段。
#### 适用场景
- 大表加字段：表数据量在千万级以上，需要快速添加新列。
- 在线业务：生产环境不允许长时间锁表，需要最小化DDL对业务的影响。
- 可空列添加：添加允许NULL的列，无需指定默认值。
- 带默认值的NOT NULL列：PostgreSQL 11+支持带常量默认值的NOT NULL列秒级添加。
 
#### 功能限制
- 不支持添加列的同时创建索引（需分步操作）。
- 不支持添加带有VOLATILE函数默认值且同时指定NOT NULL的列（需分两步）。
- 如果表存在继承关系，子表不会自动继承新列的元数据变更。
- 添加列后首次全表扫描可能触发延迟物化，产生一定的IO开销。
- 该特性不适用于ADD COLUMN ... GENERATED ALWAYS AS（生成列）。
 
#### 常用操作
- 场景一：添加可空列（最快，毫秒级完成）
  ```
  ALTER TABLE orders ADD COLUMN remark TEXT;
  ```
  该操作仅修改系统目录，瞬间完成，对业务零影响。
  
 
- 场景二：添加带默认值的列
  ```
  ALTER TABLE orders ADD COLUMN status INT DEFAULT 0;
  ```
  PostgreSQL 11+中，该操作同样为秒级完成。默认值存储在元数据中，查询时动态返回。
  

- 场景三：添加带默认值的NOT NULL列
  ```
  ALTER TABLE orders ADD COLUMN is_deleted BOOLEAN NOT NULL DEFAULT FALSE;
  ```
  PostgreSQL 11+支持该操作秒级完成，无需重写表数据。
  

- 场景四：添加列并创建索引（分步操作） -- 第一步：秒级添加列
  ```
  ALTER TABLE orders ADD COLUMN user_tag TEXT;
  ```
  -- 第二步：在线创建索引（不阻塞读写）
  ```
  CREATE INDEX CONCURRENTLY idx_orders_user_tag ON orders(user_tag);
  ```
  注意：CREATE INDEX CONCURRENTLY不会持有排他锁，可在线执行。
  

- 场景五：VOLATILE默认值处理 -- 错误方式（不支持秒级）：
  ```
  -- ALTER TABLE orders ADD COLUMN create_time TIMESTAMPTZ NOT NULL DEFAULT NOW();
  ```
  -- 正确方式（分两步）：
  ```
  ALTER TABLE orders ADD COLUMN create_time TIMESTAMPTZ DEFAULT NULL;
  ALTER TABLE orders ALTER COLUMN create_time SET DEFAULT NOW();
  UPDATE orders SET create_time = NOW() WHERE create_time IS NULL;
  ```
  
#### 最佳实践建议
- 版本确认：确认RDS for PostgreSQL实例版本为11及以上，可通过**SELECT version();**查看。
- 提前规划：在业务低峰期执行DDL操作，即使是秒级操作也建议在低峰期执行。
- 分步操作：对于复杂变更（加字段+建索引+加约束），拆分为多个独立步骤，每步验证后再执行下一步。
- 延迟物化优化：添加列后，可通过VACUUM操作提前触发物化，避免首次查询时的性能抖动。
- 监控锁等待：执行DDL前可通过pg_locks视图监控当前锁状态，确保无长事务阻塞。
- 备份验证：重要表的DDL变更前，建议先在只读实例或测试环境验证。
- 避免频繁加字段：虽然秒级加字段性能优异，但频繁修改表结构仍会增加系统目录膨胀，建议合理规划表结构。
- 结合CONCURRENTLY：需要加索引时使用**CREATE INDEX CONCURRENTLY**，避免阻塞业务。
 
#### 常见问题
- **问题1：秒级加字段后，新列的数据何时写入磁盘？**
  新列数据采用延迟物化机制，在首次全表扫描或该列被访问时才逐行计算并写入。可通过VACUUM提前触发物化。
  
- **问题2：添加列后表文件大小是否会立即增大？**
  不会。Instant ADD COLUMN仅修改元数据，表文件大小不变。数据在延迟物化时才会占用额外空间。
  
- **问题3：秒级加字段是否支持所有数据类型？**
  支持所有标准数据类型。但对于带有复杂默认值表达式（如VOLATILE函数）的NOT NULL列，需分步操作。
  
- **问题4：添加列后主备同步是否会延迟？**
  由于Instant ADD COLUMN仅产生少量元数据变更的WAL日志，主备同步延迟极小，不会像传统方式那样产生大量WAL导致延迟。
  
- **问题5：如何确认当前实例是否支持秒级加字段？**
  执行SELECT version();确认PostgreSQL版本 \>= 11。RDS for PostgreSQL 11及以上版本均支持该特性。
  
- **问题6：秒级加字段操作是否需要特殊权限？**
  不需要特殊权限，具有ALTER权限的用户即可执行。与常规ALTER TABLE ADD COLUMN权限要求一致。
  
 
