Docker部署出现MySQL磁盘占用100%情况

Docker部署出现MySQL磁盘占用100%情况

情景

MySQL的binlog文件配置出错导致磁盘占用完的问题

由于使用Docker部署MySQL,然后未配置一些相关设置,导致再长久的使用中,已经让MySQL占用磁盘达到了恐怖的31G

sudo du -h --max-depth=1 / 2>/dev/null | sort -hr | head -n 10
# 扫描根目录下占用空间最大的前10个目录
39G		/
34G	    /usr
3.8G	/var
1.3G	/snap
102M	/boot
6.3M	/etc
1.1M	/run
516K	/root
68K		/tmp
40K		/home
sudo du -sh /var/lib/docker/*
# 检查 Docker 存储目录占用
5.9M	/var/lib/docker/buildkit
4.0K	/var/lib/docker/containers
4.0K	/var/lib/docker/engine-id
6.2M	/var/lib/docker/image
92K	/var/lib/docker/network
4.0K	/var/lib/docker/nuke-graph-directory.sh
2.2G	/var/lib/docker/overlay2
16K	/var/lib/docker/plugins
4.0K	/var/lib/docker/runtimes
4.0K	/var/lib/docker/swarm
4.0K	/var/lib/docker/tmp
28K	/var/lib/docker/volumes
sudo du -sh /usr/app/mysql/data
31G	/usr/app/mysql/data

由上面的检测情况就知道MySQL已经爆炸了

Linux的du命令解析

du命令用来查看目录或文件所占用磁盘空间的大小。常用选项组合为:du -sh

du常用的选项:
  -h:以人类可读的方式显示
  -a:显示目录占用的磁盘空间大小,还要显示其下目录和文件占用磁盘空间的大小
  -s:显示目录占用的磁盘空间大小,不要显示其下子目录和文件占用的磁盘空间大小
  -c:显示几个目录或文件占用的磁盘空间大小,还要统计它们的总和
  --apparent-size:显示目录或文件自身的大小
  -l :统计硬链接占用磁盘空间的大小
  -L:统计符号链接所指向的文件占用的磁盘空间大小  

du -sh : 查看当前目录总共占的容量。而不单独列出各子项占用的容量 
du -lh --max-depth=1 : 查看当前目录下一级子文件和子目录占用的磁盘容量。
du -sh * | sort -n 统计当前文件夹(目录)大小,并按文件大小排序
du -sk filename 查看指定文件大小

使用ls查看Mysql的文件情况,发现有一对binlog文件,并且每个文件大小都超过了1G

root@main-blog:/usr/app/mysql/data# ls
 auto.cnf        binlog.000060   binlog.000065   binlog.000070   binlog.000075   binlog.000080   binlog.000085   binlog.000090   client-cert.pem      ibdata1         mysql.sock              server-cert.pem
 binlog.000056   binlog.000061   binlog.000066   binlog.000071   binlog.000076   binlog.000081   binlog.000086   binlog.index    client-key.pem      '#innodb_redo'   mysql_upgrade_history   server-key.pem
 binlog.000057   binlog.000062   binlog.000067   binlog.000072   binlog.000077   binlog.000082   binlog.000087   blog           '#ib_16384_0.dblwr'  '#innodb_temp'   performance_schema      sys
 binlog.000058   binlog.000063   binlog.000068   binlog.000073   binlog.000078   binlog.000083   binlog.000088   ca-key.pem     '#ib_16384_1.dblwr'   mysql           private_key.pem         undo_001
 binlog.000059   binlog.000064   binlog.000069   binlog.000074   binlog.000079   binlog.000084   binlog.000089   ca.pem          ib_buffer_pool       mysql.ibd       public_key.pem          undo_002
root@main-blog:/usr/app/mysql/data# du -sh * | sort -n
0	mysql.sock
1.1G	binlog.000059
1.1G	binlog.000060
1.1G	binlog.000061
1.1G	binlog.000062
1.1G	binlog.000063
1.1G	binlog.000064
1.1G	binlog.000065
1.1G	binlog.000066
1.1G	binlog.000067
1.1G	binlog.000068
1.1G	binlog.000070
1.1G	binlog.000071
1.1G	binlog.000072
1.1G	binlog.000073
1.1G	binlog.000074
1.1G	binlog.000075
1.1G	binlog.000076
1.1G	binlog.000080
1.1G	binlog.000081
1.1G	binlog.000082
1.1G	binlog.000084
1.1G	binlog.000085
1.1G	binlog.000086
1.1G	binlog.000087
1.1G	binlog.000088
1.1G	binlog.000089
66M	binlog.000083
697M	binlog.000069
825M	binlog.000077
891M	binlog.000056
944M	binlog.000058
973M	binlog.000090

为什么

为什么会有大量的binlog文件?

  • MySQL 默认行为
    • binlog 用于记录数据库的修改操作(如增删改),支持主从复制和数据恢复。
    • 默认情况下,如果未配置过期策略,binlog 会一直保留,文件数量会不断增加。
  • 大事务操作
    • 批量插入、大批量更新或长时间未提交的事务会导致单个 binlog 文件急剧增大。
  • 复制延迟
    • 如果使用主从复制,从库长时间未同步会导致主库保留大量 binlog 文件。

怎么解决

怎么解决大量binlog文件恢复系统?

  • 方式一:通过 MySQL 命令清理

连接到 MySQL 容器,执行以下命令清理 早于指定时间或指定文件之前 的 binlog:

 docker exec -it blog_mysql /bin/bash # 进入容器内部
 mysql -u root -p  # 登录
 SHOW BINARY LOGS; # 查看所有 binlog 文件列表
 +---------------+------------+-----------+
| Log_name      | File_size  | Encrypted |
+---------------+------------+-----------+
| binlog.000075 | 1074323858 | No        |
| binlog.000076 | 1074323858 | No        |
| binlog.000077 |  864701254 | No        |
| binlog.000078 |        181 | No        |
| binlog.000079 |        181 | No        |
| binlog.000080 | 1074326012 | No        |
| binlog.000081 | 1074323858 | No        |
| binlog.000082 | 1074323858 | No        |
| binlog.000083 |   68783098 | No        |
| binlog.000084 | 1074324790 | No        |
| binlog.000085 | 1074323858 | No        |
| binlog.000086 | 1074323858 | No        |
| binlog.000087 | 1074323858 | No        |
| binlog.000088 | 1074323858 | No        |
| binlog.000089 | 1074323858 | No        |
| binlog.000090 | 1018642405 | No        |
| binlog.000091 |   22927797 | No        |
+---------------+------------+-----------+
17 rows in set (0.04 sec)
 SHOW BINARY LOG STATUS; # 查看当前正在使用的 binlog 文件
 +---------------+----------+--------------+------------------+-------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000091 | 36029305 |              |                  |                   |
+---------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
-- 删除早于 7 天的 binlog
PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY;

-- 或删除指定文件名之前的 binlog(保留最新文件)
PURGE BINARY LOGS TO 'binlog.000090';  -- 删除 000090 之前的所有 binlog
  • 方式二:直接删除文件(需谨慎)

直接删除文件可能导致数据不一致,仅限紧急情况

# 进入 MySQL 数据目录
cd /usr/app/mysql/data
bash
# 删除旧的 binlog 文件(保留最新的 3-5 个)
sudo rm binlog.000056 binlog.000057 ...  # 手动指定要删除的文件名

怎么预防

  • 配置 binlog 自动清理策略

修改 MySQL 配置文件(my.cnf 或通过 Docker 环境变量),设置 binlog 过期时间和最大文件大小:

[mysqld]
# 设置 binlog 过期时间为 3 天(按需调整)
expire_logs_days = 3

# 或 MySQL 8.0+ 使用时间单位(推荐)
binlog_expire_logs_seconds = 259200  # 3天(3*24*3600)

# 限制单个 binlog 文件大小(默认 1G,可调整为 100M-500M)
max_binlog_size = 100M
  • Docker 部署的配置方式

docker run 命令中直接传递参数:

docker run -d \
  --name mysql \
  -v mysql_data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=your_password \
  mysql:8.0 \
  --expire_logs_days=3 \
  --max_binlog_size=100M
  • 排查大事务问题

如果某个 binlog 文件异常大(如 1.1GB),可能是由大事务导致。检查慢查询日志:

-- 查看未提交的长事务
SELECT * FROM information_schema.INNODB_TRX\G
-- 查看 binlog 事件详情(需启用 GTID 或知道具体 binlog 文件)
SHOW BINLOG EVENTS IN 'binlog.000071' LIMIT 10;
+---------------+--------+----------------+-----------+-------------+--------------------------------------+
| Log_name      | Pos    | Event_type     | Server_id | End_log_pos | Info                                 |
+---------------+--------+----------------+-----------+-------------+--------------------------------------+
| binlog.000091 |      4 | Format_desc    |         1 |         127 | Server ver: 9.1.0, Binlog ver: 4     |
| binlog.000091 |    127 | Previous_gtids |         1 |         158 |                                      |
| binlog.000091 |    158 | Anonymous_Gtid |         1 |         238 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000091 |    238 | Query          |         1 |         330 | BEGIN                                |
| binlog.000091 |    330 | Table_map      |         1 |         418 | table_id: 85 (blog.article)          |
| binlog.000091 |    418 | Update_rows    |         1 |      242984 | table_id: 85 flags: STMT_END_F       |
| binlog.000091 | 242984 | Table_map      |         1 |      243072 | table_id: 85 (blog.article)          |
| binlog.000091 | 243072 | Update_rows    |         1 |      387686 | table_id: 85 flags: STMT_END_F       |
| binlog.000091 | 387686 | Table_map      |         1 |      387774 | table_id: 85 (blog.article)          |
| binlog.000091 | 387774 | Update_rows    |         1 |      442598 | table_id: 85 flags: STMT_END_F       |
+---------------+--------+----------------+-----------+-------------+--------------------------------------+
10 rows in set (0.01 sec)

优化应用逻辑,避免以下操作:

  1. 单条 SQL 操作大量数据(如 DELETE FROM large_table)。

  2. 未提交的事务长时间运行。


  • 验证结果

清理后检查磁盘空间:

df -h

查看当前 binlog 状态:

SHOW BINARY LOGS;  -- 列出所有 binlog 文件
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds%';  -- 检查过期策略

配置文件

[mysqld]
server-id=1  # 必须设置
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
secure-file-priv= NULL

#最大链接数
max_connections=1024

#是否对sql语句大小写敏感,1表示不敏感
lower_case_table_names=1
log_bin_trust_function_creators=1

#启用log-bin
log-bin=mysql-bin

#设置binlog日志格式
binlog_format=mixed

# 设置 binlog 过期时间为 3 天(按需调整)
# expire_logs_days = 3

# MySQL 8.0+ 使用时间单位(推荐)
binlog_expire_logs_seconds = 259200  # 3天(3*24*3600)

# 限制单个 binlog 文件大小(默认 1G,可调整为 100M-500M)
max_binlog_size = 500M

# 数据表默认时区
default-time-zone='+08:00'

# Custom config should go here
!includedir /etc/mysql/conf.d/

server-id 是 MySQL 主从复制(Replication)环境中用于 唯一标识每个实例 的核心参数。

场景作用
主从复制主库和从库必须配置不同的 server-id,否则复制链路会因标识冲突而中断。
多源复制当从库需要连接多个主库时,每个主库和从库的 server-id 必须唯一,避免数据混乱。
组复制(Group Replication)在 InnoDB Cluster 或 Group Replication 中,server-id 用于节点间的唯一识别和通信。
Binlog 写入即使未启用主从复制,若开启了 binlog(log-bin),MySQL 也要求必须设置 server-id,否则服务无法启动。

binlog_format 的三种模式详解

binlog_format 决定了 binlog 记录数据变更的方式,直接影响 数据一致性、复制安全性和日志大小

模式工作原理优点缺点适用场景
STATEMENT记录实际执行的 SQL 语句(如 UPDATE users SET age=30 WHERE id=5;)。日志量小,节省存储和网络带宽。某些场景下主从不一致(如使用 NOW()UUID() 等非确定性函数)。简单业务,无复杂函数或存储过程。
ROW记录每行数据的变更细节(如 UPDATE users SET age=30 WHERE id=5; 会记录修改前后的整行数据)。数据一致性高,无不确定性问题。日志体积大,尤其是批量操作(如全表更新)。金融等高一致性要求的业务。
MIXED混合模式:默认使用 STATEMENT,当检测到不确定操作时自动切换到 ROW平衡日志大小和一致性。仍需处理部分潜在不一致问题。常规业务,需兼顾性能和一致性。

查看配置文件启用情况

SHOW VARIABLES LIKE 'server_id';
SHOW VARIABLES LIKE 'binlog_format';

检查时区

-- 查看全局时区
SHOW GLOBAL VARIABLES LIKE '%time_zone%';

-- 查看当前会话时区
SHOW VARIABLES LIKE '%time_zone%';

-- 查看当前时间
SELECT NOW();
### 卸载并重新安装 MySQL 容器 #### 停止并移除现有 MySQL 容器 为了安全地卸载现有的 MySQL 容器,首先要停止正在运行的容器: ```bash docker stop mysql_container_name_or_id ``` 确认容器已经停止后,可以将其彻底移除。这一步骤会删除容器本身及其关联的数据卷(如果未指定 `--volumes` 参数,则不会删除数据卷)。 ```bash docker rm -f mysql_container_name_or_id ``` 如果有挂载到主机上的持久化存储需求,在此之前应备份重要数据以防丢失[^1]。 对于不再需要的数据卷也可以一并清理掉,防止占用磁盘空间: ```bash docker volume prune ``` 以上命令将会询问是否继续执行,输入 y 来确认操作[^2]。 #### 移除本地 Docker 中的 MySQL 镜像 若希望完全清除旧版本的影响,并确保新安装的是最新版或特定版本的 MySQL ,则还需要删除已有的镜像文件: ```bash docker rmi mysql_image_tag ``` 这里 `mysql_image_tag` 是指具体的 MySQL 版本标签,比如 `mysql:8.0` 或者其他自定义标记名。当存在依赖该镜像创建出来的活动容器时,上述指令可能会失败;此时应该先按照前面的方法处理好这些容器后再试一次[^3]。 #### 重新拉取最新的 MySQL 镜像 现在可以从官方仓库获取新的 MySQL 镜像来准备后续部署工作: ```bash docker pull mysql:latest ``` 这条语句中的 `latest` 表示下载最新稳定发布的 MySQL 版本。当然也能够通过替换为具体的小版本号如 `5.7`, `8.0.x` 等方式精确控制所使用的软件版本[^4]。 #### 启动全新的 MySQL 容器实例 最后一步就是基于刚刚更新过的镜像启动一个新的 MySQL 实例了。下面给出一个简单的例子说明如何做这件事: ```bash docker run --name some-mysql \ -e MYSQL_ROOT_PASSWORD=my-secret-pw \ -d mysql:tag ``` 这段脚本里 `-e` 设置环境变量用于配置初始密码等参数;而 `some-mysql` 则是要给这个新建的服务起的名字;至于后面的 `mysql:tag` 就是指定要用哪个镜像作为模板啦! 另外还可以加上更多选项来自定义端口映射、网络设置之类的细节部分以满足实际应用场景的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值