十年运维经验 MySQL 生产 20 个核心参数调优实战指南

AI 概述
本文结合十年运维经验,详解MySQL 20个核心性能参数调优,划分参数类别、给出适配不同服务器的配置模板、脚本及实战案例。同时梳理调优原则、常见误区、监控方案与最佳实践,帮助用户按需优化,规避故障。
目录
文章目录隐藏
  1. 一、概述
  2. 二、详细步骤
  3. 三、示例代码和配置
  4. 四、最佳实践和注意事项
  5. 五、故障排查和监控
  6. 六、总结
  7. 附录
  8. 结语

十年运维经验 MySQL 生产 20 个核心参数调优实战指南

一、概述

1.1 背景介绍

MySQL 参数配置是数据库性能优化的核心,多数线上性能故障、OOM 宕机、读写卡顿问题,均源于参数不合理配置。MySQL 参数繁多,但真正影响业务性能的核心参数仅有二十个左右。本文基于十年运维实战经验,聚焦 MySQL8.0 系列版本,从原理、配置、优化、案例及监控维度,系统讲解核心参数调优方案,适配主流生产业务场景。

1.2 技术特点

MySQL 架构与参数关系

                          ┌─────────────────────────────────────────────┐
                          │              MySQL Server                   │
                          │  ┌─────────────────────────────────────┐   │
                          │  │         Connection Layer             │   │
                          │  │  • max_connections                   │   │
                          │  │  • thread_cache_size                 │   │
                          │  │  • wait_timeout                      │   │
                          │  └─────────────────────────────────────┘   │
                          │  ┌─────────────────────────────────────┐   │
                          │  │          SQL Layer                   │   │
                          │  │  • sort_buffer_size                  │   │
                          │  │  • join_buffer_size                  │   │
                          │  │  • tmp_table_size                    │   │
                          │  └─────────────────────────────────────┘   │
                          │  ┌─────────────────────────────────────┐   │
                          │  │        Storage Engine (InnoDB)       │   │
                          │  │  • innodb_buffer_pool_size           │   │
                          │  │  • innodb_log_file_size              │   │
                          │  │  • innodb_flush_log_at_trx_commit    │   │
                          │  │  • innodb_io_capacity                │   │
                          │  └─────────────────────────────────────┘   │
                          └─────────────────────────────────────────────┘
                                              │
                          ┌───────────────────┴───────────────────┐
                          │            Disk I/O                    │
                          │  • Data Files  • Redo Log  • Binlog   │
                          └────────────────────────────────────────┘

参数分类

分类 参数特点 代表参数
连接类 控制客户端连接行为 max_connections, wait_timeout
内存类 控制各类缓冲区大小 innodb_buffer_pool_size, sort_buffer_size
IO 类 控制磁盘读写行为 innodb_io_capacity, sync_binlog
日志类 控制日志记录行为 innodb_log_file_size, binlog_format
复制类 控制主从复制行为 replica_parallel_workers

调优原则

  1. 理解为先:先理解参数作用,再调整数值;
  2. 一次一个:每次只调整一个参数,观察效果;
  3. 监控验证:调整后必须通过监控验证效果;
  4. 压测确认:重大调整需在测试环境压测验证;
  5. 记录变更:所有调整都要记录变更历史。

1.3 适用场景

本文的调优建议适用于:

  • MySQL 8.0.35+ 或 8.4 LTS 版本;
  • 专用数据库服务器(非混合部署);
  • 16GB 以上内存的服务器;
  • SSD/NVMe 存储环境;
  • OLTP 为主的业务场景。

对于以下场景需要特别调整:

  • OLAP 分析型场景:需要增大排序和临时表相关缓冲区;
  • 混合部署场景:需要严格限制 MySQL 内存使用;
  • HDD 机械硬盘:IO 相关参数需要保守设置。

1.4 环境要求

组件 版本要求 说明
MySQL 8.0.35+ / 8.4 LTS 推荐 8.4 LTS 长期支持版本
操作系统 Rocky 9 / Ubuntu 24.04 内核 5.14+支持 io_uring
内存 16GB+ 建议专用服务器 32GB+
磁盘 NVMe SSD IOPS > 10000
CPU 8 核+ 支持超线程
文件系统 XFS/ext4 推荐 XFS

二、详细步骤

2.1 准备工作

2.1.1 当前配置检查

调优之前,先了解当前配置状态:

-- 查看关键参数当前值
SELECT
    VARIABLE_NAME,
    VARIABLE_VALUE
FROM performance_schema.global_variables
WHERE VARIABLE_NAME IN (
    'innodb_buffer_pool_size',
    'innodb_buffer_pool_instances',
    'innodb_log_file_size',
    'innodb_log_buffer_size',
    'innodb_flush_log_at_trx_commit',
    'innodb_flush_method',
    'innodb_io_capacity',
    'innodb_io_capacity_max',
    'innodb_read_io_threads',
    'innodb_write_io_threads',
    'max_connections',
    'thread_cache_size',
    'table_open_cache',
    'sort_buffer_size',
    'join_buffer_size',
    'tmp_table_size',
    'max_heap_table_size',
    'sync_binlog',
    'binlog_cache_size'
)
ORDERBY VARIABLE_NAME;

2.1.2 硬件信息收集

#!/bin/bash
# collect_hardware_info.sh
# 收集服务器硬件信息

echo"=== MySQL 调优信息收集 ==="

echo -e "\n[CPU 信息]"
lscpu | grep -E "^(CPU\(s\)|Thread|Core|Socket|Model name)"

echo -e "\n[内存信息]"
free -h
cat /proc/meminfo | grep -E "^(MemTotal|MemFree|Cached|Buffers)"

echo -e "\n[磁盘信息]"
lsblk -d -o NAME,SIZE,ROTA,TYPE,MODEL
# ROTA=0 表示 SSD,ROTA=1 表示 HDD

echo -e "\n[磁盘 IO 能力测试]"
# 使用 fio 测试 4K 随机读写 IOPS
ifcommand -v fio &> /dev/null; then
    echo"测试 4K 随机读 IOPS..."
    fio --name=randread --ioengine=libaio --direct=1 --bs=4k \
        --iodepth=64 --rw=randread --size=1G --runtime=30 \
        --time_based --filename=/tmp/fio_test \
        2>/dev/null | grep -E "(read:|IOPS)"
fi

echo -e "\n[MySQL 数据目录磁盘使用]"
df -h /var/lib/mysql

echo -e "\n[当前 MySQL 进程内存使用]"
ps aux | grep mysqld | grep -v grep | awk '{print "RSS: " $6/1024 " MB"}'

2.1.3 性能基线记录

-- 记录调优前的性能基线

-- 1. InnoDB 缓冲池命中率
SELECT
    (1 - (
        (SELECT VARIABLE_VALUE FROM performance_schema.global_status
         WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') /
        (SELECT VARIABLE_VALUE FROM performance_schema.global_status
         WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests')
    )) * 100AS buffer_pool_hit_rate;

-- 2. 临时表使用情况
SELECT
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Created_tmp_disk_tables') /
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Created_tmp_tables') * 100
    AS tmp_disk_table_pct;

-- 3. 连接使用情况
SELECT
    @@max_connections AS max_conn,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Max_used_connections') AS max_used,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Threads_connected') AS current_conn;

-- 4. 慢查询数量
SELECT VARIABLE_VALUE AS slow_queries
FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Slow_queries';

-- 5. 排序和连接相关
SELECT
    gs1.VARIABLE_VALUE AS sort_merge_passes,
    gs2.VARIABLE_VALUE AS select_full_join
FROM performance_schema.global_status gs1,
     performance_schema.global_status gs2
WHERE gs1.VARIABLE_NAME = 'Sort_merge_passes'
AND gs2.VARIABLE_NAME = 'Select_full_join';

2.2 核心配置

2.2.1 InnoDB 核心参数(参数 1-8)

参数 1: innodb_buffer_pool_size

这是 MySQL 最重要的参数,没有之一。InnoDB 缓冲池用于缓存数据页和索引页,缓冲池越大,能缓存的数据越多,磁盘 IO 越少,性能越好。

# 计算公式:物理内存 * 0.7 ~ 0.8(专用服务器)
# 示例:64GB 内存服务器
innodb_buffer_pool_size = 48G

取值依据

内存大小 推荐值 说明
8GB 5-6GB 留 2-3GB 给系统和其他进程
16GB 10-12GB 约 70-75%
32GB 22-25GB 约 70-80%
64GB 45-50GB 约 70-80%
128GB 96-100GB 约 75-80%

验证效果

-- 查看缓冲池命中率(应 > 99%)
SHOWGLOBALSTATUSLIKE'Innodb_buffer_pool_read%';

-- 计算命中率
SELECT
    ROUND((1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100, 2) AS hit_rate
FROM (
    SELECT
        SUM(CASEWHEN VARIABLE_NAME = 'Innodb_buffer_pool_reads'THENCAST(VARIABLE_VALUE ASUNSIGNED) END) AS Innodb_buffer_pool_reads,
        SUM(CASEWHEN VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'THENCAST(VARIABLE_VALUE ASUNSIGNED) END) AS Innodb_buffer_pool_read_requests
    FROM performance_schema.global_status
    WHERE VARIABLE_NAME IN ('Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests')
) AS stats;

参数 2: innodb_buffer_pool_instances

将缓冲池分成多个实例,减少多线程并发访问时的锁竞争。

# 当 buffer_pool_size > 1GB 时设置
# 建议:每个实例 1-2GB,最大不超过 64
innodb_buffer_pool_instances = 8

# MySQL 8.0 默认已经自动设置合理值
# 当 buffer_pool_size < 1GB 时,自动设为 1

参数 3: innodb_log_file_size

InnoDB 重做日志文件大小。更大的日志文件可以减少 checkpoint 频率,提高写入性能,但会增加崩溃恢复时间。

# MySQL 8.0.30+ 使用 innodb_redo_log_capacity 替代
# innodb_redo_log_capacity = innodb_log_file_size * innodb_log_files_in_group * 2

# MySQL 8.0.30 之前的配置
innodb_log_file_size = 2G
innodb_log_files_in_group = 2

# MySQL 8.0.30+的配置
innodb_redo_log_capacity = 8G  # 相当于之前的 4 个 2G 日志文件

取值依据

-- 计算 1 小时内产生的日志量,以此为基准设置
SHOWENGINEINNODBSTATUS\G
-- 查看 Log sequence number 和 Log flushed up to
-- 一小时前后的差值就是每小时日志量

-- 建议设置为能容纳 1 小时日志量的 2-4 倍
-- 典型值范围:1GB - 8GB

参数 4: innodb_log_buffer_size

重做日志缓冲区大小。事务在提交前先写入日志缓冲区,然后刷盘。

# 默认 16MB,大事务场景可适当增大
innodb_log_buffer_size = 64M

什么时候需要增大

-- 查看日志缓冲区等待次数
SHOWGLOBALSTATUSLIKE'Innodb_log_waits';

-- 如果 Innodb_log_waits > 0,说明日志缓冲区不够
-- 需要增大 innodb_log_buffer_size

参数 5: innodb_flush_log_at_trx_commit

这是性能与数据安全的平衡参数,控制 redo log 的刷盘策略。

# 取值说明:
# 0: 每秒刷盘,事务提交时不刷盘(最快,可能丢 1 秒数据)
# 1: 每次提交都刷盘(最安全,性能最差)
# 2: 每次提交写 OS 缓存,每秒刷盘(折中方案)

# 生产环境推荐
innodb_flush_log_at_trx_commit = 1  # 金融等对数据安全要求极高的场景

# 或
innodb_flush_log_at_trx_commit = 2  # 一般业务场景,OS 崩溃时可能丢 1 秒数据

性能对比

取值 数据安全性 写入 TPS 适用场景
0 低(可能丢 1 秒) 最高 测试环境
1 最高 较低 金融、订单系统
2 中(OS 崩溃丢 1 秒) 较高 一般业务

参数 6: innodb_flush_method

控制 InnoDB 数据文件和日志文件的刷盘方法。

# Linux 环境推荐 O_DIRECT
# 避免双重缓存(InnoDB 缓冲池 + OS Page Cache)
innodb_flush_method = O_DIRECT

# MySQL 8.0 默认值
# 如果使用 SSD,O_DIRECT 是最佳选择

取值说明

方法 数据文件 日志文件 适用场景
fsync 系统默认 系统默认 传统 HDD
O_DIRECT 绕过 OS 缓存 fsync SSD,推荐
O_DIRECT_NO_FSYNC 绕过缓存,无 fsync 无 fsync 高性能 SSD

参数 7: innodb_io_capacity

告诉 InnoDB 后台任务可用的 IOPS 能力。

# 根据磁盘类型设置
# HDD: 200-800
# SSD: 5000-20000
# NVMe: 20000-50000

innodb_io_capacity = 10000      # 普通 SSD
innodb_io_capacity_max = 20000  # 最大值

如何确定正确的值?

# 1. 使用 fio 测试磁盘 IOPS
fio --name=randrw --ioengine=libaio --direct=1 --bs=16k \
    --iodepth=64 --rw=randrw --rwmixread=70 --size=1G \
    --runtime=60 --time_based --filename=/var/lib/mysql/fio_test

# 2. 设置 io_capacity 为测试结果的 50-75%
# 3. 设置 io_capacity_max 为测试结果的 100%

参数 8: innodb_read_io_threads / innodb_write_io_threads

控制 InnoDB 后台 IO 线程数量。

# 默认值都是 4
# SSD 环境可以适当增加
innodb_read_io_threads = 8
innodb_write_io_threads = 8

# 计算依据:
# 读线程:处理预读和从磁盘读取数据
# 写线程:处理脏页刷盘
# 一般设置为 CPU 核心数的一半

2.2.2 连接与线程参数(参数 9-12)

参数 9: max_connections

允许的最大并发连接数。

# 不要设置过大!每个连接都会消耗内存
# 计算公式:预期并发用户数 * 1.5

# 根据应用规模设置
max_connections = 500  # 中小型应用
# max_connections = 2000  # 大型应用

设置过大的危害

-- 每个连接的内存消耗(近似)
-- 基础消耗 + sort_buffer + join_buffer + read_buffer + ...
-- 约 2-10MB per connection

-- 假设 max_connections = 10000,sort_buffer = 4MB
-- 最坏情况内存消耗:10000 * 4MB = 40GB
-- 可能导致 OOM

合理设置

-- 查看历史最大连接数
SHOWGLOBALSTATUSLIKE'Max_used_connections';
SHOWGLOBALSTATUSLIKE'Max_used_connections_time';

-- 建议设置为历史最大值的 1.5-2 倍
-- 但不要超过实际需要

参数 10: thread_cache_size

线程缓存大小,用于复用已创建的线程。

# 计算公式:max_connections / 10,最大不超过 100
thread_cache_size = 50

验证效果

-- 查看线程创建情况
SHOWGLOBALSTATUSLIKE'Threads_%';

-- 线程缓存命中率计算
-- 命中率 = 1 - (Threads_created / Connections)
-- 应该 > 99%

参数 11: wait_timeout / interactive_timeout

连接空闲超时时间。

# 非交互连接的超时时间(应用程序连接)
wait_timeout = 600  # 10 分钟

# 交互连接的超时时间(mysql 客户端)
interactive_timeout = 3600  # 1 小时

设置依据

场景 推荐值 说明
Web 应用 60-300 秒 短连接或连接池
后台服务 600-3600 秒 长连接
批处理任务 3600-28800 秒 可能长时间运行

参数 12: table_open_cache

表文件描述符缓存。

# 计算公式:max_connections * 每个连接平均使用表数
# 一般设置为:max_connections * 2 或更多

table_open_cache = 4000
table_open_cache_instances = 16  # 分成多个实例减少锁竞争

验证效果

-- 查看表缓存状态
SHOWGLOBALSTATUSLIKE'Open%tables%';
SHOWGLOBALSTATUSLIKE'Table_open_cache%';

-- Opened_tables 增长过快说明 table_open_cache 太小

2.2.3 内存相关参数(参数 13-16)

参数 13: sort_buffer_size

排序缓冲区大小,用于 ORDER BY、GROUP BY 操作。

# 这是 per-session 的参数,不要设置太大
# 每个需要排序的查询都会分配一个

sort_buffer_size = 2M  # 推荐值
# 最大不要超过 8M,否则可能触发 swap

常见误区

-- 错误:设置太大
-- sort_buffer_size = 256M  # 危险!

-- 假设同时有 100 个排序查询
-- 内存消耗:100 * 256M = 25.6GB
-- 可能导致 OOM 或大量 swap

参数 14: join_buffer_size

连接缓冲区大小,用于没有索引的 JOIN 操作。

# 同样是 per-session 参数
join_buffer_size = 2M

# 注意:如果 JOIN 有索引,这个参数基本不起作用
# 优化的方向应该是添加合适的索引

参数 15: tmp_table_size / max_heap_table_size

内存临时表的最大大小。两个参数取较小值生效。

# 两个参数应该设置相同
tmp_table_size = 64M
max_heap_table_size = 64M

验证效果

-- 查看临时表使用情况
SHOWGLOBALSTATUSLIKE'Created_tmp%';

-- 磁盘临时表比例 = Created_tmp_disk_tables / Created_tmp_tables
-- 应该 < 25%,否则需要增大 tmp_table_size
-- 或者优化查询,减少临时表使用

参数 16: read_buffer_size / read_rnd_buffer_size

顺序读和随机读缓冲区。

# 顺序读缓冲区(全表扫描时使用)
read_buffer_size = 1M

# 随机读缓冲区(ORDER BY 时使用)
read_rnd_buffer_size = 1M

# 这两个参数也是 per-session 的
# 默认值 128K-256K 对大多数场景足够
# 只有确认有大量全表扫描时才需要增大

2.2.4 日志与复制参数(参数 17-20)

参数 17: binlog_cache_size

二进制日志缓冲区大小。

# 用于缓存事务的 binlog
# 事务过大时会使用临时文件

binlog_cache_size = 4M

# 查看是否需要增大
SHOW GLOBAL STATUS LIKE 'Binlog_cache%';
-- Binlog_cache_disk_use > 0 说明需要增大

参数 18: sync_binlog

binlog 刷盘策略。

# 0: 依赖 OS 刷盘(最快,不安全)
# 1: 每次提交都刷盘(最安全,较慢)
# N: 每 N 个事务刷盘一次

# 生产环境推荐
sync_binlog = 1

# 与 innodb_flush_log_at_trx_commit 配合
# 双 1 配置(sync_binlog=1, innodb_flush_log_at_trx_commit=1)
# 是最安全的配置,但性能较差

性能与安全的权衡

innodb_flush_log_at_trx_commit sync_binlog 安全性 性能
1 1 最高 最低
2 1 中等
2 100 较高
0 0 最高

参数 19: binlog_expire_logs_seconds

binlog 自动清理时间(MySQL 8.0+替代 expire_logs_days)。

# 保留 7 天的 binlog
binlog_expire_logs_seconds = 604800  # 7 * 24 * 3600

# 考虑因素:
# 1. 从库可能的最大延迟时间
# 2. 数据恢复的时间点需求
# 3. 磁盘空间限制

参数 20: replica_parallel_workers(MySQL 8.0.26+)

从库并行复制 Worker 数量。

# 启用并行复制
replica_parallel_type = LOGICAL_CLOCK
replica_parallel_workers = 16  # CPU 核心数的 1-2 倍
replica_preserve_commit_order = ON

# 主库配合配置
binlog_transaction_dependency_tracking = WRITESET
transaction_write_set_extraction = XXHASH64

2.3 启动和验证

2.3.1 配置文件示例

# /etc/mysql/mysql.conf.d/optimized.cnf
# MySQL 8.0 优化配置模板
# 适用于:64GB 内存,NVMe SSD,16 核 CPU

[mysqld]
# ===================
# 基础配置
# ===================
server_id = 1
port = 3306
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
tmpdir = /tmp

# 字符集
character_set_server = utf8mb4
collation_server = utf8mb4_unicode_ci

# ===================
# InnoDB 核心参数
# ===================
# 参数 1: 缓冲池 - 64GB * 0.75 ≈ 48GB
innodb_buffer_pool_size = 48G

# 参数 2: 缓冲池实例 - 每实例约 6GB
innodb_buffer_pool_instances = 8

# 参数 3: 重做日志容量 (MySQL 8.0.30+)
innodb_redo_log_capacity = 8G

# 参数 4: 日志缓冲区
innodb_log_buffer_size = 64M

# 参数 5: 日志刷盘策略
innodb_flush_log_at_trx_commit = 2  # 生产可用 1

# 参数 6: 刷盘方法
innodb_flush_method = O_DIRECT

# 参数 7: IO 能力
innodb_io_capacity = 10000
innodb_io_capacity_max = 20000

# 参数 8: IO 线程
innodb_read_io_threads = 8
innodb_write_io_threads = 8

# InnoDB 其他优化
innodb_file_per_table = ON
innodb_stats_on_metadata = OFF
innodb_spin_wait_delay = 6
innodb_lock_wait_timeout = 50
innodb_print_all_deadlocks = ON

# ===================
# 连接与线程参数
# ===================
# 参数 9: 最大连接数
max_connections = 500

# 参数 10: 线程缓存
thread_cache_size = 50

# 参数 11: 连接超时
wait_timeout = 600
interactive_timeout = 3600

# 参数 12: 表缓存
table_open_cache = 4000
table_open_cache_instances = 16
table_definition_cache = 2000

# ===================
# 内存相关参数
# ===================
# 参数 13: 排序缓冲区
sort_buffer_size = 2M

# 参数 14: 连接缓冲区
join_buffer_size = 2M

# 参数 15: 临时表大小
tmp_table_size = 64M
max_heap_table_size = 64M

# 参数 16: 读缓冲区
read_buffer_size = 1M
read_rnd_buffer_size = 1M

# ===================
# 日志与复制参数
# ===================
# 参数 17: binlog 缓冲区
binlog_cache_size = 4M

# 参数 18: binlog 刷盘
sync_binlog = 1

# 参数 19: binlog 保留
binlog_expire_logs_seconds = 604800

# binlog 配置
log_bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL

# 参数 20: 并行复制(从库配置)
# replica_parallel_type = LOGICAL_CLOCK
# replica_parallel_workers = 16
# replica_preserve_commit_order = ON

# ===================
# 日志配置
# ===================
log_error = /var/log/mysql/error.log
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = ON

# ===================
# 性能 Schema
# ===================
performance_schema = ON
performance_schema_instrument = '%=ON'

2.3.2 配置验证脚本

#!/bin/bash
# verify_mysql_config.sh
# MySQL 配置验证脚本

MYSQL_USER="root"
MYSQL_PASS="your_password"

echo"=== MySQL 配置验证 ==="

# 1. 验证 InnoDB 缓冲池
echo -e "\n[1] InnoDB 缓冲池配置"
mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "
SELECT
    CONCAT(ROUND(@@innodb_buffer_pool_size/1024/1024/1024, 1), 'G') AS buffer_pool_size,
    @@innodb_buffer_pool_instances AS instances,
    CONCAT(ROUND(@@innodb_buffer_pool_size/@@innodb_buffer_pool_instances/1024/1024/1024, 2), 'G') AS per_instance
"

# 2. 验证缓冲池命中率
echo -e "\n[2] 缓冲池命中率"
mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "
SELECT
    CONCAT(
        ROUND((1 - SUM(CASE WHEN VARIABLE_NAME = 'Innodb_buffer_pool_reads' THEN CAST(VARIABLE_VALUE AS DECIMAL) END) /
               SUM(CASE WHEN VARIABLE_NAME = 'Innodb_buffer_pool_read_requests' THEN CAST(VARIABLE_VALUE AS DECIMAL) END)) * 100, 2),
        '%'
    ) AS hit_rate
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN ('Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests')
"

# 3. 验证连接配置
echo -e "\n[3] 连接配置"
mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "
SELECT
    @@max_connections AS max_conn,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Max_used_connections') AS max_used,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Threads_connected') AS current
"

# 4. 验证临时表情况
echo -e "\n[4] 临时表使用情况"
mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "
SELECT
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Created_tmp_tables') AS tmp_tables,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Created_tmp_disk_tables') AS disk_tables,
    CONCAT(
        ROUND((SELECT CAST(VARIABLE_VALUE AS DECIMAL) FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Created_tmp_disk_tables') /
              (SELECT CAST(VARIABLE_VALUE AS DECIMAL) FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Created_tmp_tables') * 100, 2),
        '%'
    ) AS disk_pct
"

# 5. 验证 IO 配置
echo -e "\n[5] IO 配置"
mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "
SELECT
    @@innodb_io_capacity AS io_capacity,
    @@innodb_io_capacity_max AS io_max,
    @@innodb_read_io_threads AS read_threads,
    @@innodb_write_io_threads AS write_threads
"

echo -e "\n 验证完成!"

三、示例代码和配置

3.1 完整配置示例

3.1.1 不同规格服务器配置模板

小型服务器(8GB 内存,4 核 CPU)

# /etc/mysql/mysql.conf.d/small.cnf
# 适用于:8GB 内存,4 核 CPU,SSD

[mysqld]
# InnoDB
innodb_buffer_pool_size = 5G
innodb_buffer_pool_instances = 4
innodb_redo_log_capacity = 2G
innodb_log_buffer_size = 16M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_read_io_threads = 4
innodb_write_io_threads = 4

# 连接
max_connections = 200
thread_cache_size = 20
table_open_cache = 1000

# 内存
sort_buffer_size = 1M
join_buffer_size = 1M
tmp_table_size = 32M
max_heap_table_size = 32M

# 日志
binlog_cache_size = 2M
sync_binlog = 1

中型服务器(32GB 内存,8 核 CPU)

# /etc/mysql/mysql.conf.d/medium.cnf
# 适用于:32GB 内存,8 核 CPU,SSD

[mysqld]
# InnoDB
innodb_buffer_pool_size = 24G
innodb_buffer_pool_instances = 8
innodb_redo_log_capacity = 4G
innodb_log_buffer_size = 32M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
innodb_io_capacity = 5000
innodb_io_capacity_max = 10000
innodb_read_io_threads = 8
innodb_write_io_threads = 8

# 连接
max_connections = 500
thread_cache_size = 50
table_open_cache = 2000

# 内存
sort_buffer_size = 2M
join_buffer_size = 2M
tmp_table_size = 64M
max_heap_table_size = 64M

# 日志
binlog_cache_size = 4M
sync_binlog = 1

大型服务器(128GB 内存,32 核 CPU)

# /etc/mysql/mysql.conf.d/large.cnf
# 适用于:128GB 内存,32 核 CPU,NVMe SSD

[mysqld]
# InnoDB
innodb_buffer_pool_size = 96G
innodb_buffer_pool_instances = 16
innodb_redo_log_capacity = 16G
innodb_log_buffer_size = 128M
innodb_flush_log_at_trx_commit = 1  # 大型系统通常对数据安全要求高
innodb_flush_method = O_DIRECT
innodb_io_capacity = 20000
innodb_io_capacity_max = 40000
innodb_read_io_threads = 16
innodb_write_io_threads = 16

# 连接
max_connections = 2000
thread_cache_size = 100
table_open_cache = 8000
table_open_cache_instances = 32

# 内存
sort_buffer_size = 4M
join_buffer_size = 4M
tmp_table_size = 128M
max_heap_table_size = 128M

# 日志
binlog_cache_size = 8M
sync_binlog = 1

# 其他优化
innodb_page_cleaners = 8
innodb_purge_threads = 8
innodb_lru_scan_depth = 2048

3.1.2 动态参数调整脚本

#!/usr/bin/env python3
# dynamic_tuning.py
# MySQL 动态参数调优脚本

import pymysql
from dataclasses import dataclass
from typing import Dict, List, Tuple
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


@dataclass
classTuningRecommendation:
    """调优建议"""
    parameter: str
    current_value: str
    recommended_value: str
    reason: str
    is_dynamic: bool


classMySQLTuningAdvisor:
    """MySQL 调优顾问"""

    def__init__(self, host: str, user: str, password: str, port: int = 3306):
        self.connection = pymysql.connect(
            host=host,
            user=user,
            password=password,
            port=port,
            cursorclass=pymysql.cursors.DictCursor
        )

    defanalyze(self) -> List[TuningRecommendation]:
        """分析并生成调优建议"""
        recommendations = []

        # 分析各项指标
        recommendations.extend(self._analyze_buffer_pool())
        recommendations.extend(self._analyze_connections())
        recommendations.extend(self._analyze_temp_tables())
        recommendations.extend(self._analyze_threads())
        recommendations.extend(self._analyze_io())

        return recommendations

    def_get_variable(self, name: str) -> str:
        """获取变量值"""
        with self.connection.cursor() as cursor:
            cursor.execute(f"SHOW VARIABLES LIKE '{name}'")
            result = cursor.fetchone()
            return result['Value'] if result elseNone

    def_get_status(self, name: str) -> int:
        """获取状态值"""
        with self.connection.cursor() as cursor:
            cursor.execute(f"SHOW GLOBAL STATUS LIKE '{name}'")
            result = cursor.fetchone()
            return int(result['Value']) if result else0

    def_analyze_buffer_pool(self) -> List[TuningRecommendation]:
        """分析缓冲池"""
        recommendations = []

        reads = self._get_status('Innodb_buffer_pool_reads')
        read_requests = self._get_status('Innodb_buffer_pool_read_requests')

        if read_requests > 0:
            hit_rate = (1 - reads / read_requests) * 100

            if hit_rate < 99:
                current_size = int(self._get_variable('innodb_buffer_pool_size'))
                recommended_size = int(current_size * 1.5)

                recommendations.append(TuningRecommendation(
                    parameter='innodb_buffer_pool_size',
                    current_value=self._format_bytes(current_size),
                    recommended_value=self._format_bytes(recommended_size),
                    reason=f'缓冲池命中率 {hit_rate:.2f}% 低于 99%',
                    is_dynamic=True# MySQL 8.0 支持动态调整
                ))

        return recommendations

    def_analyze_connections(self) -> List[TuningRecommendation]:
        """分析连接数"""
        recommendations = []

        max_conn = int(self._get_variable('max_connections'))
        max_used = self._get_status('Max_used_connections')
        usage_rate = max_used / max_conn * 100

        if usage_rate > 80:
            recommendations.append(TuningRecommendation(
                parameter='max_connections',
                current_value=str(max_conn),
                recommended_value=str(int(max_used * 1.5)),
                reason=f'连接使用率 {usage_rate:.1f}% 超过 80%',
                is_dynamic=True
            ))
        elif usage_rate < 20and max_conn > 200:
            recommendations.append(TuningRecommendation(
                parameter='max_connections',
                current_value=str(max_conn),
                recommended_value=str(max(200, int(max_used * 2))),
                reason=f'连接使用率仅 {usage_rate:.1f}%,max_connections 设置过大',
                is_dynamic=True
            ))

        return recommendations

    def_analyze_temp_tables(self) -> List[TuningRecommendation]:
        """分析临时表"""
        recommendations = []

        tmp_tables = self._get_status('Created_tmp_tables')
        tmp_disk_tables = self._get_status('Created_tmp_disk_tables')

        if tmp_tables > 0:
            disk_ratio = tmp_disk_tables / tmp_tables * 100

            if disk_ratio > 25:
                current_size = int(self._get_variable('tmp_table_size'))
                recommended_size = min(current_size * 2, 256 * 1024 * 1024)  # 最大 256MB

                recommendations.append(TuningRecommendation(
                    parameter='tmp_table_size',
                    current_value=self._format_bytes(current_size),
                    recommended_value=self._format_bytes(recommended_size),
                    reason=f'磁盘临时表比例 {disk_ratio:.1f}% 超过 25%',
                    is_dynamic=True
                ))

        return recommendations

    def_analyze_threads(self) -> List[TuningRecommendation]:
        """分析线程缓存"""
        recommendations = []

        threads_created = self._get_status('Threads_created')
        connections = self._get_status('Connections')

        if connections > 0:
            thread_cache_hit_rate = (1 - threads_created / connections) * 100

            if thread_cache_hit_rate < 99:
                current_cache = int(self._get_variable('thread_cache_size'))
                recommendations.append(TuningRecommendation(
                    parameter='thread_cache_size',
                    current_value=str(current_cache),
                    recommended_value=str(min(current_cache + 20, 100)),
                    reason=f'线程缓存命中率 {thread_cache_hit_rate:.1f}% 低于 99%',
                    is_dynamic=True
                ))

        return recommendations

    def_analyze_io(self) -> List[TuningRecommendation]:
        """分析 IO 配置"""
        recommendations = []

        # 检查日志等待
        log_waits = self._get_status('Innodb_log_waits')
        if log_waits > 0:
            current_size = int(self._get_variable('innodb_log_buffer_size'))
            recommendations.append(TuningRecommendation(
                parameter='innodb_log_buffer_size',
                current_value=self._format_bytes(current_size),
                recommended_value=self._format_bytes(current_size * 2),
                reason=f'日志缓冲区等待次数 {log_waits}',
                is_dynamic=False# 需要重启
            ))

        return recommendations

    @staticmethod
    def_format_bytes(size: int) -> str:
        """格式化字节数"""
        for unit in ['B', 'K', 'M', 'G']:
            if size < 1024:
                returnf"{size}{unit}"
            size //= 1024
        returnf"{size}T"

    defapply_recommendations(
        self,
        recommendations: List[TuningRecommendation],
        dynamic_only: bool = True
    ) -> Dict[str, bool]:
        """应用调优建议"""
        results = {}

        for rec in recommendations:
            if dynamic_only andnot rec.is_dynamic:
                logger.info(f"跳过非动态参数: {rec.parameter}")
                results[rec.parameter] = False
                continue

            try:
                with self.connection.cursor() as cursor:
                    cursor.execute(
                        f"SET GLOBAL {rec.parameter} = {rec.recommended_value}"
                    )
                    self.connection.commit()
                    logger.info(
                        f"成功调整 {rec.parameter}: "
                        f"{rec.current_value} -> {rec.recommended_value}"
                    )
                    results[rec.parameter] = True
            except Exception as e:
                logger.error(f"调整 {rec.parameter} 失败: {e}")
                results[rec.parameter] = False

        return results

    defgenerate_report(self, recommendations: List[TuningRecommendation]) -> str:
        """生成调优报告"""
        report = []
        report.append("=" * 60)
        report.append("MySQL 参数调优建议报告")
        report.append("=" * 60)

        ifnot recommendations:
            report.append("当前配置良好,无需调整")
        else:
            report.append(f"发现 {len(recommendations)} 项调优建议:\n")

            dynamic_params = [r for r in recommendations if r.is_dynamic]
            static_params = [r for r in recommendations ifnot r.is_dynamic]

            if dynamic_params:
                report.append("【可动态调整的参数】")
                for i, rec in enumerate(dynamic_params, 1):
                    report.append(f"\n{i}. {rec.parameter}")
                    report.append(f"   当前值: {rec.current_value}")
                    report.append(f"   建议值: {rec.recommended_value}")
                    report.append(f"   原因: {rec.reason}")

            if static_params:
                report.append("\n【需要重启生效的参数】")
                for i, rec in enumerate(static_params, 1):
                    report.append(f"\n{i}. {rec.parameter}")
                    report.append(f"   当前值: {rec.current_value}")
                    report.append(f"   建议值: {rec.recommended_value}")
                    report.append(f"   原因: {rec.reason}")

        return"\n".join(report)


if __name__ == "__main__":
    advisor = MySQLTuningAdvisor(
        host="127.0.0.1",
        user="root",
        password="your_password"
    )

    recommendations = advisor.analyze()
    report = advisor.generate_report(recommendations)
    print(report)

    # 可选:应用动态参数调整
    # results = advisor.apply_recommendations(recommendations, dynamic_only=True)

3.2 实际应用案例

3.2.1 案例一:电商大促前的参数调优

场景描述

某电商平台即将迎来双 11 大促,预计流量是平时的 20 倍。当前配置是根据日常负载设置的,需要针对大促场景进行调优。

当前配置

# 日常配置
innodb_buffer_pool_size = 24G
max_connections = 500
innodb_io_capacity = 2000
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1

优化方案

# 大促期间配置

# 1. 增大缓冲池应对更多并发读取
innodb_buffer_pool_size = 48G  # 从 24G 增加到 48G

# 2. 增大连接数
max_connections = 2000  # 从 500 增加到 2000

# 3. 提升 IO 能力
innodb_io_capacity = 10000  # 从 2000 增加到 10000
innodb_io_capacity_max = 20000

# 4. 牺牲少量安全性换取性能
sync_binlog = 100  # 从 1 改为 100
innodb_flush_log_at_trx_commit = 2  # 从 1 改为 2

# 5. 增大临时表大小应对复杂查询
tmp_table_size = 128M
max_heap_table_size = 128M

# 6. 增大排序缓冲区
sort_buffer_size = 4M
join_buffer_size = 4M

动态调整脚本

#!/bin/bash
# promotion_tuning.sh
# 大促参数切换脚本

MYSQL_CMD="mysql -u root -p'password'"

echo"开始应用大促配置..."

# 动态调整参数
$MYSQL_CMD << EOF
-- 增大连接数
SET GLOBAL max_connections = 2000;

-- 调整 IO 参数
SET GLOBAL innodb_io_capacity = 10000;
SET GLOBAL innodb_io_capacity_max = 20000;

-- 放宽刷盘策略
SET GLOBAL sync_binlog = 100;
SET GLOBAL innodb_flush_log_at_trx_commit = 2;

-- 增大会话缓冲区
SET GLOBAL sort_buffer_size = 4*1024*1024;
SET GLOBAL join_buffer_size = 4*1024*1024;
SET GLOBAL tmp_table_size = 128*1024*1024;
SET GLOBAL max_heap_table_size = 128*1024*1024;

-- 验证
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'innodb_io_capacity';
SHOW VARIABLES LIKE 'sync_binlog';
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
EOF

echo"大促配置应用完成!"
echo"注意:innodb_buffer_pool_size 需要在线扩容或重启"

大促后恢复

#!/bin/bash
# restore_normal.sh
# 恢复日常配置

MYSQL_CMD="mysql -u root -p'password'"

echo"恢复日常配置..."

$MYSQL_CMD << EOF
SET GLOBAL max_connections = 500;
SET GLOBAL innodb_io_capacity = 2000;
SET GLOBAL innodb_io_capacity_max = 4000;
SET GLOBAL sync_binlog = 1;
SET GLOBAL innodb_flush_log_at_trx_commit = 1;
SET GLOBAL sort_buffer_size = 2*1024*1024;
SET GLOBAL join_buffer_size = 2*1024*1024;
SET GLOBAL tmp_table_size = 64*1024*1024;
SET GLOBAL max_heap_table_size = 64*1024*1024;
EOF

echo"日常配置恢复完成!"

3.2.2 案例二:慢查询导致的性能问题排查

问题现象

数据库 CPU 持续在 80%以上,慢查询数量增加,业务响应变慢。

排查步骤

-- 1. 查看当前运行的查询
SELECT
    id,
    user,
    host,
    db,
    command,
    time,
    state,
    LEFT(info, 100) ASquery
FROM information_schema.processlist
WHERE command != 'Sleep'
ORDERBYtimeDESC;

-- 2. 分析慢查询日志
-- 使用 pt-query-digest 分析

-- 3. 查看资源消耗最高的语句
SELECT
    DIGEST_TEXT,
    COUNT_STAR AS exec_count,
    SUM_TIMER_WAIT/1000000000000AS total_sec,
    AVG_TIMER_WAIT/1000000000AS avg_ms,
    SUM_ROWS_EXAMINED AS rows_examined
FROM performance_schema.events_statements_summary_by_digest
ORDERBY SUM_TIMER_WAIT DESC
LIMIT10;

发现问题

-- 发现大量全表扫描查询
SELECT * FROM orders WHEREstatus = 'pending'
-- 该表有 500 万行,status 字段没有索引

优化方案

-- 1. 添加索引
CREATEINDEX idx_status ON orders(status);

-- 2. 临时增大 sort_buffer_size 应对积压
SETGLOBAL sort_buffer_size = 8*1024*1024;

-- 3. 增大 read_buffer_size 缓解全表扫描
SETGLOBAL read_buffer_size = 2*1024*1024;

参数调优建议

# 针对读多写少场景
read_buffer_size = 2M
read_rnd_buffer_size = 2M

# 针对复杂查询场景
sort_buffer_size = 4M
join_buffer_size = 4M
tmp_table_size = 128M
max_heap_table_size = 128M

3.2.3 案例三:从库延迟优化

问题现象

从库复制延迟持续在几百秒,影响读写分离架构。

排查命令

-- 查看从库状态
SHOW REPLICA STATUS\G

-- 关键指标
-- Seconds_Behind_Source: 延迟秒数
-- Relay_Log_Space: 中继日志大小
-- Replica_SQL_Running_State: SQL 线程状态

发现问题

从库使用单线程复制,无法充分利用多核 CPU。

优化方案

-- 停止复制
STOP REPLICA;

-- 配置并行复制
SETGLOBAL replica_parallel_type = 'LOGICAL_CLOCK';
SETGLOBAL replica_parallel_workers = 16;
SETGLOBAL replica_preserve_commit_order = ON;

-- 启动复制
START REPLICA;

从库专用配置

# /etc/mysql/mysql.conf.d/replica.cnf
# 从库专用优化配置

[mysqld]
# 并行复制
replica_parallel_type = LOGICAL_CLOCK
replica_parallel_workers = 16
replica_preserve_commit_order = ON

# 从库可以放宽刷盘策略
innodb_flush_log_at_trx_commit = 2
sync_binlog = 0

# 增大 IO 能力加快回放
innodb_io_capacity = 15000
innodb_io_capacity_max = 30000

# 从库不需要二进制日志(除非级联复制)
# skip_log_bin

# 只读
read_only = ON
super_read_only = ON

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 参数调优黄金法则

法则 1:先测量后调优

# 使用 sysbench 进行基准测试
sysbench oltp_read_write \
    --mysql-host=127.0.0.1 \
    --mysql-user=test \
    --mysql-password=test \
    --mysql-db=sbtest \
    --tables=10 \
    --table-size=1000000 \
    --threads=32 \
    --time=300 \
    --report-interval=10 \
    prepare

sysbench oltp_read_write \
    --mysql-host=127.0.0.1 \
    --mysql-user=test \
    --mysql-password=test \
    --mysql-db=sbtest \
    --tables=10 \
    --table-size=1000000 \
    --threads=32 \
    --time=300 \
    --report-interval=10 \
    run

法则 2:单参数调整

每次只调整一个参数,观察效果后再调整下一个。

法则 3:记录所有变更

-- 创建参数变更记录表
CREATETABLE param_change_log (
    idINT PRIMARY KEY AUTO_INCREMENT,
    changed_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP,
    parameter VARCHAR(100),
    old_value VARCHAR(100),
    new_value VARCHAR(100),
    changed_by VARCHAR(50),
    reason VARCHAR(500)
);

-- 记录变更
INSERTINTO param_change_log (parameter, old_value, new_value, changed_by, reason)
VALUES ('innodb_buffer_pool_size', '24G', '48G', 'dba_john', '大促扩容');

4.1.2 内存参数配置原则

总可用内存 = 物理内存 * 90%(留 10%给 OS)

内存分配 =
  innodb_buffer_pool_size (60-70%)
  + max_connections * per_connection_memory (10-20%)
  + 其他缓冲区 (5-10%)
  + OS 和其他进程 (10%)

per_connection_memory 计算

-- 每个连接的最大内存消耗
SELECT
    (@@sort_buffer_size + @@join_buffer_size + @@read_buffer_size +
     @@read_rnd_buffer_size + @@binlog_cache_size) / 1024 / 1024AS per_conn_mb;

-- 最坏情况总消耗
SELECT
    @@max_connections *
    (@@sort_buffer_size + @@join_buffer_size + @@read_buffer_size +
     @@read_rnd_buffer_size + @@binlog_cache_size) / 1024 / 1024 / 1024AS max_session_memory_gb;

4.1.3 IO 参数配置原则

存储类型 innodb_io_capacity innodb_io_capacity_max
HDD 7200RPM 200 400
HDD 15000RPM 400-800 1000
SSD SATA 2000-5000 5000-10000
SSD NVMe 10000-50000 20000-100000
云盘 SSD 根据规格 根据规格

4.2 注意事项

4.2.1 常见错误

错误配置 问题 正确做法
innodb_buffer_pool_size = 物理内存 OOM 设为物理内存的 70-80%
max_connections = 10000 内存耗尽 根据实际需要设置
sort_buffer_size = 256M 单会话内存过大 设为 1-4M
innodb_log_file_size 太小 频繁 checkpoint 设为能容纳 1 小时日志量
sync_binlog = 0 (生产) 数据丢失风险 生产环境设为 1

4.2.2 需要重启的参数

以下参数修改后需要重启 MySQL:

# 需要重启的参数
innodb_buffer_pool_size        # MySQL 8.0 可在线调整
innodb_log_file_size           # 8.0.30 前需要重启
innodb_page_size               # 初始化后无法修改
innodb_data_file_path          # 无法在线修改
lower_case_table_names         # 无法在线修改

4.2.3 动态参数调整注意事项

-- 在线调整 buffer_pool_size(MySQL 8.0)
-- 可能需要几分钟完成

SETGLOBAL innodb_buffer_pool_size = 48*1024*1024*1024;

-- 查看调整进度
SHOWSTATUSLIKE'Innodb_buffer_pool_resize_status';

五、故障排查和监控

5.1 参数相关问题诊断

-- 内存不足诊断
SELECT
    CONCAT(ROUND(@@innodb_buffer_pool_size/1024/1024/1024, 1), 'G') ASbuffer_pool,
    CONCAT(@@max_connections, ' * ',
           ROUND((@@sort_buffer_size + @@join_buffer_size)/1024/1024, 1), 'M = ',
           ROUND(@@max_connections * (@@sort_buffer_size + @@join_buffer_size)/1024/1024/1024, 1), 'G'
    ) AS max_session_memory;

-- 连接数问题诊断
SELECT
    @@max_connections AS max_conn,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Max_used_connections') AS max_used,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Threads_connected') AScurrent,
    (SELECT VARIABLE_VALUE FROM performance_schema.global_status
     WHERE VARIABLE_NAME = 'Aborted_connects') AS aborted;

5.2 关键监控指标

指标 健康阈值 查询方法
缓冲池命中率 > 99% Innodb_buffer_pool_read_requests vs reads
连接使用率 < 80% Threads_connected / max_connections
临时表磁盘率 < 25% Created_tmp_disk_tables / Created_tmp_tables
线程缓存命中率 > 99% 1 – Threads_created / Connections
表缓存命中率 > 99% 1 – Opened_tables / Open_tables

5.3 监控脚本

#!/bin/bash
# mysql_parameter_monitor.sh
# MySQL 参数效果监控脚本

MYSQL_CMD="mysql -u monitor -p'password' -N"

whiletrue; do
    timestamp=$(date '+%Y-%m-%d %H:%M:%S')

    # 收集指标
    result=$($MYSQL_CMD << EOF
SELECT
    ROUND((1 - bp_reads.val / bp_requests.val) * 100, 2) AS buffer_hit_rate,
    ROUND(threads.val / max_conn.val * 100, 1) AS conn_usage,
    ROUND(disk_tmp.val / all_tmp.val * 100, 1) AS disk_tmp_pct
FROM
    (SELECT CAST(VARIABLE_VALUE AS DECIMAL) AS val FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads') bp_reads,
    (SELECT CAST(VARIABLE_VALUE AS DECIMAL) AS val FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests') bp_requests,
    (SELECT CAST(VARIABLE_VALUE AS DECIMAL) AS val FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Threads_connected') threads,
    (SELECT CAST(VARIABLE_VALUE AS DECIMAL) AS val FROM performance_schema.global_variables WHERE VARIABLE_NAME = 'max_connections') max_conn,
    (SELECT CAST(VARIABLE_VALUE AS DECIMAL) AS val FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Created_tmp_disk_tables') disk_tmp,
    (SELECT CAST(VARIABLE_VALUE AS DECIMAL) AS val FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Created_tmp_tables') all_tmp;
EOF
)

    echo"$timestamp | $result"
    sleep 60
done

六、总结

6.1 技术要点回顾

本文详细介绍了 MySQL 20 个核心参数的调优方法,关键要点如下:

  1. innodb_buffer_pool_size是最重要的参数
    • 设置为物理内存的 70-80%
    • 命中率应保持在 99%以上
  2. 连接相关参数要平衡
    • max_connections不是越大越好
    • 每个连接都会消耗内存
  3. IO 参数要匹配硬件能力
    • innodb_io_capacity根据磁盘类型设置
    • SSD 和 HDD 差异巨大
  4. 刷盘策略要权衡安全与性能
    • 双 1 配置最安全但性能较差
    • 根据业务重要性选择合适配置
  5. 并行复制大幅提升从库性能
    • MySQL 8.0 的LOGICAL_CLOCK配合WRITESET
    • Worker 数量设置为 CPU 核心数的 1-2 倍

6.2 20 个参数速查表

序号 参数 推荐值 说明
1 innodb_buffer_pool_size 物理内存 70-80% 最重要的参数
2 innodb_buffer_pool_instances 8-16 减少锁竞争
3 innodb_redo_log_capacity 4-8G 8.0.30+新参数
4 innodb_log_buffer_size 64M 日志缓冲区
5 innodb_flush_log_at_trx_commit 1 或 2 刷盘策略
6 innodb_flush_method O_DIRECT SSD 推荐
7 innodb_io_capacity 根据磁盘 IO 能力
8 innodb_read/write_io_threads 8 IO 线程
9 max_connections 根据需要 最大连接
10 thread_cache_size 50 线程缓存
11 wait_timeout 600 空闲超时
12 table_open_cache 4000 表缓存
13 sort_buffer_size 2M 排序缓冲
14 join_buffer_size 2M 连接缓冲
15 tmp_table_size 64M 临时表
16 read_buffer_size 1M 读缓冲
17 binlog_cache_size 4M binlog 缓冲
18 sync_binlog 1 binlog 刷盘
19 binlog_expire_logs_seconds 604800 binlog 保留
20 replica_parallel_workers 16 并行复制

附录

A. 命令速查表

-- 查看所有变量
SHOWVARIABLES;
SHOWGLOBALVARIABLESLIKE'%innodb%';

-- 查看所有状态
SHOWGLOBALSTATUS;
SHOWGLOBALSTATUSLIKE'%Innodb%';

-- 动态修改参数
SETGLOBAL variable_name = value;
SETSESSION variable_name = value;

-- 查看参数是否可动态修改
SELECT * FROM performance_schema.global_variables
WHERE VARIABLE_NAME = 'innodb_buffer_pool_size';

B. 配置参数快速对照

场景 关键参数调整
读多写少 增大 buffer_pool,减小 redo_log
写多读少 增大 redo_log,调整刷盘策略
复杂查询 增大 tmp_table_size,sort_buffer
高并发 增大 connections,thread_cache
从库延迟 parallel_workers,放宽刷盘

C. 术语表

术语 说明
Buffer Pool InnoDB 缓冲池,缓存数据和索引
Redo Log 重做日志,保证事务持久性
Checkpoint 检查点,将脏页刷盘的操作
IOPS 每秒 IO 操作数
TPS 每秒事务数
QPS 每秒查询数

结语

MySQL 性能调优并非机械套用参数,而是结合服务器硬件、存储介质与业务场景的精细化适配。本文梳理的 20 项核心参数调优规则、多场景配置模板与实战案例,兼顾性能与数据安全。遵循先监测、后调优、小步迭代的原则,可有效规避配置误区,大幅提升 MySQL 数据库的稳定性与并发承载能力。

以上关于十年运维经验 MySQL 生产 20 个核心参数调优实战指南的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 十年运维经验 MySQL 生产 20 个核心参数调优实战指南

发表回复