大表(
千万级订单表),在跑,里面关联很多业务,要新增字段,
这时,你去加,所有业务在执行命令的那一秒全部卡死,导致全公司损失惨重,这还不得赔得底裤都不剩...
问题背景
在大数据量表(如万级订单表)上直接执行 ALTER TABLE 新增字段会导致锁表,阻塞读写请求,可能引发超时、连接池耗尽甚至应用崩溃。
🔐锁表
DDL通常会导致锁表。
啥叫锁表
定义:是指数据库在执行某些操作时,限制其他会话对表的访问,目的是保证数据的一致性和完整性,是数据库并发控制的基础机制。
啥时候会导致锁表
- 执行
DDL操作(如ALTER TABLE添加列) - 执行大量数据修改(eg: 大量
UPDATE) - 显式用
LOCK TABLE语句 - 某些特定查询(eg:SELECT FOR UPDATE)
性能影响:
- 导致查询排队等待
- 可能引发连接池耗尽
- 极端情况下导致应用超时
eg:
-- 会话1执行(会锁表)
ALTER TABLE orders ADD COLUMN discount DECIMAL(10,2);
-- 会话2尝试查询(会被阻塞)
SELECT * FROM orders WHERE user_id=100;
解决方案与操作代码
1. 主从架构处理
- 适用场景:主库写,从库读的MySQL主从复制环境。
- 步骤与代码:
-- 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)
- 工具安装:
sudo apt-get install percona-toolkit # Debian/Ubuntu - 执行命令:
pt-online-schema-change \ --alter="ADD COLUMN new_field VARCHAR(255)" \ D=database,t=orders \ --execute - 原理:创建临时表(也叫
幽灵👻表)同步数据,原子切换表名。 - 注意:需监控磁盘空间和数据库负载。
幽灵👻表:
主要 DDL 操作包括:
-
CREATE - 创建数据库对象(表、视图、索引等)
CREATE TABLE users (id INT, name VARCHAR(50)); -
ALTER - 修改现有对象结构
ALTER TABLE users ADD COLUMN email VARCHAR(100); -
DROP - 删除对象
DROP TABLE users; -
TRUNCATE - 清空表数据(保留结构)
TRUNCATE TABLE logs; -
RENAME - 重命名对象(部分数据库支持)
RENAME TABLE old_name TO new_name;
以下三种方法与前两种不同,以此为河界:
3. 扩展新表(低风险推荐)
- 操作代码:
-- 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. 预留字段(提前设计)
- 建表示例:
CREATE TABLE orders ( id BIGINT PRIMARY KEY, reserved_json JSON, -- 存储未来动态字段 reserved_field1 VARCHAR(255), reserved_field2 INT ); - 使用示例:
UPDATE orders SET reserved_json = JSON_SET(reserved_json, '$.new_property', 'value') WHERE id = 1;
5. 业务优化(非数据库方案)
- 临时数据存Redis:
// Spring Boot示例 @Cacheable(value = "order:extensions", key = "#orderId") public String getOrderExtension(Long orderId) { return "临时数据"; }
总结与选型建议
| 方案 | 风险 | 适用场景 | 推荐指数 |
|---|---|---|---|
| 主从架构 | 高 | 已具备主从且允许停机维护 | ⭐⭐ |
| 在线DDL工具 | 中 | 专业DBA团队 | ⭐⭐⭐ |
| 扩展新表 | 低 | 高频字段变更 | ⭐⭐⭐⭐ |
| 预留字段 | 低 | 初期设计阶段 | ⭐⭐⭐ |
| 业务优化(非DB) | 最低 | 非核心数据或临时需求 | ⭐⭐⭐⭐ |
以上任何操作都需胆大心细,同时需熟练。