开篇引入
"我的服务器有32G内存,12核CPU,帮我配一下MySQL。"
每次听到这种需求,我都想说:没有放之四海而皆准的配置文件。MySQL的配置要基于工作负载、数据量、业务需求来定制,而不是简单地按硬件比例放大。
《高性能MySQL》第5章开篇就强调了这一点。配置不是"调优",而是对MySQL行为的精确控制。这篇文章帮你建立正确的配置观念。
MySQL配置的基本原理
配置从哪来
MySQL读取配置的地方有两个:
1. 命令行参数
mysqld --port=3306 --datadir=/var/lib/mysql
2. 配置文件
# 类Unix系统查找顺序
/etc/my.cnf
/etc/mysql/my.cnf
$MYSQL_HOME/my.cnf
~/.my.cnf
最佳实践:把所有配置写进文件,而不是命令行。命令行容易被遗忘,文件更可控。
配置文件的格式
[mysqld]
# MySQL服务器配置
port = 3306
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
pid-file = /var/run/mysqld/mysqld.pid
# InnoDB配置
innodb_buffer_pool_size = 128G
innodb_log_file_size = 4G
[client]
# 客户端配置
port = 3306
socket = /var/lib/mysql/mysql.sock
注意:
- 配置项可以用下划线或横线:
max_connections=max-connections - 建议统一风格,我偏好下划线
- 放在正确的section里,
[mysqld]是服务器配置,[client]是客户端
变量作用域
MySQL变量有三种作用域:
全局变量(Global):整个服务器生效
-- 查看全局配置
SHOW GLOBAL VARIABLES LIKE 'max_connections';
-- 修改全局配置(需要SUPER权限)
SET GLOBAL max_connections = 1000;
会话变量(Session):只影响当前连接
-- 查看会话配置
SHOW SESSION VARIABLES LIKE 'sort_buffer_size';
-- 修改会话配置
SET SESSION sort_buffer_size = 1048576;
实例变量:全局+会话都有,修改会话不影响全局
-- 全局值 vs 会话值
SHOW VARIABLES; -- 默认显示会话值
SHOW GLOBAL VARIABLES; -- 显示全局值
-- 两者的关系
SET GLOBAL sort_buffer_size = 2097152; -- 设置全局默认值
SET sort_buffer_size = 1048576; -- 只改当前会话
动态修改的陷阱
动态修改配置后,如果MySQL重启,配置会恢复到配置文件中的值。
-- 动态修改(重启失效)
SET GLOBAL max_connections = 2000;
-- 持久化(MySQL 8.0+)
SET PERSIST max_connections = 2000;
-- 等价于:
-- 1. 立即应用
-- 2. 写入 mysqld-auto.cnf
-- 查看持久化的配置
SELECT * FROM performance_schema.persisted_variables;
动态修改的副作用
有些变量修改会立即生效,有些则不会:
| 变量 | 何时生效 | 行为说明 |
|---|---|---|
table_open_cache | 下次打开表时 | 检查缓存大小 |
thread_cache_size | 下次关闭连接时 | 可能不会立即减少内存 |
sort_buffer_size | 立即分配 | 建议会话级别设置 |
read_buffer_size | 查询需要时 | 整块分配 |
MySQL 8.0的智能配置
innodb_dedicated_server
MySQL 8.0引入了这个选项,让MySQL自动根据硬件配置参数:
[mysqld]
# 开启自动配置(推荐用于云环境)
innodb_dedicated_server = ON
开启后,MySQL会自动设置:
innodb_buffer_pool_sizeinnodb_log_file_sizeinnodb_log_files_in_groupinnodb_flush_method
计算规则(大约):
内存 ≤ 1G: buffer_pool = 128M
内存 1-4G: buffer_pool = 物理内存 * 0.5
内存 4-64G: buffer_pool = 物理内存 * 0.75
内存 > 64G: buffer_pool = 物理内存 * 0.5
注意:自动配置只是起点,生产环境应该根据实际工作负载调整。
配置文件推荐模板
最小配置示例
[mysqld]
# 基本设置
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
pid-file = /var/run/mysqld/mysqld.pid
# 字符集
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# InnoDB(根据硬件调整)
innodb_buffer_pool_size = 64G
innodb_log_file_size = 4G
innodb_flush_log_at_trx_commit = 1
innodb_file_per_table = 1
# 连接
max_connections = 2000
thread_cache_size = 64
# 查询优化
sort_buffer_size = 2M
read_buffer_size = 2M
join_buffer_size = 2M
# 慢查询日志
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
[client]
socket = /var/lib/mysql/mysql.sock
default-character-set = utf8mb4
什么不该做
1. 不要迷信"调优比率"
网上流传的"缓冲池命中率要>95%"之类的说法是误导。
-- 缓冲池命中率低,不一定是缓存太小
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';
-- 命中率 = read_requests / (read_requests + reads)
命中率低可能是因为工作负载本身就难以缓存,跟缓存大小无关。
2. 不要用调优脚本
那些"一键优化MySQL"的脚本:
- 假设所有服务器都一样
- 可能设置危险的值
- 让你不理解真正的问题
3. 不要盲目照搬别人的配置
"我看到某博客说sort_buffer_size设成8M最好"
"某论坛说innodb_buffer_pool_size要设成物理内存的80%"
这些建议可能对别人的场景有效,但不一定适合你。
4. 不要相信内存公式
MySQL启动时会输出一个内存公式,告诉你"最坏情况下会占用多少内存"。
这个公式是错的。它忽略了:
- 线程栈
- Prepared语句
- InnoDB内部结构
- 操作系统开销
正确的配置思路
第一步:理解工作负载
-- 查看最常见的查询类型
SELECT
DIGEST_TEXT,
COUNT_STAR,
SUM_ROWS_EXAMINED,
SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY COUNT_STAR DESC
LIMIT 20;
第二步:设置基本参数
-- 根据数据量设置Buffer Pool
-- 如果数据有100GB,Buffer Pool至少要有100GB
-- 留30%给系统和其他开销
第三步:设置安全参数
[mysqld]
# 防止数据损坏
innodb_checksum_algorithm = crc32
# 防止连接耗尽
max_connections = 2000
wait_timeout = 600
interactive_timeout = 600
# 防止SQL注入(应用层也要做)
sql_mode = STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO
第四步:监控和调优
# 使用状态变量调整配置
mysqladmin extended-status -r -i 1 | grep -E 'Threads_running|Questions|Innodb_buffer_pool_wait_free'
常用配置项速查
连接相关
[mysqld]
max_connections = 2000 # 最大连接数
thread_cache_size = 64 # 线程缓存
wait_timeout = 600 # 空闲连接超时
max_connect_errors = 100000 # 允许的错误连接数
内存相关
[mysqld]
innodb_buffer_pool_size = 128G # InnoDB缓冲池
key_buffer_size = 32M # MyISAM索引缓存
sort_buffer_size = 2M # 排序缓存(会话级调整更佳)
join_buffer_size = 2M # JOIN缓存
read_buffer_size = 2M # 顺序读缓存
tmp_table_size = 256M # 临时表大小
max_heap_table_size = 256M # 内存表大小
InnoDB相关
[mysqld]
innodb_buffer_pool_size = 128G
innodb_log_file_size = 4G
innodb_log_files_in_group = 3
innodb_flush_log_at_trx_commit = 1
innodb_file_per_table = 1
innodb_flush_method = O_DIRECT
日志相关
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
配置变更管理
使用版本控制
# 配置文件放到Git中
git add /etc/my.cnf
git commit -m "更新max_connections到2000"
变更流程
1. 在测试环境验证
↓
2. 评估影响(内存、连接数、IO)
↓
3. 准备回滚方案
↓
4. 变更生产配置
↓
5. 验证效果
↓
6. 监控SLO
记录变更
-- 记录配置变更历史
CREATE TABLE config_changes (
id INT AUTO_INCREMENT PRIMARY KEY,
variable_name VARCHAR(64),
old_value VARCHAR(255),
new_value VARCHAR(255),
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
changed_by VARCHAR(64),
reason TEXT
);
小结
- 没有万能配置:配置要基于工作负载,而不是硬件规格
- 理解变量作用域:全局变量vs会话变量,动态修改vs持久化
- innodb_dedicated_server:云环境可以考虑使用
- 不要迷信调优比率:那些经验法则可能是误导
- 不要用调优脚本:手动配置更可控
- 配置变更要记录:用Git管理配置文件
- 先优化查询和索引:配置调优的收益远小于查询优化
- 监控SLO:配置改变后要确认客户体验没有下降
MySQL的配置哲学是:保持简单,只改需要的。默认设置经过了大量测试,大多数情况下是安全的。在有明确理由之前,不要随意修改。
延伸阅读
- 《高性能MySQL》第5章 优化服务器设置
- MySQL 8.0 Reference Manual: Server System Variables
- MySQL Server Variables: dev.mysql.com/doc/refman/…