知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
一、元数据锁(Metadata Lock, MDL)
1. MDL的作用
- 保护表结构一致性:防止在表结构变更过程中,其他会话修改表定义或执行可能导致元数据冲突的操作(如DROP TABLE、ALTER TABLE等)。
- 控制并发:确保在DDL操作期间,DML操作(SELECT/INSERT/UPDATE/DELETE)与DDL操作之间的隔离性。
2. MDL的锁类型与冲突
- 共享锁(SHARED_READ/SHARED_WRITE) :
DML操作(SELECT/INSERT等)会隐式获取共享MDL锁,允许多个会话并发执行。 - 排他锁(EXCLUSIVE) :
DDL操作(ALTER TABLE等)需获取排他MDL锁,阻塞其他所有MDL请求,直到DDL完成。
3. 加字段过程中的MDL行为
- MySQL 5.5及之前 :
- 整个ALTER TABLE过程持有排他MDL锁,阻塞所有DML操作。
- 导致业务完全不可写/读,直到DDL完成。
- MySQL 5.6+(Online DDL) :
- 阶段1(准备阶段):短暂获取排他MDL锁,检查表结构并初始化操作。
- 阶段2(执行阶段):降级为共享MDL锁,允许并发DML操作。
- 阶段3(提交阶段):再次短暂获取排他MDL锁,替换表文件并提交变更。
- 总阻塞时间:主要集中在准备和提交阶段,通常为毫秒级。
二、表级锁(LOCK)
1. 表级锁的类型
- READ锁:允许其他会话读,阻塞写。
- WRITE锁:阻塞其他会话的所有读写操作。
2. 加字段时的表级锁行为
- 传统COPY算法 (非Online DDL):
- 创建临时表并复制数据时,需持有WRITE锁,阻塞所有DML。
- 示例:未指定
ALGORITHM=INPLACE
时,MyISAM表的ALTER操作。
- Online DDL(INPLACE算法) :
- 仅在准备和提交阶段短暂获取WRITE锁,执行阶段允许并发DML。
- 示例:InnoDB表添加字段(非全文索引、非空间索引等场景)。
三、不同场景下的锁行为
1. 支持Online DDL的ADD COLUMN
-
场景 :InnoDB表添加普通字段(如
VARCHAR
、INT
),且不涉及索引重建。 -
锁行为 :
- 准备阶段:短暂排他MDL锁 + WRITE锁(创建临时表)。
- 执行阶段:共享MDL锁,允许并发DML。
- 提交阶段:短暂排他MDL锁 + WRITE锁(表替换)。
-
示例 :
sqlALTER TABLE users ADD COLUMN phone VARCHAR(20) DEFAULT NULL, ALGORITHM=INPLACE, LOCK=NONE;
2. 不支持Online DDL的ADD COLUMN
- 场景:添加字段涉及存储格式变更(如修改列顺序)、添加全文索引、空间索引。
- 锁行为 :
- 全程排他MDL锁 + WRITE锁,阻塞所有DML。
- 示例:MyISAM表添加字段,或InnoDB表添加
FULLTEXT
索引。
3. 长事务阻塞MDL
- 场景:存在未提交的长事务(如未提交的SELECT或事务型DML)。
- 问题:DDL操作在准备阶段需等待长事务释放共享MDL锁,导致后续所有MDL请求排队。
- 现象 :
SHOW PROCESSLIST
显示Waiting for table metadata lock
。 - 解决:终止长事务或调整DDL执行窗口。
四、锁冲突的监控与诊断
1. 监控MDL锁
sql
-- 查看当前MDL锁等待
SELECT * FROM performance_schema.metadata_locks
WHERE OWNER_THREAD_ID != sys.ps_thread_id(0);
2. 查看阻塞会话
sql
-- 显示等待MDL锁的会话
SELECT
p.id AS blocked_pid,
p.user AS blocked_user,
p.host AS blocked_host,
b.id AS blocking_pid,
b.user AS blocking_user,
b.host AS blocking_host
FROM information_schema.innodb_lock_waits w
JOIN information_schema.processlist p ON w.requesting_pid = p.id
JOIN information_schema.processlist b ON w.blocking_pid = b.id;
五、优化建议
1. 优先使用Online DDL
-
指定
ALGORITHM=INPLACE
和LOCK=NONE
:sqlALTER TABLE users ADD COLUMN phone VARCHAR(20), ALGORITHM=INPLACE, LOCK=NONE;
2. 避免高并发时执行DDL
- 选择业务低峰期操作,减少锁冲突概率。
3. 监控并清理长事务
sql
-- 查找运行时间超过60秒的事务
SELECT * FROM information_schema.innodb_trx
WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;
4. 使用第三方工具
- gh-ost:无触发器、基于binlog的在线表结构变更工具,完全避免锁表。
- pt-online-schema-change:通过创建影子表同步数据,仅在切换时短暂锁表。
六、总结
场景 | 锁类型 | 阻塞范围 | 优化方案 |
---|---|---|---|
Online DDL(INPLACE) | 短暂排他MDL + WRITE锁 | 仅准备和提交阶段 | 指定ALGORITHM=INPLACE |
COPY算法(非Online) | 全程排他MDL + WRITE锁 | 所有DML操作 | 使用第三方工具(如gh-ost) |
长事务阻塞 | MDL共享锁未释放 | DDL及后续查询排队 | 监控并终止长事务 |
通过合理选择Online DDL、避免长事务干扰,并借助工具降低锁冲突,可显著减少加字段操作对业务的影响。