深入分析mysql给表加字段涉及到的锁

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


一、元数据锁(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表添加普通字段(如VARCHARINT),且不涉及索引重建。

  • 锁行为

    • 准备阶段:短暂排他MDL锁 + WRITE锁(创建临时表)。
    • 执行阶段:共享MDL锁,允许并发DML。
    • 提交阶段:短暂排他MDL锁 + WRITE锁(表替换)。
  • 示例

    sql 复制代码
    ALTER 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=INPLACELOCK=NONE

    sql 复制代码
    ALTER 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、避免长事务干扰,并借助工具降低锁冲突,可显著减少加字段操作对业务的影响。

相关推荐
xiaolingting几秒前
设计模式在 JDK 中的具体应用与分析
java·单例模式·设计模式·代理模式·享元模式·中介模式·jdk设计模式应用
Microsoft Word3 分钟前
代码块与设计模式
java·算法·设计模式
wenbin_java3 分钟前
设计模式之适配器模式:原理、实现与应用
java·设计模式·适配器模式
码农的天塌了5 分钟前
java设计模式之适配器模式
java·设计模式·适配器模式
大G哥6 分钟前
Java 设计模式:观察者模式
java·开发语言·观察者模式·设计模式
37手游后端团队9 分钟前
聊聊提示词注入攻击那些事
人工智能·后端·程序员
Asthenia04129 分钟前
从实习生“777惨案”聊起:上生产环境部署 Spring Boot Docker 的正确姿势
后端
得物技术13 分钟前
得物 Android Crash 治理实践
android·后端
uhakadotcom14 分钟前
阿里云函数计算:从本地开发到云端部署的完整指南
后端·面试·github
武帝为此20 分钟前
【Maven教程与实战案例】
java·maven