MySQL 数据库崩溃(crash)的常见原因和解决办法
数据技术嘉年华,十周年盛大开启,点我立即报名!大会以“自研·智能·新基建——云和数据促创新 生态融合新十年” 为主题,相邀数据英雄,总结过往十年历程与成绩,展望未来十年趋势与目标!近60场演讲,大咖云集,李飞飞、苏光牛、林晓斌、黄东旭...,快来pick你喜欢的嘉宾主题吧!
检查 MySQL 数据库的启动时间
Linux 系统中的 systemd 会在 mysqld 进程 crash 后自动重新启动 MySQL 的服务,需要注意的是使用 kill -9 杀死 mysqld 进程系统会自动重新启动,而只使用 kill 命令则不会重新启动,因为执行 kill 命令,系统会发送一个 SIGTERM 信号给 mysqld,mysql 数据库会正常关闭,日志中会出现类似下面的记录:
2020-10-26T09:06:48.435181Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.19) MySQL Community Server - GPL.
MySQL 数据库 crash 后都会重新启动,因此我们有时可能不知道 MySQL 数据库已经 crash 过了,但我们可以从mysql数据库启动时间上找到线索,下面介绍四种检查 MySQL 数据库启动时间的方法。
检查 MySQL 服务状态
scutech@scutech:~$ service mysql status
● mysql.service - MySQL Community Server
Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2020-10-21 05:54:18 NDT; 4 days ago
Process: 774 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid (code=exited, status=0/SUCCESS)
Process: 708 ExecStartPre=/usr/share/mysql/mysql-systemd-start pre (code=exited, status=0/SUCCESS)
Main PID: 791 (mysqld)
Tasks: 27 (limit: 2328)
CGroup: /system.slice/mysql.service
└─791 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
显示 MySQL 数据库已经运行 4 天多。
检查 MySQL 中的 uptime 状态
mysql> show global status like 'uptime';
+---------------+--------+
| Variable_name | Value |
+---------------+--------+
| Uptime | 428334 |
+---------------+--------+
1 row in set (0.32 sec)
这个值是以秒为单位,下面换算成以天为单位是 4 天多。
mysql> select 428334/60/60/24;
+-----------------+
| 428334/60/60/24 |
+-----------------+
| 4.957569444444 |
+-----------------+
1 row in set (0.01 sec)
查询 uptime 状态的另一种方法是使用 mysqladmin version 或在 mysql 客户端里用 “\s” 进行查询。
使用 ps 检查进程启动时间
使用 ps 命令查询发现 mysqld 启动了4天23小时3分种54秒
scutech@scutech:~$ ps -eo pid,user,args,etime|grep mysqld
791 mysql /usr/sbin/mysqld --daemoniz 4-23:03:54
检查 MySQL 日志
找关键字 “ready for connections”,可以查到启动信息。
2020-10-21T08:24:18.986765Z 0 [Note] /usr/sbin/mysqld: ready for connections.
Version: '5.7.28-log' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
MySQL 数据库 crash 的常见原因
MySQL 数据库 crash 的最常见原因有两个,一个是 mysql 的 bug , 另一个是 mysql 申请系统资源失败。
MySQL 的 bug
MySQL数据库 crash 的最常见的一个原因当然是 MySQL 的bug。95% 的 bug 都是和具体的 sql 相关,通常是 MySQL crash 前执行的最后一个 sql 有问题,因此定位 bug 时应打开 general query log ,根据最后一个 sql 来查找线索。
当你确定了 crash 的原因后,应该检查一下 MySQL 的 bug 库(https://bugs.mysql.com),通常采用 Advanced search,看看有没有类似的问题。如果你找到了可能与你相关的 bug,确认它是否修复了。如果已经修复了,那么把 MySQL 升级到 bug 已经修复的版本。
在每个版本的 Release Notes 里面有一节 Bugs Fixed ,可以查到修复的 bug 。
MySQL 申请系统资源失败
出现这种现象通常会在 MySQL 的错误日志里下面的提示:
mysqld got signal 11
我们使用 perror 查看一下 11 错误的原因:
root@scutech:~# perror 11
OS error code 11: Resource temporarily unavailable
MySQL error code MY-000011: Can't unlock file (OS errno %d - %s)
最常见的是系统内存不足,由于系统连接数的增加和每个连接对内存的占用增加,造成 MySQL 申请新的内存失败而 crash,此时错误日志里面通常有类似下面的记录:
10:55:07 UTC - mysqld got signal 11 ;
This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built,
or misconfigured. This error can also be caused by malfunctioning hardware.
Attempting to collect some information that could help diagnose the problem.
As this is a crash and something is definitely wrong, the information
collection process might fail.
Please help us make Percona Server better by reporting any
bugs at http://bugs.percona.com/
key_buffer_size=16777216
read_buffer_size=8388608
max_used_connections=95
max_threads=50001
thread_count=31
connection_count=28
It is possible that mysqld could use up to
key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 819273023 K bytes of memory
Hope that's ok; if not, decrease some variables in the equation.
此段的记录里面列出了内存占用的计算方法,但需要注意这个公式里面没有把内存占用的大户,Innodb 的 buffer pool 计算进来,大家可以用这个公式加上 buffer pool 计算自己的内存占用,然后根据自己的实际情况对各个参数进行调整,如果连接数过多,可以通过修改 max_connections 对最大连接数进行限制。
除了内存不足造成 MySQL 申请系统资源失败外,另一个常见的现象是磁盘问题,例如磁盘空间满了,磁盘上的文件 corrupt 都会造成 MySQL crash。此时需要定位 crash 的根本原因有下面几种方法:
仔细阅读 MySQL 的错误日志,这个日志里面的一些程序调试信息看起来很让人困惑,但静下心来仔细看,很多时候会找到线索;
打开 general query log ,找到最后一个 sql 访问的表或索引,检查这个表或索引,如果有问题就重建,通常可以解决问题。
开启 core dump,使用 gdb 分析 core file;
使用 strace 跟踪 mysqld 进程的系统调用;
使用 CMake 的选项 -DWITH_DEBUG=1 重新编译 mysqld,然后运行重新编译后的 mysqld,查看 trace 文件。
数据技术嘉年华,汇聚业内多种数据库最佳实践和顶级技术专家,只为总结 2020 ,与您尽享技术前沿,领先一步卓立变革潮头!
作者:姚远,Oracle 10G和12C OCM,MySQL 5.6 和 5.7 OCP,现在鼎甲公司任顾问,专注于Oracle,MySQL数据库多年。