凌晨一点四十七,手机震了。监控发来的:MySQL 主库挂了。

我爬起来登录,第一件事看进程还在不在。ps 一下,mysqld 没了。再看 mysql 的错误日志,最后几行是:

2026-08-21T01:46:52.123456Z 0 [ERROR] [MY-010584] InnoDB: Assertion failure in thread ...
2026-08-21T01:47:03.881234Z 0 [ERROR] [MY-010587] InnoDB: Failing assertion: ...

当时我脑子里的第一反应是:InnoDB assertion,这他妈是 bug 还是磁盘坏了?赶紧查磁盘,df -h 一看,空间还有 40% 多,没事。再看慢日志,发现崩溃前几分钟有一条大查询,全表扫描,扫了六百万行。

我就开始怀疑是这条查询把 MySQL 搞崩的。跟开发说,你们这个 SQL 有问题,全表扫,是不是它把 buffer pool 挤爆了。

开发不服,说这条 SQL 跑了半年了,一直没事。

我又回去翻系统日志。dmesg 一翻,真相在那摆着:

Out of memory: Kill process 18423 (mysqld) score 782 or sacrifice child
Killed process 18423 (mysqld) total-vm:31896544kB, anon-rss:11234876kB

是 OOM killer 干的,不是 MySQL 自己崩的。MySQL 那个 assertion 是它被 kill 之后的连锁反应,日志里那两行看着吓人,其实是死后的尸体。

那为什么内存会不够?free -h 一看:

          total        used        free      shared  buff/cache   available

Mem: 62G 24G 1.2G 1.1G 36G 31G

free 只有 1.2G,available 还有 31G。看着没事对吧?但问题是,这台机器 swap 配的是 0。页面缓存涨起来之后,内存一紧张,内核找不到可回收的页,直接找进程开刀。mysqld 是内存大户,score 782,排第一,就它了。

关键是我查了配置,innodb_buffer_pool_size 设的是 20G,但服务器上还跑着两个 Java 应用,一个吃 8G,一个吃 6G,加起来早就超了。当初是谁把这仨塞一台机器的,已经查无此人了。

修法没什么花样:给机器加了 8G swap,把两个 Java 应用挪走一个,buffer_pool 从 20G 降到 16G,然后 /etc/sysctl.conf 里 vm.swappiness 设成 10。重启 MySQL,观察了一周,没再被杀。

后来我养成了个习惯,看内存先看 available 和 swap,别只看 free。free 那 1.2G 是假象,available 31G 里一大半是页缓存,真到要命的时候内核不跟你讲情面,它只挑分高的杀。

对了,那条全表扫描的 SQL 后来开发优化了,加了索引。虽然它不是元凶,但也活该被说。