大表(
千万级订单表
),在跑,里面关联很多业务,要新增字段,
这时,你去加,所有业务在执行命令的那一秒全部卡死,导致全公司损失惨重,这还不得赔得底裤都不剩...
问题背景
在大数据量表(如万级订单表)上直接执行 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) | 最低 | 非核心数据或临时需求 | ⭐⭐⭐⭐ |
以上任何操作都需胆大心细,同时需熟练。