前言介绍
MySQL 数据库在长期运行过程中,会持续产生各类日志文件,包括二进制日志(binlog)、错误日志(error log)、慢查询日志(slow query log)以及通用查询日志(general log)。这些日志文件虽然对数据恢复、故障排查和性能分析至关重要,但如果未配置合理的清理策略或日志文件保留时间过长,会迅速占用大量磁盘空间。当磁盘使用率持续上涨,甚至达到100%时,会导致数据库无法写入新数据、查询变慢甚至服务宕机。本教程将手把手教你如何安全、有效地清理MySQL日志文件,释放磁盘空间,并设置自动清理机制,防止问题再次发生。
前置准备
在开始操作前,请确保满足以下条件:
1. 拥有MySQL服务器的访问权限,建议使用root用户或具有SUPER权限和日志管理权限的账号。
2. 能够通过SSH或远程桌面登录到MySQL所在的服务器(如果是云数据库,需确认控制台是否提供日志管理功能)。
3. 确认MySQL服务状态正常,建议在业务低峰期进行操作,避免影响线上服务。
4. 提前备份重要数据,尤其是二进制日志(binlog)可能用于增量恢复,清理前请确认不需要或已另存。
5. 准备一个文本编辑器或命令行工具,用于执行SQL语句和Linux命令。
分步操作步骤
1. 检查当前磁盘空间与日志占用情况
在清理前,先明确磁盘空间被哪些日志文件占用,避免盲目操作。
- 登录MySQL服务器,执行以下SQL语句查看当前日志文件配置:
```sql
SHOW VARIABLES LIKE 'log_bin%';
SHOW VARIABLES LIKE 'expire_logs_days';
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
SHOW VARIABLES LIKE 'slow_query_log_file';
SHOW VARIABLES LIKE 'general_log_file';
SHOW VARIABLES LIKE 'log_error';
```
重点关注`log_bin`是否开启(值为ON),以及`expire_logs_days`或`binlog_expire_logs_seconds`(MySQL 8.0及以上版本)的当前值。
- 在Linux系统中,使用`du`命令查看MySQL日志目录(通常为`/var/lib/mysql`或`/var/log/mysql`)的占用情况:
```bash
du -sh /var/lib/mysql/*binlog*
du -sh /var/log/mysql/*
```
如果MySQL安装在Windows上,通过资源管理器或`dir`命令查看日志文件大小。
- 使用`df -h`命令确认整体磁盘使用率,例如:
```bash
df -h
```
记录下当前磁盘使用百分比,以便清理后对比效果。
2. 安全清理二进制日志(binlog)
二进制日志是磁盘占用的大头,清理时需谨慎,避免影响主从复制或数据恢复。
- 查看当前二进制日志列表:
```sql
SHOW BINARY LOGS;
```
输出结果会列出所有binlog文件名及大小。
- 手动清理指定时间之前的binlog(推荐方法):
例如,删除3天前生成的所有binlog文件:
```sql
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 3 DAY);
```
或者删除到指定文件名之前的所有binlog(保留当前正在使用的文件):
```sql
PURGE BINARY LOGS TO 'mysql-bin.000010';
```
注意:不要使用`RESET MASTER`命令,它会删除所有binlog并重置序号,可能导致主从复制中断。
- 检查清理结果:
```sql
SHOW BINARY LOGS;
```
确认已删除的文件不再出现,且磁盘空间已释放(可通过`df -h`验证)。
- 如果MySQL是主从架构,务必先检查从库的复制状态:
```sql
SHOW SLAVE STATUS\G;
```
确认`Master_Log_File`和`Read_Master_Log_Pos`指向的binlog文件没有被清理。如果清理了从库尚未读取的binlog,会导致复制报错。
3. 清理错误日志、慢查询日志和通用查询日志
这些日志文件通常不会自动轮转,会无限增长。
- 错误日志(error log):
错误日志通常位于`/var/log/mysql/error.log`或`/var/lib/mysql/hostname.err`。在MySQL命令行中执行:
```sql
FLUSH LOGS;
```
该命令会关闭当前日志文件,并创建一个新的空日志文件(MySQL会自动重命名旧文件,例如`error.log.1`)。然后你可以手动删除旧文件:
```bash
rm /var/log/mysql/error.log.1
```
注意:不要直接删除当前正在使用的日志文件,否则MySQL会继续向已删除的文件写入(导致文件句柄残留,空间不释放)。必须先执行`FLUSH LOGS`。
- 慢查询日志(slow query log):
同样先执行`FLUSH LOGS`,然后删除旧文件:
```bash
rm /var/log/mysql/slow-query.log.1
```
如果慢查询日志文件非常大(例如几十GB),可以先清空文件内容而不删除文件(避免MySQL找不到文件):
```bash
> /var/log/mysql/slow-query.log
```
然后执行`FLUSH LOGS`让MySQL重新打开文件。
- 通用查询日志(general log):
通用查询日志记录所有SQL语句,增长极快。如果确认不需要,可以直接关闭该日志:
```sql
SET GLOBAL general_log = OFF;
```
然后按照上述方法清理旧文件。清理后如需开启,执行`SET GLOBAL general_log = ON;`。
4. 设置自动清理策略,防止再次占满磁盘
手动清理只能解决一时问题,必须配置自动轮转或过期删除。
- 设置二进制日志自动过期(MySQL 8.0及以上):
```sql
SET GLOBAL binlog_expire_logs_seconds = 259200; -- 3天,单位秒
```
或修改配置文件`/etc/my.cnf`(或`/etc/mysql/my.cnf`)永久生效:
```ini
[mysqld]
binlog_expire_logs_seconds = 259200
```
对于MySQL 5.7及以下版本,使用`expire_logs_days`参数:
```ini
expire_logs_days = 3
```
- 配置日志轮转(logrotate):
大多数Linux发行版自带logrotate,可以自动轮转MySQL日志。编辑配置文件:
```bash
vim /etc/logrotate.d/mysql
```
添加以下内容(根据实际日志路径调整):
```
/var/log/mysql/*.log {
daily
rotate 7
missingok
compress
delaycompress
notifempty
create 640 mysql mysql
sharedscripts
postrotate
/usr/bin/mysqladmin flush-logs
endscript
}
```
参数说明:
- `daily`:每天轮转一次。
- `rotate 7`:保留7个轮转后的旧日志文件。
- `compress`:压缩旧日志以节省空间。
- `postrotate`:轮转后执行`mysqladmin flush-logs`,让MySQL生成新日志。
- 验证配置:
```bash
logrotate -d /etc/logrotate.d/mysql # 调试模式,不会实际执行
logrotate -f /etc/logrotate.d/mysql # 强制执行一次
```
5. 清理MySQL数据目录中的临时文件与废弃文件
除了日志文件,MySQL数据目录中可能还残留临时表文件、崩溃恢复产生的临时文件等。
- 检查临时文件:
```bash
ls -lh /var/lib/mysql/#sql* /var/lib/mysql/ib*
```
其中`#sql_*.ibd`是临时表文件,如果MySQL正常关闭,这些文件应该不存在。如果发现大量此类文件,说明可能有异常中断的查询。
- 清理临时表文件:
在MySQL中执行:
```sql
DROP TEMPORARY TABLE IF EXISTS 表名;
```
但更简单的方法是重启MySQL服务(需在业务低峰期):
```bash
systemctl restart mysql
```
重启后MySQL会自动清理临时文件。
- 检查`ib_logfile0`和`ib_logfile1`(InnoDB重做日志):
这些文件通常不会无限增长,但如果你修改了`innodb_log_file_size`参数,旧的重做日志文件可能残留。删除前需先正常关闭MySQL,然后删除文件,再启动MySQL(会自动创建新文件):
```bash
systemctl stop mysql
rm /var/lib/mysql/ib_logfile*
systemctl start mysql
```
常见问题
1. 清理binlog后,主从复制报错“Could not find first log file name in binary log index file”
原因:清理了从库尚未读取的binlog文件。解决方法:在从库上执行`CHANGE MASTER TO MASTER_LOG_FILE='新文件名', MASTER_LOG_POS=4;`,跳过缺失的日志。但这样会导致数据不一致,建议重新搭建从库。
2. 执行`FLUSH LOGS`后,旧日志文件没有被重命名
检查MySQL配置文件中的`log_error`、`slow_query_log_file`等参数是否使用了绝对路径且MySQL用户有写入权限。另外,确保`logrotate`配置中的`create`参数正确,否则新日志文件可能无法创建。
3. 清理后磁盘空间没有立即释放
如果MySQL进程仍然持有已删除文件的句柄(例如直接`rm`了正在使用的日志文件),空间不会释放。此时需要执行`FLUSH LOGS`或重启MySQL服务。可以使用`lsof | grep deleted`命令查看哪些已删除文件仍被占用。
4. 设置`binlog_expire_logs_seconds`后,binlog没有自动删除
该参数只在binlog文件切换时生效(例如达到`max_binlog_size`或执行`FLUSH LOGS`)。可以手动执行`FLUSH LOGS`触发一次清理。另外,检查MySQL版本是否支持该参数(MySQL 8.0.1及以上)。
5. 通用查询日志导致磁盘爆满,但无法直接删除
如果通用查询日志已导致磁盘满,MySQL可能无法写入新日志。可以先通过`SET GLOBAL general_log = OFF;`关闭日志,然后使用`> /var/log/mysql/general.log`清空文件内容(不要删除文件),再执行`FLUSH LOGS`。
收尾总结
通过本教程,你学会了如何检查MySQL日志文件的磁盘占用情况,安全清理二进制日志、错误日志、慢查询日志和通用查询日志,并配置了自动轮转和过期删除策略。核心要点总结如下:
- 清理前务必检查主从复制状态,避免影响数据同步。
- 使用`PURGE BINARY LOGS`而非`RESET MASTER`来清理binlog。
- 对于正在使用的日志文件,先执行`FLUSH LOGS`再删除旧文件。
- 配置`binlog_expire_logs_seconds`(或`expire_logs_days`)和logrotate,实现自动化管理。
- 定期(例如每周)检查磁盘使用率,防患于未然。
建议将上述操作纳入数据库日常运维手册,并设置监控告警(如磁盘使用率超过80%时自动通知),确保MySQL长期稳定运行。