MySQL 5.5大表添加列遇Lock wait timeout,求更优方案及原因
MySQL大表ALTER锁超时问题:根源分析与更优解决方案
针对你在MySQL 5.5环境下给appln_doc表添加列时遇到的锁等待超时问题,咱们先理清根源,再分享几个比导出导回更安全高效的解决方案:
问题根源确认
没错,这个问题就是**表数据量大(30万条)+ 存储大尺寸文档(LOB类型)**共同导致的:
MySQL 5.5并不支持Online DDL(在线表结构变更),执行ALTER TABLE加列时,会对原表加排他锁,并且需要把整个表的所有数据复制到一个新的表结构中。当表中存了大量大对象(比如图片这类Blob数据)时,数据复制的过程会异常缓慢,期间只要有其他事务占用锁资源,就很容易触发Lock wait timeout exceeded错误。你提到子表appln_doc_details无数据,所以问题完全集中在这个主表的DDL操作上。
更优解决方案
1. 用Percona Toolkit的pt-online-schema-change工具(首推)
这是业内处理MySQL大表结构变更的常用工具,核心逻辑是不锁原表完成改表:
- 先创建一个和原表结构一致的新表,在新表上执行ALTER操作;
- 通过触发器把原表的增量数据实时同步到新表;
- 数据同步完成后,无缝替换原表。
命令示例(替换你的数据库名):
pt-online-schema-change --alter="ADD COLUMN is_signature tinyint(1) DEFAULT 0" D=your_db_name,t=appln_doc --execute
注意:执行前确保表有主键/唯一索引(工具依赖这个做数据同步),并且提前做好全量备份,防止意外。
2. 手动模拟在线改表流程
如果不想用第三方工具,可以手动复刻类似逻辑,步骤如下:
- 步骤1:创建带新列的空表
CREATE TABLE appln_doc_new LIKE appln_doc; ALTER TABLE appln_doc_new ADD COLUMN is_signature tinyint(1) DEFAULT 0; - 步骤2:分批迁移原表数据(避免一次性锁表)
假设表的主键是id,每次迁移1万条:INSERT INTO appln_doc_new SELECT *, 0 FROM appln_doc WHERE id BETWEEN 1 AND 10000; INSERT INTO appln_doc_new SELECT *, 0 FROM appln_doc WHERE id BETWEEN 10001 AND 20000; -- 重复直到所有数据迁移完成 - 步骤3:创建触发器同步增量数据
分别创建INSERT/UPDATE/DELETE触发器,确保迁移期间原表的新操作同步到新表:-- 示例INSERT触发器 DELIMITER // CREATE TRIGGER trg_appln_doc_insert AFTER INSERT ON appln_doc FOR EACH ROW BEGIN INSERT INTO appln_doc_new VALUES (NEW.*, 0); END // DELIMITER ; - 步骤4:切换表名完成替换
RENAME TABLE appln_doc TO appln_doc_old, appln_doc_new TO appln_doc; - 步骤5:验证数据无误后,删除旧表
DROP TABLE appln_doc_old;
3. 临时调整锁等待超时参数(应急方案,不推荐)
如果只是想临时尝试常规ALTER,可以调大锁等待超时时间:
SET GLOBAL innodb_lock_wait_timeout = 300; -- 单位:秒,默认是50
但这个方法只是延长等待时间,改表期间原表依然会被锁住,业务会受影响,而且对于大表+大对象的情况,依然可能失败,仅适合低峰期应急尝试。
对比你当前的方案
你用的导出Excel→截断表→导回数据的方法虽然可行,但存在明显缺点:
- 大尺寸文档导出到Excel效率极低,还容易出现格式/容量问题;
- 截断表后如果导回失败,数据风险极高(必须有完整备份才能兜底);
- 整个过程中表无法提供服务,业务中断时间长。
而上面的在线改表方案,能在几乎不影响业务的前提下完成结构变更,安全性和效率都高很多。
内容的提问来源于stack exchange,提问作者Arun Sudhakaran
相关产品推荐
相关产品推荐

