DDL卡了40分钟,真凶是一条没提交的SELECT

简介: ALTER TABLE 卡四十分钟,换算法重试都没用。拆清 MDL 的持有与等待队列,讲明未提交的 SELECT 怎么堵住整张表,给出三层排查思路,说明 KILL QUERY 为何放不掉 MDL。

大家好,我是数据库小学妹👋 我踩过的坑,你别再踩。

有回发版加索引,一条 ALTER TABLE 卡了四十分钟。我先以为是表大、DDL 慢,换了算法,又重试了两次。结果不是慢,是根本拿不到锁。SHOW PROCESSLIST 里那一行写着 Waiting for table metadata lock,后面还排着一串查询。业务不是被 DDL 拖慢的,是被它堵住的。

一、卡住的不是 DDL,是 MDL

ALTER TABLE 执行前,要先拿到这张表的元数据锁,也就是 MDL。它保护的是表结构,不是行数据。DDL 要拿排他锁,普通查询拿共享锁。只要有人还持有共享锁,排他锁就得等。

MDL 不是只有一种。SELECT 拿 MDL_SHARED_READ,DML 拿 MDL_SHARED_WRITE。在线 DDL 还会先拿 MDL_SHARED_UPGRADABLE。真正改表定义时,要升级成 MDL_EXCLUSIVE。升级这一步,就得等前面所有共享锁退场。

等的不是几毫秒。MDL 跟着事务走,事务不提交,锁就不放。一条普通的 SELECT,只要在事务里,也会把 MDL 一直攥着。这就是 DDL 卡住的起点。

二、一条 SELECT 为什么能挡住 DDL

很多人以为只有写操作才持锁。其实读也会。事务里执行一条 SELECT,MySQL 会在表上挂一个 MDL_SHARED_READ。这条语句跑完了,锁也不会马上放,要等事务提交或回滚。

所以真正危险的不是长 SQL,是长事务。查询早就返回了,连接还在 Sleep,事务还开着。更常见的是连接池把连接还回去了,事务却没提交。应用以为这次请求结束了,MDL 还挂在那条会话上。DDL 只能在队列里等,等到 lock_wait_timeout 超时,默认是一年。

-- 会话 A:开事务,读一下,不提交
SET autocommit = 0;
SELECT COUNT(*) FROM t_order WHERE id = 1;
-- 不 commit,连接挂着

-- 会话 B:加索引,卡在等待 MDL
ALTER TABLE t_order ADD INDEX idx_user_id (user_id);
-- State: Waiting for table metadata lock

三、最怕的不是等,是后面跟着排队

MDL 有一套等待队列。一个排他锁在等,后面的共享锁请求也会往后排。这是为了避免 DDL 被源源不断的新查询饿死。代价是,一条卡住的 DDL,会把整张表变成单行道。

怎么确认是在排队?SHOW PROCESSLIST 里会同时出现两种状态。DDL 那一行是 Waiting for table metadata lock,新来的查询也可能变成同一句。看到两行以上,基本就能判断是 MDL 队列,不是单条 SQL 慢。

那天就是这样。DDL 卡住之后,新的查询开始变慢,接着超时,最后连接池也满了。我一开始还在查 SQL,其实 SQL 都没问题。问题在表级队列上,一条没提交的 SELECT 把整条路堵死了。

四、把等待链一层层剥开

先看谁在等。MDL 在 Server 层,不在 InnoDB 层,SHOW ENGINE INNODB STATUS 里看不到它。performance_schema.metadata_locks 会列出当前所有 MDL,已授予和等待中的都在里面。LOCK_STATUS 是 PENDING 的那一行,就是卡住的请求。OWNER_THREAD_ID 指向持锁或等锁的线程。再看谁堵了谁,sys.schema_table_lock_waits 把等待方和阻塞方拼在一起,还直接给出 kill 语句。阻塞方如果是空闲连接,blocking_query 可能是空的。这时去 information_schema.innodb_trx 看事务,trx_started 越早越可疑,trx_state 是 RUNNING、trx_query 是空,基本就是开了事务没提交。

还有个前提。metadata_locks 要有数据,得先开 wait/lock/metadata/sql/mdl 这个 instrument。8.0 默认是开的。表查出来是空的,先确认它的状态。查不到不等于没有锁,可能是没采集。

-- 0. 确认 MDL 采集已开启(8.0 默认 ON)
SELECT NAME, ENABLED, TIMED
FROM performance_schema.setup_instruments
WHERE NAME = 'wait/lock/metadata/sql/mdl';

-- 1. 看 MDL:谁在等,谁已拿到
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE,
       LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID
FROM performance_schema.metadata_locks
WHERE OBJECT_SCHEMA = 'shop'
ORDER BY OBJECT_NAME;

-- 2. 看等待链:谁堵了谁,附带 kill 语句
SELECT * FROM sys.schema_table_lock_waits\G

-- 3. 看事务:谁开得最久,谁一直没提交
SELECT trx_id, trx_state, trx_started,
       trx_mysql_thread_id, trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started;

-- 4. 把 PFS 线程 ID 换成 processlist ID
SELECT THREAD_ID, PROCESSLIST_ID, PROCESSLIST_USER,
       PROCESSLIST_HOST, PROCESSLIST_STATE, PROCESSLIST_TIME
FROM performance_schema.threads
WHERE THREAD_ID = ?;

还有一个细节,OWNER_THREAD_ID 是 PFS 的线程 ID,不是 SHOW PROCESSLIST 里的 ID。不 join performance_schema.threads,你拿到的号可能对不上人。查等待链时,先把这两张表对上,再决定杀谁。这是很多人查 MDL 查一半就断线的原因。

还有一层更容易被忽略。阻塞方未必是 MDL 的持有者,它可能也在等行锁。一个事务先锁了一行,另一个事务在等这行。第三个事务的 DDL,又在等第二个事务的 MDL。三条链套在一起,只看 MDL 表会以为是第二个事务的锅。往下再查 sys.innodb_lock_waits,才能找到最底下那个真正不放锁的事务。

五、在线 DDL 也绕不过 MDL

很多人一听 DDL 卡住,就换 ALGORITHM=INPLACE, LOCK=NONE。这两个参数能避免拷贝整张表,也能减少数据锁。但它们管不了 MDL。DDL 开始时仍要拿一次排他 MDL,结束时还要再拿一次,用来切换表定义。

ALGORITHM=INSTANT 也一样。它只改元数据,看起来最快,照样要拿 MDL。还有些变更只能走 COPY,LOCK=NONE 根本用不上。先看操作类型,再谈算法,别把参数当护身符。

gh-ost 和 pt-osc 也一样。它们把拷贝放到影子表上,最后一步 cutover 仍要短暂拿住原表的 MDL。如果这时有一个长事务挂着,cutover 就会反复重试。工具不会失败,它会一直等。你以为它在跑,其实它卡在门口。

这套逻辑在金仓 KES 上也是通的。sys_locks 能看到锁的持有和等待关系。sys_stat_activity 能查到会话状态和阻塞链。排查思路和 MySQL 的 metadata_locks 加 schema_table_lock_waits 是一脉相承的。换库不换理。DDL 卡住,先找持锁的事务,不是换算法。

六、先止血,再改习惯

止血的第一步不是杀 DDL,是找根阻塞者。杀 DDL 只是把等待的请求撤掉。持锁的事务还在,下一次发版还会卡。找到那个事务之后,先确认它在干什么。再决定是让它提交、回滚,还是直接断连接。

这里有个容易踩的坑。KILL QUERY 只停当前语句,事务还在,MDL 也还在。要让 MDL 释放,通常得 KILL CONNECTION,或者让应用自己提交、回滚。杀之前看清楚线程 ID,别把业务连接误杀了。

wait_timeout 也会兜底。空闲连接超时后会被断开,事务跟着回滚,MDL 自然释放。但这是被动兜底,回滚的代价可能比卡住更大。别把它当常规手段。

lock_wait_timeout 也值得单独说。它管的是 MDL 等待,默认 31536000 秒,也就是一年。innodb_lock_wait_timeout 管的是行锁,默认 50 秒,两个不是一个东西。给 DDL 会话设一个短超时,比如 5 到 10 秒。卡住就快速失败,别让它把整张表拖进队列。

根治还是靠事务习惯。事务里只放必要的 SQL。别把 RPC 调用、文件上传、用户思考时间包进去。报表查询单独走从库或只读实例。给它设 max_execution_time,跑太久自己退出。发版前先查一遍长事务,超过 60 秒的先找业务确认,再动 DDL。这几步做完,MDL 卡住的概率会低很多。

发版前先查一遍长事务。下面这条 SQL 会按开始时间列出超过 60 秒的事务。看到它们,先找业务确认。处理完再动 DDL。

SELECT trx_id, trx_started, trx_mysql_thread_id,
       TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS seconds
FROM information_schema.innodb_trx
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 60
ORDER BY trx_started;

监控也要补上。innodb_trx 里超过 60 秒的事务,应该有一条告警。metadata_locks 里 PENDING 超过 5 秒,也应该有一条。别等业务超时了,才去看等待链。告警早五分钟,处置就从容很多。

七、避坑清单

DDL 卡住,先看 SHOW PROCESSLIST 里的 State。出现 Waiting for table metadata lock,就别再怀疑 DDL 算法了,去查持有 MDL 的事务。算法只决定怎么拷贝,决定不了能不能拿到锁。锁不在算法手里,在人手里。

lock_wait_timeout 和 innodb_lock_wait_timeout 别混。前者管 MDL,默认一年;后者管行锁,默认 50 秒。DDL 会话把前者调短,卡住就失败,别把整张表挂在队列里。超时不是故障,是保护。

最后一条是我自己踩的。有次 DDL 卡了四十分钟,我先 kill 了 DDL,又重试了两次。结果新查询继续排队,业务反而更慢。后来才知道,真正持锁的是一条没提交的 SELECT。现在发版前,我先跑一条长事务检查。超过 60 秒的先找业务确认,再动 DDL。这个顺序反了,越救越乱。

八、写在最后

MDL 最难的地方,是它不报错。DDL 只是在等,查询也只是变慢。没有异常栈,没有红色告警,只有一张表慢慢变成单行道。等你看到连接池满了,往往已经堵了一阵子。

DDL 的算法决定它怎么改表,MDL 决定它能不能开始。把这两件事分开看,排查就不会跑偏。下次再遇到 DDL 卡住,先别动参数。去看那条等待链,找到最底下那个没提交的事务。锁是它拿的,也得它放。

我现在把 MDL 当成发版前的一道检查。先看长事务,再看 DDL 的算法,最后才排窗口。顺序对了,DDL 只是一次变更。顺序反了,它就会变成一次事故。

你们发版做 DDL,会先查长事务吗?有没有被 MDL 卡过?当时是怎么找到阻塞会话的?评论区聊聊。

我是数据库小学妹,帮你少走弯路少踩坑,咱们下篇见👋

相关文章
|
云安全 安全 网络安全
|
4天前
|
JavaScript 安全 前端开发
Nodejs Brotli decoder 代码执行漏洞 (CVE-2026-111497)10629
Node.js发布安全更新修复Brotli解码器高危RCE漏洞(CVE-2026-111497),建议受影响版本用户立即升级。
1878 1
|
11天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
1769 37
|
5天前
|
安全 应用服务中间件 Go
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
PHP2Shell 双 RCE 已遭在野利用:WordPress、Drupal 全在射程内,nginx 其实另有洞。拆成因与修复顺序
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
|
8天前
|
Web App开发 人工智能 前端开发
2026 年 AI 浏览器怎么选?Ego Lite、Tabbit、Chrome DevTools MCP 深度对比,开发者到底该用哪个?
2026年AI浏览器选型指南:Ego Lite专注多Agent协同(Mac优先),Tabbit国产首选,原生支持Windows与Codex控制;Chrome DevTools MCP非浏览器而是开发者利器,深度集成Console/Network/Performance调试能力,让AI真正“看懂并验证”前端代码。
1128 3
|
12天前
|
安全 Linux 芯片
【万字长文】Ollama下载、安装、配置、使用保姆级教程(2026最新图解)
Ollama 是一款免费开源的本地大模型运行工具,支持 DeepSeek、Qwen 等主流模型,一条命令即可下载运行。2026 最新版 v0.35.1 官方安装包(1.47GB)已提供国内直连下载,纯本地、离线可用、无广告、无隐私泄露风险。
1108 2
|
12天前
|
JSON 程序员 数据格式
最新版Notepad++下载、安装、配置保姆级教程(附官网安装包)
Notepad++ 是一款免费开源、轻量快速的 Windows 文本编辑器,支持多标签、语法高亮、代码折叠与宏录制,体积小、启动快、资源占用低,兼容 x64/x32/ARM 架构,是程序员和文字工作者的高效利器。
1481 0
|
15天前
|
存储 人工智能 安全
千问办公官网入口网页版登录:qwenwork.cn 注册送2000积分、登录送100积分
阿里云千问办公(QwenWork)是AI智能工作平台,支持一句话生成PPT、表格、网页、视频及数据分析。个人免费版送2000积分+每日登录赠100分,含1GB存储、5个发布页;企业版198元/席/月起。阿里千问办公QwenWork官网:https://t.aliyun.com/U/0VCTGt 阿里AI工作平台,一句话完成数据分析、PPT 生成、视频剪辑、网页搭建等复杂任务
|
10天前
|
人工智能 开发者
Qoder CN 大使计划正式开启|无限创造,等你一起
全年免费积分、优先内测、活动支持与更多共创机会,邀请用 Qoder 把想法变成现实的你。
676 7