docker

关注公众号 jb51net

关闭
首页 > 网站技巧 > 服务器 > 云和虚拟化 > docker > Docker MySQL 8.0.45性能优化配置文档

Docker MySQL 8.0.45性能优化配置文档方式

作者:周易宅

文章主要讨论了MySQL 8.0.4 Docker容器在特定服务器环境中的优化过程,首先描述了当前配置和参数,接着分析了优化前的配置,发现存在严重的资源浪费,随后,文章详细介绍了优化后的参数配置,包括InnoDB缓存、连接与线程配置、内存在存临时表、慢查询日志等等的调整

一、硬件环境

项目配置
CPU4 核
内存7.7 GB(可用 ~6.5 GB)
磁盘97 GB
Docker 资源限制无限制(CPU=0, Mem=0)
MySQL 版本8.0.45(官方镜像)

二、配置文件架构

宿主机                         容器内
──────────────────────────────────────────
/data/mysql8/conf/  ──bind──▶ /etc/mysql/conf.d/
  ├── max_allowed_packet.cnf       (由 my.cnf 中 !includedir 自动加载)
  └── performance.cnf

/data/mysql8/data/  ──bind──▶ /var/lib/mysql/

所有配置通过 docker inspect mysql8 确认挂载正确,容器重启/重建均不丢失。

三、参数持久化方案

3.1 为什么 SET GLOBAL 不持久

MySQL 中修改参数有两种方式:

方式示例生效范围容器重启后
SET GLOBALSET GLOBAL max_allowed_packet=512M;当前运行实例,内存中❌ 丢失,恢复默认值
配置文件max_allowed_packet=512M 写入 my.cnf启动时读取,全局生效✅ 永久生效

因此所有性能参数必须写入配置文件,而非仅靠 SET GLOBAL

3.2 Docker 绑定挂载 → 持久化

MySQL 官方镜像的 my.cnf 末尾有指令:

!includedir /etc/mysql/conf.d/

这意味着放在 /etc/mysql/conf.d/ 目录下的任何 .cnf 文件都会被自动加载

关键:该目录通过 Docker bind mount 映射到宿主机,因此文件在宿主机上创建即可持久化:

宿主机文件                     挂载关系        容器内加载路径
────────────────────────────────────────────────────────────
/data/mysql8/conf/     ──bind mount──▶  /etc/mysql/conf.d/
  └─ performance.cnf                      └─ 被 my.cnf 的 !includedir 自动加载

docker inspect 验证挂载配置:

docker inspect mysql8 --format '{{json .Mounts}}' | python3 -m json.tool

输出示例:

[
    {
        "Type": "bind",
        "Source": "/data/mysql8/conf",
        "Destination": "/etc/mysql/conf.d",
        "RW": true
    },
    {
        "Type": "bind",
        "Source": "/data/mysql8/data",
        "Destination": "/var/lib/mysql",
        "RW": true
    }
]

Source = 宿主机目录,文件存这里就永远不会丢。

3.3 创建持久化配置(完整操作)

# Step 1: 在宿主机创建配置文件
cat > /data/mysql8/conf/performance.cnf << 'EOF'
[mysqld]
innodb_buffer_pool_size = 2G
innodb_buffer_pool_instances = 2
innodb_log_file_size = 256M
# ... 其他参数 ...
max_allowed_packet = 512M
EOF

# Step 2: 验证容器内可见
docker exec mysql8 cat /etc/mysql/conf.d/performance.cnf

# Step 3: 优雅关闭 + 重启(innodb_log_file_size 变更需额外处理)
docker exec mysql8 mysql -uroot -p -e "SET GLOBAL innodb_fast_shutdown=0;"
docker exec mysql8 rm -f /var/lib/mysql/ib_logfile*
docker restart mysql8

# Step 4: 验证参数已从配置文件加载
docker exec mysql8 mysql -uroot -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size';"
# 预期: 2147483648 (2G)

3.4 持久化验证

重启容器后确认参数来自配置文件而非默认值:

# 重启
docker restart mysql8

# 验证关键参数仍为优化值
docker exec mysql8 mysql -uroot -p -e "
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'innodb_log_file_size';
SHOW VARIABLES LIKE 'max_allowed_packet';
"
验证点默认值(重启后如未持久化)持久化后期望
innodb_buffer_pool_size134217728 (128M)2147483648 (2G)
innodb_log_file_size50331648 (48M)268435456 (256M)
max_allowed_packet67108864 (64M)536870912 (512M)

如果重启后值与期望不符,说明配置文件未被正确加载,检查:

  1. docker inspect 确认挂载路径正确
  2. docker exec mysql8 ls /etc/mysql/conf.d/ 确认文件存在
  3. docker exec mysql8 mysqld --verbose --help 2>/dev/null | grep -A1 "conf.d" 确认 !includedir 生效

3.5 当前已持久化的配置文件

/data/mysql8/conf/
├── max_allowed_packet.cnf    # [mysqld] max_allowed_packet=512M
└── performance.cnf           # [mysqld] 全部 InnoDB/连接/缓存优化参数

两个文件互不冲突,MySQL 会按文件名字母顺序加载(后加载的同名参数覆盖先加载的)。

四、优化前现状分析

MySQL 8.0.45 官方镜像的全部默认参数,运行在 7.7GB 服务器上存在严重浪费:

关键问题默认值浪费程度
Buffer Pool 仅 128M7.7GB 总内存利用率 1.6%🔴 严重
Redo Log 仅 50M高写入场景频繁 checkpoint🟡 中等
IO 容量仅 200未利用 SSD 吞吐能力🟡 中等
每次事务都刷盘开发/测试环境过度安全🟡 中等
线程缓存仅 9频繁创建/销毁线程🟢 轻微
慢查询日志关闭无法排查性能问题🟢 轻微

五、优化参数详解

4.1 InnoDB 核心优化

参数说明优化前优化后影响
innodb_buffer_pool_size数据和索引缓存,最重要的性能参数128M2G缓存命中率从 ~30% 提升至 95%+,减少磁盘 IO
innodb_buffer_pool_instancesBuffer Pool 分区数12多核并发访问时减少锁竞争
innodb_log_file_sizeRedo Log 单个文件大小50M256M减少 checkpoint 频率,写入更平滑
innodb_log_buffer_sizeRedo Log 内存缓冲区16M64M批量写入磁盘,减少小 IO
innodb_io_capacity后台 IO 操作吞吐上限2001000充分利用磁盘吞吐能力
innodb_io_capacity_max紧急情况下 IO 上限20003000突发 IO 允许更高吞吐
innodb_flush_log_at_trx_commit事务日志刷盘策略12⚠️ 1=每次提交刷盘(最安全),2=每秒刷盘(平衡)
innodb_flush_method数据文件刷盘方式fsyncO_DIRECT绕过 OS 缓存,避免双重缓存

innodb_flush_log_at_trx_commit 重要说明

值为 0:每秒写入日志并刷盘(性能最高,可能丢 1 秒数据)
值为 1:每次提交都写入并刷盘(最安全,性能最低)  ← 默认
值为 2:每次提交写入,每秒刷盘(平衡方案)        ← 当前

内存分配分析

服务器总内存:     7.7 GB
系统预留:         ~1.0 GB
其他服务:         ~3.0 GB
MySQL Buffer Pool: 2.0 GB
剩余可用:         ~1.7 GB

4.2 连接与线程

参数说明优化前优化后
max_connections最大连接数151300
thread_cache_size线程缓存932
wait_timeout非交互连接超时(秒)28800 (8h)3600 (1h)
interactive_timeout交互连接超时(秒)28800 (8h)3600 (1h)

wait_timeout 设为 3600s 可自动清理长时间空闲连接,避免连接数耗尽。

4.3 表缓存

参数说明优化前优化后
table_open_cache打开表缓存数量40004000
table_definition_cache表定义缓存20004000

当前 126 张表,4000 足够。每张表打开消耗约 4KB,总消耗约 16MB。

4.4 内存临时表与排序

参数说明优化前优化后备注
tmp_table_size内存临时表大小16M64M超过此值转为磁盘临时表
max_heap_table_sizeMEMORY 引擎表最大大小16M64M与 tmp_table_size 保持一致
sort_buffer_size排序缓冲区256K512K每个需要排序的会话分配
join_buffer_sizeJOIN 缓冲区256K512K每个需要 JOIN 的会话分配
read_buffer_size顺序读缓冲区128K256K全表扫描时使用
read_rnd_buffer_size随机读缓冲区256K512KORDER BY 后读取使用

sort_buffer_sizejoin_buffer_sizeper-session(每个连接)的,

不要设太大。当前值在 300 连接上限下最坏情况内存占用:300 × (512K+512K+256K+512K) ≈ 525MB

4.5 Binlog 日志

参数说明优化前优化后
log_bin启用二进制日志ONON
binlog_formatbinlog 格式ROWROW
sync_binlogbinlog 刷盘频率1 (每次)1 (保持)
binlog_cache_size事务 binlog 缓存32K1M
binlog_expire_logs_secondsbinlog 自动清理30天7天

binlog_expire_logs_seconds 从 30 天缩短到 7 天,减少磁盘占用。

4.6 监控与诊断

参数说明优化前优化后
slow_query_log慢查询日志开关OFFON
long_query_time慢查询阈值(秒)102
slow_query_log_file慢查询日志路径默认/var/lib/mysql/slow.log

超过 2 秒的查询会被记录,便于后续排查和优化。

4.7 其他

参数说明优化前优化后
max_allowed_packet最大数据包64M512M

导入 127MB 的 test.sql 时遇到 ERR 2006 MySQL server has gone away,就是此参数过小导致。

512M 足以应对大型 INSERT 语句。

六、完整配置文件

以下为 /data/mysql8/conf/performance.cnf 的实际内容:

[mysqld]

# ===== InnoDB 核心优化 =====
# Buffer Pool: 最重要参数,设为可用内存 60-70%
# 当前服务器 7.7G 总内存,分配 2G 给 MySQL
innodb_buffer_pool_size = 2G
innodb_buffer_pool_instances = 2

# Redo Log: 增大减少 checkpoint 频率
innodb_log_file_size = 256M
innodb_log_buffer_size = 64M

# IO 容量: SSD 推荐 1000-2000,HDD 保持 200
innodb_io_capacity = 1000
innodb_io_capacity_max = 3000

# 刷新策略: 1=每次刷盘(最安全), 2=每秒刷盘(平衡)
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT

# ===== 连接与线程 =====
thread_cache_size = 32
max_connections = 300
wait_timeout = 3600
interactive_timeout = 3600

# ===== 表缓存 =====
table_open_cache = 4000
table_definition_cache = 4000

# ===== 临时表 =====
tmp_table_size = 64M
max_heap_table_size = 64M

# ===== 排序与 JOIN 缓冲 (per-session) =====
sort_buffer_size = 512K
join_buffer_size = 512K
read_buffer_size = 256K
read_rnd_buffer_size = 512K

# ===== 慢查询日志 =====
slow_query_log = ON
long_query_time = 2
slow_query_log_file = /var/lib/mysql/slow.log

# ===== Binlog =====
binlog_cache_size = 1M
binlog_expire_logs_seconds = 604800

# ===== 其他 =====
max_allowed_packet = 512M

七、变更 innodb_log_file_size 的特殊步骤

修改 innodb_log_file_size 时,MySQL 不会自动调整已存在的 redo log 文件,

必须手动删除旧文件后重启:

# 1. 优雅关闭 MySQL (完整刷盘)
docker exec mysql8 mysql -uroot -p -e "SET GLOBAL innodb_fast_shutdown=0;"

# 2. 删除旧 redo log 文件
docker exec mysql8 rm -f /var/lib/mysql/ib_logfile*

# 3. 重启容器(会自动创建新大小的 redo log)
docker restart mysql8

跳过步骤 2 会导致 MySQL 启动失败,报错 redo log size mismatch。

八、验证优化效果

# 确认所有参数已生效
docker exec mysql8 mysql -uroot -p -e "
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'innodb_log_file_size';
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
SHOW VARIABLES LIKE 'innodb_io_capacity';
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'max_allowed_packet';
"

预期输出:

参数
innodb_buffer_pool_size2147483648 (2G)
innodb_log_file_size268435456 (256M)
innodb_flush_log_at_trx_commit2
innodb_io_capacity1000
slow_query_logON
max_allowed_packet536870912 (512M)

九、优化效果预估

指标优化前优化后
Buffer Pool 命中率~30%95%+
写入吞吐量基准值5~10 倍
Checkpoint 频率频繁低频
慢查询可见性2 秒以上自动记录
最大并发连接151300
binlog 磁盘占用30 天7 天

十、后续建议

定期查看慢查询日志

docker exec mysql8 mysqldumpslow /var/lib/mysql/slow.log

监控 Buffer Pool 命中率

SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
-- read_requests / (reads + read_requests) 应 > 95%

根据业务增长调整:如果表数据量增长到接近 2G,考虑酌情增大 innodb_buffer_pool_size

磁盘 I/O 压力大时:可提升 innodb_io_capacity 到 2000

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文