易君召
易君召
发布于 2026-10-26 / 1 阅读
0

MySQL 性能优化完整方案

优化分 5 大层次:架构层 → 配置参数 → 索引优化 → SQL 语句 → 运维监控,由上到下落地,优先改 SQL 和索引(收益最高),再调参数,最后考虑架构扩容。

说明:下面默认是 InnoDB(MySQL 默认存储引擎,生产几乎都用)

一、架构层面(高并发、大数据量场景)

  1. 读写分离 主库写,从库读;应用层路由,读请求走从库,分担主库压力。

  • 简单:一主一从 / 一主多从

  • 中间件:ProxySQL、MyCat、ShardingSphere-JDBC

  • 注意:有同步延迟,强一致性场景不能读从库

  1. 分库分表 单表数据过大(InnoDB 建议单表控制在 2000w 以内,视字段而定):

  • 水平分表:按 user_id、order_id 哈希 / 范围分片

  • 垂直分表:大字段(text、blob)拆到独立附表

优先分表,不要过早分库,分库带来分布式事务、跨库 join 成本很高

  1. 缓存兜底 Redis 缓存热点数据(商品、配置、用户基础信息),挡住大部分读请求,避免打到 MySQL。

  • 缓存策略:Cache Aside(旁路缓存)最常用

  • 避免:缓存穿透、击穿、雪崩

  1. 存储与硬件

  • SSD 优先,不要机械盘;随机 IO 是 MySQL 瓶颈

  • 独立磁盘放 ibdata、redo log、binlog,减少 IO 争抢

  • 内存优先给 InnoDB Buffer Pool

二、核心配置优化(my.cnf/my.ini,MySQL8.0)

原则:Buffer Pool 是最重要参数,不要盲目抄网上配置,按服务器内存配比

[mysqld]
# 基础
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql

# 1. InnoDB 内存(重中之重)
innodb_buffer_pool_size = 物理内存的 50%~70%
# 例:32G服务器 → 20G左右;不要超过70%,留内存给系统、连接、缓存
innodb_buffer_pool_instances = 8  # 大于16G时设置,一般每1~2G一个实例
innodb_log_file_size = 1G           # redo log,8.0推荐512M~2G,不要太大(故障恢复慢)
innodb_log_buffer_size = 64M
innodb_flush_log_at_trx_commit = 1  # 1=强持久(默认,安全);2=每秒刷盘,性能高、宕机丢1s数据
innodb_flush_method = O_DIRECT      # 绕过OS缓存,避免双缓存,SSD必开
innodb_file_per_table = ON          # 每张表独立ibd文件,方便回收空间

# 2. 连接相关
max_connections = 1000              # 不要无限大,连接越多内存开销越大,配合连接池
wait_timeout = 300
interactive_timeout = 300
# 连接池建议:应用侧(HikariCP)控制连接数,不要依赖MySQL开大max_connections

# 3. Binlog(主从、数据恢复)
log_bin = ON
binlog_format = ROW                 # 推荐ROW,主从数据一致性更好;STATEMENT有坑
sync_binlog = 1                     # 1=每次事务刷盘,安全;0/100=提升性能,丢失风险
binlog_expire_logs_seconds = 86400*7 # 自动清理7天前binlog

# 4. 排序、分组、join临时内存(会话级,不要设置过大)
sort_buffer_size = 2M
join_buffer_size = 2M
read_buffer_size = 1M
read_rnd_buffer_size = 4M

# 5. 其他
tmp_table_size = 64M
max_heap_table_size = 64M           # 超过这个大小,临时表落地磁盘,性能暴跌
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

❌ 坑:sort_buffer /join_buffer 是每个连接单独分配,开大了高并发下瞬间吃光内存!

三、索引优化(收益最高,优先做)

1. 索引基础原则

  • 优先 B+Tree 普通索引,少用唯一索引(唯一校验有开销)

  • 联合索引遵循最左前缀

  • 索引不是越多越好:索引加快查询,但降低写入(insert/update/delete)速度,每个索引都要维护

  • 不索引:低区分度字段(性别、状态 0/1),索引几乎无效

2. 失效场景(高频踩坑)

  • 索引列做函数运算:where date(create_time)='2026-10-01' → 索引失效,改成范围 create_time between ...

  • 隐式类型转换:where phone=13800138000,phone 是 varchar,会自动转数字,索引失效

  • like %关键词,前缀模糊查询,索引失效;关键词% 可以走索引

  • or 左右一边无索引,整个不走索引

  • 不等于 != <> not in 容易不走索引

3. 推荐实践

  • 主键:自增主键(聚簇索引有序,页分裂少),不要用 UUID 做主键

  • 覆盖索引:查询字段全部在索引里,避免回表(Extra: Using index)

    -- 好:索引 idx_uid_name(uid,name)
    select name from t where uid=1001;
    
  • 避免 select *,只查需要字段

  • 定期监控冗余索引、未使用索引

-- 查看没用的索引(MySQL 8.0+)
SELECT * FROM sys.schema_unused_indexes;

四、SQL 语句优化(日常最常见瓶颈)

  1. 先用 explain 分析执行计划 重点看字段:

  • type:最好是 ref/range,最差 ALL(全表扫描,必须优化)

  • key:实际用到的索引

  • rows:预估扫描行数,越小越好

  • Extra:

    • Using filesort:文件排序,需要优化 order by,加到联合索引

    • Using temporary:创建临时表(group by/distinct 无索引)

    • Using index:覆盖索引,优秀

  1. 优化规则

  • 大表禁止 select count(*) 实时统计;用 Redis、定时统计表计数

  • 避免大事务:事务越大,锁持有越久,容易锁等待、死锁、undo 膨胀

  • limit 深分页:limit 100000,10 很慢 → 改成主键分页:where id>100000 limit 10

  • 批量插入用 insert into t values (),(),(),不要循环单条 insert

  • 禁止 join 大表,尽量小表驱动大表

  • 尽量少用 distinct,能业务层去重就不用数据库

五、锁、事务与 InnoDB 细节优化

  1. InnoDB 行锁是基于索引,不走索引就会升级为表锁,并发直接废掉

  2. 尽量缩短事务执行时间,不要在事务里做网络调用、sleep

  3. 避免间隙锁:范围查询(between、< >)会产生间隙锁,高并发秒杀场景注意

  4. 死锁:统一资源访问顺序;减少 update 多表、多行

六、监控 & 慢查询(定位问题)

开启慢查询日志

slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1   # 超过1s的SQL记录
log_queries_not_using_indexes = ON # 记录没走索引的SQL

工具:

  • mysqldumpslow 分析慢日志

  • pt-query-digest(Percona Toolkit)

  • 监控指标:QPS、TPS、连接数、锁等待、InnoDB 缓冲池命中率、redo 刷盘、binlog 延迟

关键健康指标

  • Buffer Pool 命中率 > 99% 比较健康

  • Innodb_row_lock_waits:锁等待次数持续走高 → 锁问题

  • Handler_read_rnd_next 持续暴涨 → 大量全表扫描

七、其他避坑

  1. 不要用 MyISAM(表锁,并发差,不支持事务)

  2. 大 DDL(加索引、改字段)不要业务高峰执行,8.0 支持 Instant DDL,但不是所有场景都支持;低版本用 pt-online-schema-change /gh-ost 在线改表,避免锁表

  3. 定期清理大表历史数据,不要直接 delete 大量数据(会产生大量 undo、碎片),推荐分批次 delete 或者分区表

  4. 分区表:按时间分区(日志、订单),删除历史分区极快,适合时序类数据

八、优化优先级总结(照着这个顺序排查)

  1. 抓慢 SQL,explain,补索引、改写 SQL(投入小收益最大 ✅)

  2. 检查事务、锁、死锁问题

  3. 调整 innodb_buffer_pool_size 等核心参数

  4. 热点数据加 Redis 缓存

  5. 读写分离、分表、升级 SSD

  6. 最后才考虑分库


本文原创作者:易君召,详见:https://www.yijunzhao.cn/authors/yijunzhao,转载请注明出处。

原文链接 https://www.yijunzhao.cn/archives/mysql-performance-optimization-complete-guide

欢迎访问 小易撩挨踢

https://www.yijunzhao.cn/