MySQL主从延迟飙升?元数据锁可能是“真凶”

现象:IO线程欢快,SQL线程却在划水看到从库的SecondsBehindMaster从30秒直线飙升到1000秒,你是不是冷汗直冒?

IO线程还在欢快地读取日志,SQL线程却像被冻住了一样,一步一步往后挪,而且越来越慢。

这真的不是网络问题,而是你的DDL操作在暗地里搞破坏。我见过太多线上事故的根源就藏在一条看似不起眼的ALTERTABLE里------有个哥们改了一个varchar(5000)字段,想扩容到varchar(6000),结果整个从库崩溃了。

今天就给你讲清楚这个坑到底怎么踩,以及怎么安稳地跨过去。问题最初看起来很诡异。你用SHOWREPLICASTATUS一看,SecondsBehindMaster直接冲到1000+秒,业务告诉你数据没同步,但是你登上从库查看:

SecondsBehindMaster:1000

IOThreadRunning:Yes

SQLThreadRunning:Yes

RelayLogIORunning:Yes

这说明读日志的线程活得好好的,问题一定出在应用日志的线程上。
IO线程正常工作,说明主库没问题,网络也没问题。

那为什么SQL线程这么慢呢?多半是被什么东西卡住了。定位:SHOWREPLICASTATUS里的假象这里最轻易出现的差错就是看错位置。你或许会看到SQLDelay:0

RelayLogPos:1024看起来SQLDelay是0,说明没有故意延迟复制,RelayLogPos也在那儿......可这TM一个小时都没动过!
这就是关键信号:中继日志位置不再增长,说明SQL线程卡住了。

这时候,很多人会傻愣愣地查看网络、检查磁盘空间、观察缓冲池,完全没察觉到,问题就在这时出现了:某张表已经被锁定,SQL线程正在依次排队等着解锁抓锁:performanceschema里的真凶打开performanceschema,看看有没有元数据锁:

ini 复制代码
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db'   AND OBJECT_NAME = 'your_table'   AND LOCK_TYPE = 'EXCLUSIVE';

如果这里蹦出一条记录,LOCKTYPE是EXCLUSIVE,状态是WAITINGFOR,那你就找到真凶了。 大概率是:

字段名
OBJECTSCHEMA yourdb
OBJECTNAME yourtable
LOCKTYPE EXCLUSIVE
LOCKDURATION TRANSACTION
STATUS WAITING FOR TABLE METADATA LOCK
OBJECTTYPE TABLE

这说明有个ALTERTABLE操作正在等待表的元数据锁,而它成功地把整个复制队列给堵死了。根因:大字段改动触发全表重建你的DBA或者开发在改表的时候,执行了这样的命令:

sql 复制代码
ALTER TABLE big_table MODIFY COLUMN description varchar(6000);

看起来是个很小的改动对吧?

但是问题来了:varchar(5000)改到varchar(6000),虽然都是可变长度字段,但MySQL5.7和早期的8.0不认啊!

它们不能用INSTANT算法,必须重建整个表,这意味着:

  • 需要一把排他锁(EXCLUSIVE)
  • 要把所有行都重新写一遍

要是表有这10GB数据,那可能要花10分。

在这10分钟里,所有试图访问这个表的查询都被挡在外面。
从库上的SQL线程要同步这个DDL,但是它排在队伍最后面,要等主库上的所有数据变更都复制完了才能轮到它......结果就是:延迟越来越大,最后彻底崩溃。 解决:MySQL8.0的INSTANT魔法如果你用的是MySQL8.0.12及以上,恭喜你,有个秘密武器:

sql 复制代码
ALTER TABLE big_table MODIFY COLUMN description varchar(6000), ALGORITHM=INSTANT;

只要改动的字段长度≤255字节,或者只是扩大长度(从小到大),ALGORITHM=INSTANT就能秒级完成,真的,就是秒级。

这个算法有多猛:

  • ✅ 只修改元数据,不碰表数据
  • ✅ 不需要排他锁,其他查询照常执行
  • ✅ 不产生大量binlog
  • ✅ 从库完全无感知
  • ✅ SecondsBehindMaster还是0

秒杀,真的大扩容怎么办?用gh-ost救命但是,如果你这样做

sql 复制代码
ALTER TABLE big_table MODIFY COLUMN data varchar(5000) -> varchar(50000);

这超出255字节的跨度,INSTANT也爱莫能助,得重新构建表,这时候,不可以让主库直接开展ALTER操作,得运用专业工具gh-ost。

gh-ost是怎么活命的

scss 复制代码
gh-ost --user="root" --password="pass" --host="127.0.0.1" \  --database="your_db" --table="your_table" \  --alter="MODIFY COLUMN data varchar(50000)" \  --execute

这个工具会在后台悄悄地

  1. 创建影子表(跟原表一样的结构)
  2. 边改表边复制数据,业务照常读写
  3. 在二进制日志里抄下增量更新,不断应用到影子表
  4. 到最后一瞬间,原表和影子表闪电切换

整个过程中,原表一秒都不锁,业务无感知。

虽然比INSTANT慢,但也就是几分钟的事,遥遥好过直接ALTER要花的半小时。预防:打好这三道保险第一道:所有DDL先验证一遍

scss 复制代码
pt-online-schema-change \  --user=root --password=pass --host=127.0.0.1 \  --database=your_db --table=your_table \  --alter="MODIFY COLUMN description varchar(6000)" \  --dry-run

--dry-run不会真的改表,但会告诉你这个改动会花多长时间,需要多少资源。 如果输出显示"模拟成功",真正执行的时候成功率就很高。

第二道:加上执行时间限制把这个参数写进my.cnf`

ini 复制代码
[mysqld]max_execution_time=300000

所有查询最多,耗费时间300秒(也就是300000毫秒),要是超出时间就会自动断开连接,不要让慢查询一直占用资源不释放。第三道:关键的DDL一定在业务低谷期执行。凌晨3点修改表格,清晨8点又上线,即便出现些许问题也不会影响白天的用户,结语MySQL主从延迟的根源往往不在网络、不在磁盘、也不在缓冲池,而在你对DDL操作的敬畏心不够。 一条小小的ALTERTABLE,如果处理不当,就能把整条主从链路炸掉,让你排查半天还摸不着头脑。牢记这个顺序,先用,pt-online-schema-change--dry-run来验证,小的改动使用INSTANT,大的改动采用gh-ost,关键操作,设置maxexecution_time,接着挑选一个业务低峰期去执行,这种情况下,1000秒的延迟雪崩便永远不会到来。如果这篇文章帮你避免了一次线上事故,最好的回报就是关注和转发。

让更多人知道DDL操作的正确打开方式,咱们一起把数据库搞稳定。

声明,本文内容九成是本人原创,少量素材经过AI辅助生成,并且所有内容都经过本人严格核查,图片素材,都来自真实素材或者AI原创,文章旨在宣扬正能量,没有低俗不良导向,希望读者知道。

相关推荐
無量2 小时前
MySQL架构原理与执行流程
后端·mysql
JHC0000002 小时前
dy直播间评论保存插件
java·后端·python·spring cloud·信息可视化
武子康3 小时前
大数据-190 Filebeat→Kafka→Logstash→Elasticsearch 实战
大数据·后端·elasticsearch
西京刀客3 小时前
go语言-切片排序之sort.Slice 和 sort.SliceStable 的区别(数据库分页、内存分页场景注意点)
后端·golang·sort·数据库分页·内存分页
计算机毕设VX:Fegn08953 小时前
计算机毕业设计|基于springboot + vue汽车销售系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·汽车·课程设计
聆风吟º3 小时前
【Spring Boot 报错已解决】Spring Boot项目启动报错 “Main method not found“ 的全面分析与解决方案
android·spring boot·后端
Rover.x3 小时前
Arthas内存泄露排查
java·后端
艺杯羹3 小时前
掌握Spring Boot配置艺术:从YAML基础到实战进阶
java·spring boot·后端·yaml
喵叔哟3 小时前
12.云平台部署
后端·.netcore