mysql自身不支持error.log、slow_query_log_file等文本日志按大小滚动,必须用logrotate配合postrotate中执行flush logs通知mysql重开日志文件,否则新内容仍写入旧文件句柄;binlog须用binlog_expire_logs_seconds等原生机制清理,严禁logrotate直接操作。

MySQL 自身不提供对 error.log、slow_query_log_file 等文本日志的“按大小滚动”能力,必须靠外部工具(如 logrotate)配合 MySQL 的日志重开机制实现——否则切割后新内容仍写入旧文件句柄,等于白切。
logrotate 必须配 postrotate + FLUSH LOGS,不能只切文件
单纯用 logrotate rename 文件,MySQL 进程还在往原文件描述符里写,新日志不会落地到新文件。关键动作是通知 MySQL 关闭旧句柄、打开新文件:
-
FLUSH LOGS是唯一通用且安全的方式,兼容所有 MySQL 版本和日志类型(error.log、slow.log、general.log) - 避免用
kill -USR1:MySQL 8.0+ 已废弃该信号对error.log的支持,仅binlog有效 - 脚本中需加判断:检查 MySQL 是否存活,否则
mysql -e "FLUSH LOGS"会失败,导致轮转后日志继续写进已 rename 的旧文件 - 示例片段(
/etc/logrotate.d/mysql):
"/var/log/mysql/error.log" {
daily
missingok
rotate 7
compress
notifempty
create 640 mysql adm
sharedscripts
postrotate
if systemctl is-active --quiet mysql; then
mysql -Nse "FLUSH LOGS;" 2>/dev/null || true
fi
endscript
}
想按大小触发滚动?用 size 替代 daily,但注意权限和路径一致性
logrotate 支持 size 500M 这类条件,比时间更贴近“防止单个文件过大”的原始需求,但有几个硬约束:
-
size检查发生在 logrotate 启动时(通常是每天 cron 触发),不是实时监控;若单日日志暴增超限,当天也不会切 - 必须确保
create权限与 MySQL 进程用户匹配:Ubuntu/Debian 通常用adm组,CentOS/RHEL 用mysql组,错配会导致新日志无法写入 - 路径要精确匹配 MySQL 实际写入位置:
SHOW VARIABLES LIKE 'log_error';查出的路径必须和 logrotate 配置中的路径完全一致(包括软链目标) - 不建议在同一个配置块里混用
daily和size,行为不可预测;选一个主策略即可
binlog 不能走 logrotate,必须用 MySQL 原生过期机制
mysql-bin.000xxx 类文件由 MySQL 自己管理索引(mysql-bin.index),若用 logrotate 直接删或 rename,会导致:
- 索引文件内容与磁盘文件不一致,
SHOW BINARY LOGS显示异常 - 主从复制中断,从库报
Could not find first log file name in binary log index file - 备份工具(如
mysqldump --master-data)获取的 binlog 位置失效
正确做法是禁用 logrotate 管理 binlog,改用 MySQL 原生控制:
- MySQL 8.0.23+:设
binlog_expire_logs_seconds = 604800(7 天),并确认binlog_expire_logs_auto_purge = ON - 旧版:设
expire_logs_days = 7,但注意该参数精度为天,且 8.0.23+ 与binlog_expire_logs_seconds冲突会报错ERROR 3683 -
max_binlog_size只控制单文件上限(如536870912字节),不影响总量,必须搭配过期策略
慢查询日志太大?优先关掉或切表存储,别硬扛文件滚动
slow_query_log_file 是最常因“开着不管”而膨胀到 GB 级的日志,直接轮转治标不治本:
- 日常生产环境建议
SET GLOBAL slow_query_log = OFF,排查性能问题时再临时开启 - 若必须长期开启,推荐改用表存储:
SET GLOBAL log_output = 'TABLE'; SET GLOBAL slow_query_log = ON;,日志写入mysql.slow_log表,天然规避文件滚动问题 - 强行用
logrotate管理时,务必在postrotate中执行FLUSH LOGS,否则slow.log切割后新慢查仍写进旧文件 - 不要试图用日期变量动态改
slow_query_log_file路径(如%y%m%d):MySQL 不解析 strftime 格式,只会字面创建带百分号的文件名
真正容易被忽略的点是:logrotate 的执行时机可能早于 MySQL 启动(比如系统重启后),此时 postrotate 中的 mysql -e 命令必然失败,旧日志持续写入已被 rename 的文件——所以必须加 systemctl is-active --quiet mysql 这类存活判断,而不是依赖 PID 文件是否存在。











