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

MySQL 实用优化技巧

适用 MySQL 5.7 / 8.0,默认 InnoDB,不罗列玄学方案,优先低成本、高收益手段

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

1. 索引设计原则

  1. 最左前缀原则:联合索引 idx(a,b,c),查询条件必须带上最左列 a 才能走索引;范围条件(> < between like 前缀通配)后面的列失效。 ✅ where a=1 and b=2 走索引 ❌ where b=2 and c=3 不走 ❌ where a>1 and b=2:只有 a 用到索引,b 失效

  2. 区分度优先:选择性高的字段放联合索引前面(性别、状态这类低区分度不要单独建索引)。

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

    -- 不要 select *
    select id,name from t where uid=100;
    -- 索引 idx_uid(uid,name) 就是覆盖索引
    
  4. 禁止过度索引:单表索引建议控制在 5 个以内;索引会加速查询、减慢写入(insert/update/delete)。

  5. 前缀索引:长字符串(url、摘要)只建前缀索引,降低索引体积

    create index idx_url on t(url(64));
    
  6. 避免索引失效场景

    • 函数、运算、隐式转换:where age+1=18 / where phone='13800138000' 但 phone 是 int

    • like '%关键词' 前导通配不走 B + 树索引;需要全文检索用 FULLTEXT 或者 ES

    • or 两边字段没有都建索引,容易不走索引,可以改成 union all

2. 索引检查工具

-- 查看执行计划
explain select ...;
-- 重点看 type(ref/range 好,ALL 全表扫描危险)、key、rows、Extra

Extra 关键字:

  • Using filesort:需要额外排序,尽量优化 order by 走索引排序

  • Using temporary:创建临时表,常见 group by 没索引

  • Using index:覆盖索引,优秀

3. 主键选择

  • InnoDB 主键是聚簇索引,推荐自增主键 bigint,不要用 uuid、随机字符串做主键(页分裂、碎片暴涨)

  • 主键尽量小,二级索引叶子节点都会存主键值

二、SQL 语句写法优化(零成本)

  1. 杜绝 select *,只查需要字段,减少 IO、方便覆盖索引

  2. 分页优化:深分页 limit 100000,10 很慢,推荐主键书签分页

    -- 差:limit offset 大的时候扫描大量行
    select * from t order by id limit 100000,10;
    -- 优:书签
    select * from t where id > 100000 order by id limit 10;
    
  3. in 里面值不要太多(建议 < 1000),太多可以拆分或者临时表

  4. union all 代替 union,不需要去重排序,性能高很多

  5. 大表禁止 order by rand()

  6. join 优化:小表驱动大表,关联字段必须建立索引;尽量减少 join 层数

  7. group by:尽量利用索引有序性,避免临时表和文件排序;不需要去重统计不要 distinct

  8. 事务里少写大查询,缩短事务时长,减少锁等待、行锁冲突

三、表结构设计优化(前期成本,后期收益巨大)

  1. 字段类型够用就好,越小越好

    • 能用 tinyint 不用 int,能用 int 不用 bigint

    • 时间优先 datetime(MySQL8.0 支持毫秒),不要用字符串存时间;不推荐 timestamp(时区、范围限制)

    • IP 可以用 inet6_aton 存二进制,比 varchar 省空间

    • 字符串:固定长度用 char,变长用 varchar;超长文本用 text 单独分表,不要混在高频查询表

  2. 避免 NULL:允许 NULL 会让索引、统计、判断更复杂,默认值用 0 / ''

  3. 适当分表:

    • 水平分表:数据量大(千万级 +)、按时间 / 用户 ID 分片

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

  4. 字符集:新项目统一 utf8mb4(支持 emoji),排序规则 utf8mb4_unicode_ci 或者 utf8mb4_general_ci;不要混用字符集,会隐式转换索引失效

  5. 控制单行大小:InnoDB 一页 16KB,单行过大,一页存放行数变少,IO 上升

四、InnoDB 核心参数优化(my.cnf/my.ini)

下面是生产常用参考,根据服务器内存调整,以 8G 内存机器举例

# 最重要参数:缓冲池,建议物理内存 50%~70%
innodb_buffer_pool_size = 4G
# 8.0支持多实例buffer pool,减少锁竞争
innodb_buffer_pool_instances = 4

# 日志文件,redo log,刷盘策略,平衡性能和安全
innodb_log_file_size = 1G
innodb_log_files_in_group = 2
innodb_flush_log_at_trx_commit = 1  # 1=最安全;2=性能更高,宕机丢少量数据

# 脏页刷盘,防止突然大量刷盘抖动
innodb_max_dirty_pages_pct = 75

# 关闭查询缓存(MySQL8.0已经移除,5.7建议关闭,极易失效)
query_cache_type = 0

# 连接数,不要盲目开很大,连接是消耗资源的
max_connections = 500
wait_timeout = 600

# 排序缓冲、join缓冲:是每个连接独享!不要设太大
sort_buffer_size = 2M
join_buffer_size = 2M
read_buffer_size = 1M
read_rnd_buffer_size = 2M

重点说明:

  • innodb_buffer_pool_size:InnoDB 最重要参数,缓存索引 + 数据页

  • innodb_flush_log_at_trx_commit=1 配合 sync_binlog=1 是 ACID 安全标准;高并发可改成 2 换取性能提升

  • sort/join buffer 是每个连接单独分配,设置过大,并发上来瞬间吃光内存

五、事务、锁与并发优化(解决慢、阻塞、死锁)

  1. 事务尽量短:不要在事务里做网络调用、sleep、大查询

  2. 行锁前提:SQL 能精准走到索引;不走索引 → 行锁升级为表锁(大坑!)

  3. 更新顺序统一:多个事务更新多行,固定更新顺序,避免死锁

  4. 减少 gap lock(间隙锁):MySQL8.0 RR 隔离级别存在;业务允许可以降级为 RC(读已提交),间隙锁消失,并发大幅提升,但会出现不可重复读

  5. 避免 select ... for update 长事务占用行锁;只锁定必要行

  6. 乐观锁代替悲观锁:版本号 version 机制,减少锁争抢

六、运维层面优化

  1. 定期统计信息更新

analyze table table_name;

InnoDB 统计信息不准会导致优化器选错索引,explain 看到 rows 严重失真时执行 2. 碎片清理

  • delete/update 产生碎片:optimize table t;(InnoDB 重建表,锁表,低峰期执行)

  • 8.0 支持在线 DDL,改表尽量用 ALGORITHM=INPLACE, LOCK=NONE

  1. 慢查询日志,抓慢 SQL(最常用排查手段)

slow_query_log = ON
slow_query_log_file = slow.log
long_query_time = 1
log_queries_not_using_indexes = ON # 记录没走索引的SQL

工具:pt-query-digest 分析慢日志 4. binlog 优化

  • 格式推荐 binlog_format=ROW,安全;MIXED 老项目兼容

  • 定期清理 binlog:expire_logs_days=7

  1. 磁盘:优先 SSD;InnoDB 是随机 IO 敏感,机械盘高并发很容易瓶颈;单独放数据目录、redo log

七、架构层面优化(单库优化到顶之后)

  1. 读写分离:主库写,从库读;业务代码路由读请求到从库

  2. 分库分表:ShardingSphere / MyCat;按哈希、时间分片

  3. 热点缓存:Redis 缓存热点数据,击穿、穿透、雪崩防护,减少直接压 MySQL

  4. 归档:历史冷数据归档到单独库,主库只保留近期热数据

  5. 异构:搜索场景交给 ES,时序数据交给时序库,不要全部压 MySQL

八、常见坑速查清单

  • ❌ 索引建了但是隐式转换不走

  • ❌ limit 超大偏移分页

  • ❌ 用 uuid 当主键

  • ❌ 长事务、for update 乱加

  • ❌ sort_buffer 等会话缓冲设置超大

  • ❌ 单表索引十几个

  • ❌ 用 text/blob 放高频查询字段

  • ❌ 混用字符集排序规则