锁表这件事,第一反应就去重启数据库的,十个里有好几个后来都后悔。数据库锁表大部分时候不是锁坏掉了,而是有人在等另一个人:一个跑了很久的查询,一个忘记提交的事务,一条卡在半路的 ALTER TABLE。监控上看到的是一排请求超时,数据库里其实是一张排队表。先看清谁堵着谁,再动手,顺序反了就是白折腾一整晚。

数据库锁表,八成是有人在等
连上去第一件事不是 kill。先 SHOW PROCESSLIST,State 那一列出现 Locked 或者 Waiting for table metadata lock 的,基本都是排队的;再看 information_schema.INNODB_TRX,重点不是有多少行,是 trx_started 那一列——开得最早、trx_state 还是 RUNNING 的那个,嫌疑最大。MySQL 8.0 之后 performance_schema.data_locks 和 data_lock_waits 更好用,谁等谁一行一行摆在那里;5.7 及更早还能查 information_schema.INNODB_LOCKS,8.0 里这两张表已经移除了,照着几年前的文章敲命令敲不出来,不是你的问题。
PROCESSLIST 只能说明有人在等,说不清是被谁等。等待链一长,它就给不出答案了。这也是为什么不少人 kill 掉一个会话,几秒钟之后同样的报错又回来——被干掉的那个只是队伍中间的一位,队伍前面的人还堵在那里。
锁还分好几类,行锁、表锁、元数据锁
类型分不清,处理办法就会用错。InnoDB 默认是行级锁,两个人同时改同一行才撞上;MyISAM 是表锁,一个人写整张表都在等,一些跑着老程序的老站点上还能见到这种。第三种最容易被误判:元数据锁 MDL,一条 ALTER TABLE 挂在那里没走完,后面所有查询一起排队,现象是网站突然全站卡住,可你去看 CPU、IO 都不高,磁盘也不忙。这种要去 performance_schema.metadata_locks 里找,光盯着 PROCESSLIST 只能看到一堆 Waiting。
有了 thread id,才谈得上动手
找到堵点之后,记两样东西再动手:trx_mysql_thread_id,还有那条 SQL 原文。kill 之前不看语句的,事后基本都说不清是谁干的。KILL QUERY 只把那条语句停掉,会话还在;KILL CONNECTION 直接断会话,事务回滚、锁释放,但连接池那边立刻少一条连接,应用要能扛住这一下。半夜操作尤其注意,别顺手把连接池管理账号的会话给断了,那是另一个事故的起点。
长事务比死锁更常见
死锁这件事,数据库自己会检测并回滚其中一个,报 1213,SHOW ENGINE INNODB STATUS 里能看到 LATEST DETECTED DEADLOCK 那一段。更常见的是长事务:开了事务去调第三方接口,等用户点确认,甚至在事务里发通知邮件,一挂就是几分钟。锁一直不放,后面一堆请求排着队,表现出来就是数据库锁表。所以看 trx_started 往往比看锁的细节更有用,一条开了很久还没提交的事务,比十条死锁更值得先处理。
还有一类容易被忽略的:慢查询把锁的时间拖长。本来一条 UPDATE 几十毫秒的事,索引没走对扫了半张表,锁的持有时间跟着被拉长,并发一上来就排队。这类问题 kill 是治不了的,kill 完明天还来。要么补索引,要么把大更新拆成小批,一次几千行那样跑。
备份和 DDL,最像元凶的两个
mysqldump 在 MyISAM 表上会拿读锁,导出期间写入就得等着;InnoDB 加 –single-transaction 能拿到一致性快照而不锁表,但前提是相关表都是 InnoDB,混着引擎就不好说了。DDL 更微妙,8.0 有 online DDL,加索引多数情况不阻塞写入,可它仍然要拿元数据锁,同一张表上并发一多,排队立刻起来。表大的时候,gh-ost、pt-online-schema-change 这类工具比硬上 ALTER 稳当,用不用得看业务能不能接受那段切换时间。具体行为还是以你所用版本的官方文档为准,版本之间差得挺多。
顺便提一句主从。主库上锁着,从库也可能跟着延迟,你看到的现象可能是从库那边读到的数据停了几分钟。这种先看复制视图里的延迟值,别在主库上乱 kill——有些等待本来就在从库回放,动主库那条只会让情况更乱。
一个顺手的排查顺序
顺序大概是:一,先对时间点,告警从几点几分开始;二,PROCESSLIST 看排队的人;三,INNODB_TRX 看未提交事务的年龄;四,data_lock_waits 找等待链的头;五,怀疑 DDL 就去 metadata_locks;六,记下 SQL 再 kill;七,回头把那条慢语句的索引和事务边界改掉。少走一步问题都不大,跳过头两步直接 kill 的,多半隔一会儿又炸一次。
生产库上现场试验是最贵的一课。有人为了看锁,直接在业务高峰开个事务跑 SELECT … FOR UPDATE,一挂十几分钟,然后自己成了下一个要排查的对象。要看锁,去从库、去测试环境,或者挑业务低峰做,别在主库高峰上玩。
下次再遇上,先别急着登录服务器,在心里问一句:谁在等谁。这句话问出来,一半的问题自己就散了。剩下那一半,多半是索引没建好,或者事务开得太长——那就不是当晚能解决的事,得排进改动清单。
【版权声明】:本站内容若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!