一、MySQL 配置基础
1.1 配置文件位置与结构
MySQL 的配置文件(通常命名为my.cnf或my.ini)是性能优化的核心入口,不同环境下的默认路径如下:
操作系统 / 安装方式 |
配置文件路径 |
Linux(apt/yum) |
/etc/mysql/my.cnf 或 /etc/my.cnf |
Linux(源码安装) |
/usr/local/mysql/my.cnf |
macOS(Homebrew) |
/usr/local/etc/my.cnf |
Windows |
C:\ProgramData\MySQL\MySQL Server X.X\my.ini |
配置文件采用 “组” 结构划分不同模块的配置,核心组包括:
[client] # 客户端通用配置(如mysql命令行工具)
port = 3306
socket = /tmp/mysql.sock
[mysqld] # 服务端核心配置(性能优化主要针对此部分)
user = mysql
datadir = /var/lib/mysql
socket = /tmp/mysql.sock
[mysqld_safe] # 安全启动相关配置(日志、进程管理)
log-error = /var/log/mysql/error.log
pid-file = /var/run/mysqld/mysqld.pid
1.2 配置生效与验证
修改配置后需重启 MySQL 服务使配置生效:
# 系统服务方式
systemctl restart mysqld
# 源码安装方式
/usr/local/mysql/bin/mysqladmin -u root -p shutdown
/usr/local/mysql/bin/mysqld_safe --user=mysql &
验证配置是否生效:
-- 查看单个参数(如缓冲池大小)
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- 查看所有参数
SHOW VARIABLES;
二、核心参数优化(按重要性排序)
2.1 内存配置(性能瓶颈的主要来源)
2.1.1 InnoDB 缓冲池(innodb_buffer_pool_size)
- 作用:缓存 InnoDB 表的数据、索引、插入缓冲等,是影响性能的最关键参数。
- 优化原则:
-
- 专用数据库服务器:物理内存的 70%-80%(如 16GB 内存服务器配置 12GB)。
-
- 混合部署服务器:物理内存的 50%(避免与其他服务竞争内存)。
- 配置示例:
[mysqld]
innodb_buffer_pool_size = 12G
- 辅助配置:
# 多实例拆分缓冲池(内存>16GB时建议)
innodb_buffer_pool_instances = 4 # 拆分4个实例,减少锁竞争
- 监控指标:缓冲池命中率应≥99%,计算公式:
SELECT
(1 - (SUM(IF(VARIABLE_NAME = 'Innodb_buffer_pool_reads', VARIABLE_VALUE, 0)) /
SUM(IF(VARIABLE_NAME = 'Innodb_buffer_pool_read_requests', VARIABLE_VALUE, 0))) * 100 AS hit_rate
FROM information_schema.GLOBAL_STATUS
WHERE VARIABLE_NAME IN ('Innodb_buffer_pool_reads', 'Innodb_buffer_pool_read_requests');
2.1.2 连接相关内存
- max_connections:最大并发连接数,默认 151,需根据业务调整:
[mysqld]
max_connections = 500 # 支持500个并发连接
max_user_connections = 400 # 单个用户最大连接数(避免资源独占)
注意:连接数并非越大越好,过多会导致内存耗尽和上下文切换开销增加。
- 每个连接的内存分配:
[mysqld]
sort_buffer_size = 2M # 排序缓冲区(每个连接独占,不宜过大)
join_buffer_size = 2M # 表连接缓冲区(无索引时使用)
read_buffer_size = 1M # 顺序读取缓冲区
read_rnd_buffer_size = 2M # 随机读取缓冲区
计算参考:500 连接 × (2+2+1+2) M = 3.5GB,需纳入总内存预算。
2.1.3 临时表与缓存
- 临时表内存限制:
[mysqld]
tmp_table_size = 64M # 内存临时表最大大小
max_heap_table_size = 64M # 同tmp_table_size,取较小值生效
超过限制会转为磁盘临时表(ibtmp1 文件),性能骤降,需监控Created_tmp_disk_tables状态变量。
- 查询缓存(MySQL 5.7 及以下):
[mysqld]
query_cache_type = 0 # 禁用查询缓存(写频繁场景性能反降)
query_cache_size = 0 # 5.7默认关闭,8.0已移除
2.2 InnoDB 日志配置
- innodb_log_file_size:重做日志文件大小,影响事务性能:
[mysqld]
innodb_log_file_size = 1G # 单个日志文件大小(1G-4G为宜)
innodb_log_files_in_group = 2 # 日志文件数量(通常2个)
innodb_log_buffer_size = 64M # 日志缓冲区大小
调整方法:停止 MySQL → 删除ib_logfile0和ib_logfile1 → 重启 MySQL 自动重建。
- 事务日志刷盘策略:
[mysqld]
innodb_flush_log_at_trx_commit = 1 # 最安全(事务提交即刷盘)
# innodb_flush_log_at_trx_commit = 2 # 性能优先(每秒刷盘,掉电可能丢1秒数据)
2.3 并发与 I/O 配置
- 并发线程数:
[mysqld]
innodb_thread_concurrency = 0 # 0表示不限制(自动调整)
innodb_read_io_threads = 8 # 读I/O线程数(8-16为宜)
innodb_write_io_threads = 8 # 写I/O线程数
- I/O 调度:
[mysqld]
innodb_flush_method = O_DIRECT # 绕过操作系统缓存,减少双重缓存
innodb_io_capacity = 2000 # 后台I/O能力(SSD设为2000+,HDD设为200-500)
2.4 连接与安全配置
- 连接超时:
[mysqld]
wait_timeout = 600 # 非交互式连接超时(10分钟)
interactive_timeout = 1200 # 交互式连接超时(20分钟)
- 限制连接来源:
[mysqld]
bind-address = 192.168.1.100 # 只监听内网IP,禁止公网直接访问
三、查询优化(SQL 层面的性能提升)
3.1 慢查询定位与分析
- 启用慢查询日志:
[mysqld]
slow_query_log = 1 # 启用慢查询日志
slow_query_log_file = /var/log/mysql/slow.log # 日志路径
long_query_time = 1 # 执行时间超过1秒的查询记录
log_queries_not_using_indexes = 1 # 记录未使用索引的查询
- 分析慢查询日志:
# 使用mysqldumpslow分析(MySQL自带)
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log # 按时间排序,取前10条
# 使用pt-query-digest(Percona Toolkit,更强大)
pt-query-digest /var/log/mysql/slow.log > slow_report.txt
3.2 索引优化(减少 I/O 的关键)
- 创建高效索引:
-- 为过滤条件、连接条件、排序字段创建索引
CREATE INDEX idx_user_status_create_time ON orders (user_id, status, create_time);
-- 复合索引遵循"最左前缀原则",以下查询可命中索引
SELECT * FROM orders WHERE user_id = 100 AND status = 'paid';
SELECT * FROM orders WHERE user_id = 100;
- 避免索引失效:
-- 索引失效场景1:函数操作列
SELECT * FROM users WHERE YEAR(register_time) = 2023; -- 失效
SELECT * FROM users WHERE register_time >= '2023-01-01' AND register_time < '2024-01-01'; -- 有效
-- 索引失效场景2:隐式类型转换
SELECT * FROM products WHERE id = '123'; -- id是INT,字符串比较导致失效
SELECT * FROM products WHERE id = 123; -- 有效
3.3 常用查询优化技巧
- 分页查询优化(大偏移量问题):
-- 低效:LIMIT 100000,10需扫描100010行
SELECT * FROM logs ORDER BY id DESC LIMIT 100000, 10;
-- 高效:利用索引定位起点(仅适用于自增主键)
SELECT * FROM logs
WHERE id < (SELECT id FROM logs ORDER BY id DESC LIMIT 100000, 1)
ORDER BY id DESC LIMIT 10;
- 避免 SELECT *:只查询需要的字段,减少数据传输和内存消耗:
-- 低效
SELECT * FROM users WHERE department = 'IT';
-- 高效
SELECT id, name, email FROM users WHERE department = 'IT';
- JOIN 优化:
-- 确保连接字段有索引
ALTER TABLE orders ADD INDEX idx_user_id (user_id);
-- 小表驱动大表(将结果集小的表作为驱动表)
SELECT o.order_no, u.name
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.register_time > '2023-01-01';
四、架构优化(高并发场景的解决方案)
4.1 读写分离(主从复制)
- 架构图:
客户端 → 中间件(如MyCat)→ 主库(写操作)
→ 从库1(读操作)
→ 从库2(读操作)
- 主库配置:
[mysqld]
server-id = 1
log_bin = /var/lib/mysql/mysql-bin # 启用二进制日志
binlog_do_db = business_db # 只记录业务库日志
- 从库配置:
[mysqld]
server-id = 2
relay_log = /var/lib/mysql/relay-bin # 中继日志
read_only = 1 # 从库只读
- 从库连接主库:
-- 在从库执行
CHANGE MASTER TO
MASTER_HOST = '192.168.1.10', # 主库IP
MASTER_USER = 'repl_user', # 复制账号
MASTER_PASSWORD = 'repl_pass', # 密码
MASTER_LOG_FILE = 'mysql-bin.000001', # 主库当前日志文件
MASTER_LOG_POS = 154; # 日志位置
START SLAVE; # 启动复制
-- 检查复制状态(需Seconds_Behind_Master为0)
SHOW SLAVE STATUS\G
4.2 分库分表(解决单表过大问题)
- 分表策略:
-
- 水平分表:按 ID 范围或哈希拆分(如orders_1、orders_2)。
-
- 垂直分表:按字段冷热拆分(如user_basic存基础信息,user_detail存详细信息)。
- 实现工具:
-
- 中间件:MyCat、Sharding-JDBC。
-
- 原生支持:MySQL 8.0 的分区表(适合简单场景):
-- 按时间分区
CREATE TABLE logs (
id INT,
content TEXT,
create_time DATETIME
) PARTITION BY RANGE (TO_DAYS(create_time)) (
PARTITION p202301 VALUES LESS THAN (TO_DAYS('2023-02-01')),
PARTITION p202302 VALUES LESS THAN (TO_DAYS('2023-03-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
);
4.3 缓存策略(减轻数据库压力)
- 多级缓存架构:
- 本地缓存:应用程序内存缓存(如 Java 的 Caffeine)。
- 分布式缓存:Redis/Memcached 缓存热点数据。
- 数据库缓存:MySQL 查询缓存(已淘汰)→ InnoDB 缓冲池。
- 缓存更新策略:
-
- 读操作:先查缓存→缓存未命中→查数据库→更新缓存。
-
- 写操作:更新数据库→删除缓存(避免缓存与数据库不一致)。
五、监控与维护
5.1 关键指标监控
- 连接数:
-- 查看当前连接数
SHOW STATUS LIKE 'Threads_connected';
-- 查看连接来源
SELECT SUBSTRING_INDEX(host, ':', 1) AS ip, COUNT(*) AS count
FROM information_schema.processlist
GROUP BY ip ORDER BY count DESC;
- 锁等待:
-- 查看锁等待状态
SHOW ENGINE INNODB STATUS\G
-- 查看当前锁
SELECT * FROM information_schema.INNODB_LOCKS;
- 表空间使用:
-- 查看各表大小
SELECT
table_schema AS '数据库',
table_name AS '表名',
round(((data_length + index_length) / 1024 / 1024), 2) AS '大小(MB)'
FROM information_schema.TABLES
ORDER BY (data_length + index_length) DESC;
5.2 定期维护任务
- 每周优化:
-- 分析表(更新统计信息)
ANALYZE TABLE users, orders;
-- 优化表(整理碎片)
OPTIMIZE TABLE logs; -- InnoDB表实际执行的是ALTER TABLE,会锁表
- 每月维护:
-
- 检查磁盘空间(df -h),确保至少有 30% 空闲空间。
-
- 备份二进制日志(mysqlbinlog),清理过期日志(PURGE BINARY LOGS BEFORE '2023-01-01')。
-
- 检查数据库文件完整性(mysqlcheck -A -c)。
六、性能优化案例分析
6.1 案例 1:高并发写入慢
- 现象:电商秒杀场景,订单表插入延迟达 500ms 以上。
- 分析:SHOW ENGINE INNODB STATUS显示大量锁等待,ibdata1文件增长过快。
- 优化措施:
[mysqld]
innodb_buffer_pool_size = 16G # 增加缓冲池
innodb_flush_log_at_trx_commit = 2 # 改为每秒刷盘(允许丢1秒数据)
innodb_file_per_table = 1 # 每张表独立表空间
-- 分表处理
CREATE TABLE orders_202306 LIKE orders;
-- 配合中间件实现按月份自动路由
6.2 案例 2:查询超时
- 现象:报表查询超时,SHOW PROCESSLIST显示状态为 “Sending data”。
- 分析:EXPLAIN显示全表扫描,未使用索引。
- 优化措施:
-- 添加复合索引
CREATE INDEX idx_user_date_amount ON orders (user_id, create_date, amount);
-- 重写查询(避免SELECT *)
SELECT user_id, SUM(amount) FROM orders
WHERE user_id = 100 AND create_date BETWEEN '2023-06-01' AND '2023-06-30'
GROUP BY user_id;
七、总结与最佳实践
a.参数优化优先级:
-
- 第一优先级:innodb_buffer_pool_size(内存分配)。
-
- 第二优先级:innodb_log_file_size(日志配置)。
-
- 第三优先级:连接参数和缓存参数。
b.性能优化流程:
-
- 监控(找出瓶颈)→ 分析(定位原因)→ 优化(参数 / SQL / 架构)→ 验证(对比优化前后)。
c.避免过度优化:
-
- 80% 的性能问题由 20% 的原因导致,聚焦关键瓶颈。
-
- 小流量场景(QPS<100)无需复杂优化,优先保证稳定性。
d.持续优化:
-
- 业务增长会改变性能瓶颈,需定期(如每季度)重新评估配置。
-
- 记录每次优化的参数和效果,建立优化知识库。
通过以上优化措施,MySQL 的性能可提升 3-10 倍,具体取决于原始配置和业务场景。关键是结合实际负载调整参数,而非盲目套用模板。