大表(
千万级订单表
),在跑,里面关联很多业务,要新增字段,这时,你去加,所有业务在执行命令的那一秒全部卡死,导致全公司损失惨重,这还不得赔得底裤都不剩...
问题背景
在大数据量表(如万级订单表)上直接执行 ALTER TABLE
新增字段会导致锁表,阻塞读写请求,可能引发超时、连接池耗尽甚至应用崩溃。
🔐锁表
DDL通常会导致锁表。
啥叫锁表
定义:是指数据库
在执行某些操作时,限制其他会话
对表
的访问,目的是保证数据的一致性
和完整性
,是数据库并发控制
的基础机制
。
啥时候会导致锁表
- 执行
DDL
操作(如ALTER TABLE
添加列) - 执行大量数据修改(eg: 大量
UPDATE
) - 显式用
LOCK TABLE
语句 - 某些特定查询(eg:SELECT FOR UPDATE)
性能影响:
- 导致查询排队等待
- 可能引发连接池耗尽
- 极端情况下导致应用超时
eg:
sql
-- 会话1执行(会锁表)
ALTER TABLE orders ADD COLUMN discount DECIMAL(10,2);
-- 会话2尝试查询(会被阻塞)
SELECT * FROM orders WHERE user_id=100;
解决方案与操作代码
1. 主从架构处理
-
适用场景:主库写,从库读的MySQL主从复制环境。
-
步骤与代码 :
sql-- 1. 停止主从复制 STOP SLAVE; -- 2. 在从库执行新增字段(无锁表风险,因从库已停止同步) ALTER TABLE orders ADD COLUMN new_field VARCHAR(255); -- 3. 重启主从复制并检查延迟 START SLAVE; SHOW SLAVE STATUS\G -- 确认 'Seconds_Behind_Master' 归零 -- 4. 切换主从角色后重复操作主库
-
风险:需手动切换主从,操作复杂且短暂不可用。

2. 在线DDL工具(pt-online-schema-change)
-
工具安装 :
bashsudo apt-get install percona-toolkit # Debian/Ubuntu
-
执行命令 :
bashpt-online-schema-change \ --alter="ADD COLUMN new_field VARCHAR(255)" \ D=database,t=orders \ --execute
-
原理 :创建临时表(也叫
幽灵👻表
)同步数据,原子切换表名。 -
注意:需监控磁盘空间和数据库负载。
幽灵👻表:

主要 DDL 操作包括:
-
CREATE - 创建数据库对象(表、视图、索引等)
sqlCREATE TABLE users (id INT, name VARCHAR(50));
-
ALTER - 修改现有对象结构
sqlALTER TABLE users ADD COLUMN email VARCHAR(100);
-
DROP - 删除对象
sqlDROP TABLE users;
-
TRUNCATE - 清空表数据(保留结构)
sqlTRUNCATE TABLE logs;
-
RENAME - 重命名对象(部分数据库支持)
sqlRENAME TABLE old_name TO new_name;
以下三种方法与前两种不同,以此为河界:
3. 扩展新表(低风险推荐)
-
操作代码 :
sql-- 1. 创建新表存储扩展字段 CREATE TABLE orders_extension ( order_id BIGINT PRIMARY KEY, new_field VARCHAR(255), FOREIGN KEY (order_id) REFERENCES orders(id) ); -- 2. 应用层查询时联表(示例:Java MyBatis) @Select("SELECT o.*, e.new_field FROM orders o LEFT JOIN orders_extension e ON o.id = e.order_id WHERE o.id = #{id}") Order getOrderWithExtension(Long id);
-
优点:零锁表风险,适合高频变更需求。
4. 预留字段(提前设计)
-
建表示例 :
sqlCREATE TABLE orders ( id BIGINT PRIMARY KEY, reserved_json JSON, -- 存储未来动态字段 reserved_field1 VARCHAR(255), reserved_field2 INT );
-
使用示例 :
sqlUPDATE orders SET reserved_json = JSON_SET(reserved_json, '$.new_property', 'value') WHERE id = 1;
5. 业务优化(非数据库方案)
-
临时数据存Redis :
java// Spring Boot示例 @Cacheable(value = "order:extensions", key = "#orderId") public String getOrderExtension(Long orderId) { return "临时数据"; }
总结与选型建议
方案 | 风险 | 适用场景 | 推荐指数 |
---|---|---|---|
主从架构 | 高 | 已具备主从且允许停机维护 | ⭐⭐ |
在线DDL工具 | 中 | 专业DBA团队 | ⭐⭐⭐ |
扩展新表 | 低 | 高频字段变更 | ⭐⭐⭐⭐ |
预留字段 | 低 | 初期设计阶段 | ⭐⭐⭐ |
业务优化(非DB) | 最低 | 非核心数据或临时需求 | ⭐⭐⭐⭐ |
以上任何操作都需胆大心细,同时需熟练。