MySQL日志轮转需区分类型:binlog由PURGE BINARY LOGS管理;error.log等须用logrotate配合FLUSH LOGS,并设create权限与postrotate刷新;slow/general log推荐改用表存储以规避轮转风险。

MySQL 默认不自动滚动二进制日志和错误日志
MySQL 自身只提供 expire_logs_days 控制 binlog 过期时间,对错误日志(error.log)甚至没有内置轮转机制。哪怕设置了 log_error_verbosity=3,日志文件仍会持续追加,直到磁盘写满。很多线上事故就源于 mysqld 进程因无法写入错误日志而静默退出,但磁盘已 100% —— 此时你连错误信息都看不到。
logrotate 配置必须配合 MySQL 的 FLUSH LOGS 操作
单纯用 logrotate 切割文件,MySQL 进程仍在往旧文件句柄里写,切割后的内容不会出现在新日志里(即“日志丢失”)。必须在 postrotate 阶段通知 MySQL 重新打开日志文件:
-
logrotate配置中需包含create 640 mysql mysql,确保新日志权限正确 - 务必使用
invoke-rc.d mysql reload或mysql -e "FLUSH LOGS;",后者更轻量且兼容 systemd 环境 - 避免用
kill -USR1:MySQL 8.0+ 已废弃该信号对错误日志的支持,仅 binlog 有效
示例片段(/etc/logrotate.d/mysql):
/var/log/mysql/error.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
create 640 mysql mysql
sharedscripts
postrotate
if [ -f /var/run/mysqld/mysqld.pid ]; then
mysql -Nse "FLUSH LOGS;" 2>/dev/null || true
fi
endscript
}
区分 binlog 和 general/slow log 的处理逻辑
binlog 是 MySQL 自己管理的,不能直接交给 logrotate 切割;否则会导致 mysql-bin.index 记录错乱、主从同步中断。正确做法是:
- 关闭
log_bin的自动清理(expire_logs_days=0),改由PURGE BINARY LOGS手动或定时执行 - general_log 和 slow_query_log 若启用为文件模式(非 TABLE),才需
logrotate管理,且同样要FLUSH LOGS - 推荐将 slow_log 改为表存储(
slow_query_log_file不设值,slow_query_log=ON+log_output='TABLE'),彻底规避文件轮转问题
注意 logrotate 的 timing 和 mysqld 启动顺序冲突
如果 MySQL 启动晚于 logrotate 每日任务(比如系统重启后),postrotate 中的 mysql -e "FLUSH LOGS" 会失败,导致旧日志继续写入已被 rename 的文件。解决方法:
- 在
prerotate加判断:if systemctl is-active --quiet mysql; then ... - 或改用
copytruncate(仅适用于 error.log 和 slow/general log):它不重命名文件,而是清空原文件内容,MySQL 无感知,但存在极小概率丢最后几行日志 - 检查
logrotate实际执行时间:systemctl list-timers | grep logrotate,避免与备份脚本等高 IO 任务撞车
最稳妥的路径不是堆配置,而是把 binlog 生命周期交给 MySQL 内部控制,把 error.log 交给 logrotate + FLUSH LOGS,其余日志尽量走表存储。磁盘爆掉往往不是因为没配轮转,而是没验证轮转是否真生效。


















