面对 1.35亿条数据 的 MySQL 表添加字段,传统 ALTER TABLE 可能导致长时间锁表,严重影响业务。本文将提供一套完整的 零停机方案,涵盖 Online DDL 优化、专业工具使用 和 Java 应用层配合策略。
ALTER TABLE `orders` ADD COLUMN `is_priority` TINYINT NULL DEFAULT 0;Too many connections)-- 查看表大小(GB)
SELECT
table_name,
ROUND(data_length/1024/1024/1024,2) AS size_gb
FROM information_schema.tables
WHERE table_schema = 'your_db' AND table_name = 'orders';
-- 检查当前长事务
SELECT * FROM information_schema.innodb_trx
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;方案 | 工具 | 执行时间 | 阻塞情况 | 适用版本 | 复杂度 |
|---|---|---|---|---|---|
Online DDL | 原生MySQL | 30min-2h | 短暂阻塞写 | 5.7+ | ★★☆ |
pt-osc | Percona Toolkit | 2-4h | 零阻塞 | 所有版本 | ★★★ |
gh-ost | GitHub | 1-3h | 零阻塞 | 所有版本 | ★★★★ |
ALTER TABLE `orders`
ADD COLUMN `is_priority` TINYINT NULL DEFAULT 0,
ALGORITHM=INPLACE,
LOCK=NONE;-- 查看 DDL 状态
SHOW PROCESSLIST;
-- 查看 InnoDB 操作进度
SELECT * FROM information_schema.innodb_alter_table;时间(min) = 表大小(GB) × 2 + 10# 安装 Percona Toolkit
sudo yum install percona-toolkit
# 执行变更(自动创建触发器)
pt-online-schema-change \
--alter "ADD COLUMN is_priority TINYINT NULL DEFAULT 0" \
D=your_db,t=orders \
--chunk-size=1000 \
--max-load="Threads_running=50" \
--critical-load="Threads_running=100" \
--execute参数 | 作用 | 推荐值(亿级表) |
|---|---|---|
--chunk-size | 每次复制的行数 | 500-2000 |
--max-load | 自动暂停阈值 | Threads_running=50 |
--critical-load | 强制中止阈值 | Threads_running=100 |
--sleep | 批次间隔时间 | 0.5(秒) |
// 在触发器生效期间,需处理重复主键异常
try {
orderDao.insert(newOrder);
} catch (DuplicateKeyException e) {
// 自动重试或走降级逻辑
orderDao.update(newOrder);
}gh-ost \
--database="your_db" \
--table="orders" \
--alter="ADD COLUMN is_priority TINYINT NULL DEFAULT 0" \
--assume-rbr \
--allow-on-master \
--cut-over=default \
--execute# 运行时控制
echo throttle | nc -U /tmp/gh-ost.sock
echo no-throttle | nc -U /tmp/gh-ost.sock// 在变更期间同时写入新旧字段
public void createOrder(Order order) {
order.setIsPriority(0); // 新字段默认值
orderMapper.insert(order);
// 兼容旧代码
if (order.getV2() == null) {
orderMapper.updateIsPriority(order.getId(), 0);
}
}<!-- MyBatis 动态字段映射 -->
<insert id="insertOrder">
INSERT INTO orders
(id, user_id, amount
<if test="isPriority != null">, is_priority</if>)
VALUES
(#{id}, #{userId}, #{amount}
<if test="isPriority != null">, #{isPriority}</if>)
</insert># 监控复制延迟(主从架构)
pt-heartbeat --monitor --database=your_db
# 查看 gh-ost 进度
tail -f gh-ost.log# pt-osc 回滚(自动清理临时表)
pt-online-schema-change --drop-new-table --alter="..." --execute
# gh-ost 回滚
gh-ost --panic-on-failure --revert首选方案:
ALGORITHM=INSTANT(秒级完成)gh-ost(无触发器影响)执行窗口:
验证流程:
-- 变更后检查数据一致性
SELECT COUNT(*) FROM orders WHERE is_priority IS NULL;后续优化:
-- 添加完成后可改为 NOT NULL
ALTER TABLE orders
MODIFY COLUMN is_priority TINYINT NOT NULL DEFAULT 0;通过合理选择工具+应用层适配,即使 1.35亿条数据 的表也能实现 零感知 的字段添加。