MySQL性能优化面试全攻略

一、MySQL 性能优化常见面试题清单

类别 高频问题
索引 索引原理、B+树、聚簇/非聚簇、最左前缀、覆盖索引、索引失效
SQL 慢 SQL 排查、EXPLAIN、JOIN 优化、分页优化
架构 读写分离、分库分表、主从复制、缓存
行锁/表锁、间隙锁、死锁、MVCC
存储 InnoDB vs MyISAM、redo/undo、Buffer Pool
实战 如何定位慢查询、如何优化一条 SQL、百万数据分页

二、性能优化回答框架(建议背这个结构)

MySQL 优化我会从 5 层来看:硬件/配置 → 表结构 → 索引 → SQL → 架构。先定位瓶颈,再针对性优化,避免盲目加索引。


三、分层优化详解

1. 硬件与配置层

硬件

  • CPU:复杂查询、排序、JOIN 多时更重要
  • 内存:innodb_buffer_pool_size 通常设为物理内存的 50%~70%
  • 磁盘:SSD,随机 I/O 对数据库影响极大
  • 网络:主从复制、分库分表时带宽要够

关键参数

复制代码
# 缓冲池(最重要)
innodb_buffer_pool_size = 8G

# 日志
innodb_log_file_size = 1G
innodb_flush_log_at_trx_commit = 1   # 强一致
# = 2 性能更好,可能丢 1 秒数据

# 连接
max_connections = 500
wait_timeout = 600

# 慢查询
slow_query_log = 1
long_query_time = 1

2. 表结构设计

原则 说明
选合适类型 INTBIGINT 省空间;VARCHAR(50)VARCHAR(255) 索引更小
避免 NULL 能用 NOT NULL + 默认值就用,索引和计算更简单
适度反范式 高频查询可适当冗余,减少 JOIN
大字段拆分 文本、JSON 放单独表,避免拖慢主表
分区表 按时间/范围分区,适合历史数据归档

反例

  • SELECT *
  • 单表几千万行还不考虑归档或分表
  • 所有字段都建索引

3. 索引优化(面试最爱问)

索引原理(B+树)
  • InnoDB 默认 B+树 索引
  • 聚簇索引:叶子节点存整行数据(主键)
  • 二级索引:叶子节点存主键值,需要 回表
索引设计原则
  1. WHERE / ORDER BY / GROUP BY 高频字段考虑索引
  2. 区分度高 的字段更适合建索引(如手机号 > 性别)
  3. 最左前缀:联合索引 (a, b, c) 可用于 aa,ba,b,c
  4. 覆盖索引:查询字段都在索引里,避免回表
  5. 索引不是越多越好:写入要维护索引,占空间
索引失效场景(必背)

-- 1. 对索引列做函数/运算

WHERE YEAR(create_time) = 2024 -- 失效

WHERE create_time >= '2024-01-01' -- 有效

-- 2. 隐式类型转换

WHERE phone = 13800138000 -- phone 是 varchar,可能失效

-- 3. 左模糊

WHERE name LIKE '%张%' -- 失效

WHERE name LIKE '张%' -- 可能用到索引

-- 4. OR 一侧无索引

WHERE a = 1 OR b = 2 -- b 无索引则可能全表扫

-- 5. 违反最左前缀

索引 (a,b,c),条件只有 b、c

-- 6. 优化器判断全表扫更快

-- 小表、返回行数过多时可能不走索引

实战优化套路

-- 优化前:回表 + -filesort

SELECT * FROM orders WHERE user_id = 100 ORDER BY create_time DESC LIMIT 10;

-- 优化后:联合索引 (user_id, create_time),覆盖索引

SELECT id, create_time FROM orders

WHERE user_id = 100 ORDER BY create_time DESC LIMIT 10;

4. SQL 优化

慢 SQL 排查流程
  1. 开启慢查询日志 / 用 performance_schema

  2. EXPLAIN 看执行计划

  3. 看 type、key、rows、Extra

  4. 改索引 / 改 SQL / 改表结构

  5. 压测验证

EXPLAIN 重点字段
字段 关注点
type system > const > eq_ref > ref > range > index > ALL,至少到 range,最好 ref
key 实际用到的索引
rows 预估扫描行数,越小越好
Extra 避免 Using filesortUsing temporary
常见 SQL 优化

**避免 SELECT ***

-- 差

SELECT * FROM user WHERE id = 1;

-- 好

SELECT id, name, phone FROM user WHERE id = 1;

小表驱动大表

-- 差:大表驱动

SELECT * FROM big_table b JOIN small_table s ON b.id = s.big_id;

-- 好:小表放前面(MySQL 优化器也会调整,但写法要清晰)

SELECT * FROM small_table s JOIN big_table b ON s.big_id = b.id;

深分页优化

-- 慢:OFFSET 大时要跳过大量行

SELECT * FROM orders ORDER BY id LIMIT 1000000, 10;

-- 方案1:延迟关联

SELECT o.* FROM orders o

JOIN (SELECT id FROM orders ORDER BY id LIMIT 1000000, 10) t ON o.id = t.id;

-- 方案2:游标分页(推荐)

SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 10;

JOIN 优化

  • 被驱动表关联字段要有索引
  • JOIN 字段类型一致
  • 控制 JOIN 表数量,必要时用冗余字段

批量操作

-- 差:循环单条 INSERT

-- 好

INSERT INTO user (name, phone) VALUES

('a', '1'), ('b', '2'), ('c', '3');

-- 大批量可分批,每批 500~1000 条

COUNT 优化

-- 慢

SELECT COUNT(*) FROM orders WHERE status = 1;

-- 若只要近似值,可用统计表或缓存

-- 若 status 区分度高,建 (status) 或 (status, id) 索引

5. 架构层优化

读写分离
  • 写走主库,读走从库
  • 注意 主从延迟 导致读到旧数据
  • 强一致读走主库,或关键业务做延迟双读
缓存(Redis)
  • 热点数据放缓存,减轻 DB 压力
  • 注意 缓存穿透、击穿、雪崩
  • 更新策略:Cache Aside(先更 DB 再删缓存)较常见
分库分表
时机 信号
分表 单表千万级、索引变大、维护变慢
分库 连接数、I/O、备份恢复成为瓶颈
  • 分片键选择:用户 ID、订单 ID 等高频查询字段
  • 避免跨分片 JOIN、事务
连接池
  • 应用侧用连接池(HikariCP、Druid)
  • 避免每次请求新建连接

四、InnoDB 内核相关(加分项)

概念 作用
Buffer Pool 数据页、索引页缓存,减少磁盘 I/O
redo log 崩溃恢复,WAL
undo log 回滚、MVCC
binlog 主从复制、数据恢复
MVCC 读写不阻塞,实现可重复读

两阶段提交:redo log + binlog 保证数据一致性。

五、锁与事务(常和性能一起问)

  • 行锁:InnoDB 默认,锁粒度小
  • 间隙锁:可重复读下防幻读,可能降低并发
  • 死锁:SHOW ENGINE INNODB STATUS 查看,尽量按相同顺序访问行
  • 大事务危害:锁持有久、undo 多、主从延迟大 → 事务尽量短

六、完整面试回答模板(1~2 分钟版)

我在项目里做 MySQL 优化,一般按这个流程:

  1. 先定位

看慢查询日志、EXPLAIN、监控(QPS、连接数、锁等待、Buffer Pool 命中率)。先找 TOP 慢 SQL,不盲目优化。

  1. 索引层

给高频 WHERE/ORDER BY 加合适索引;用联合索引和最左前缀;尽量覆盖索引减少回表;避免在索引列上函数运算、隐式转换、左模糊。

  1. SQL 层

避免 SELECT *;深分页用游标或延迟关联;批量用 batch insert;小表驱动大表;减少不必要的 JOIN 和子查询。

  1. 表结构

字段类型合适、必要反范式、大字段拆分、历史数据归档或分区。

  1. 配置与架构

Buffer Pool 给够内存;读写分离扛读流量;热点走 Redis;单表过大就分表,库成为瓶颈再分库。

  1. 验证

优化前后对比 EXPLAIN、执行时间、压测结果,并观察线上监控。

举个例子:订单列表慢,发现 WHERE user_id=? ORDER BY create_time 没合适索引,建了 (user_id, create_time) 联合索引,type 从 ALL 变成 ref,耗时从 2s 降到 50ms。

七、高频追问速答

追问 回答要点
聚簇索引和非聚簇索引? 聚簇:叶子存整行,一张表一个;非聚簇:叶子存主键,需回表
为什么用 B+ 树不用 B 树? 叶子链表便于范围查询;非叶子只存 key,单页能放更多索引,树更矮
覆盖索引是什么? 查询列都在索引中,不需回表,如 (user_id, create_time) 只查这两列
如何定位慢 SQL? 慢日志、pt-query-digestEXPLAIN、Performance Schema
主从延迟怎么办? 并行复制、业务容忍、强一致走主库、关键链路异步补偿
分库分表后怎么做分页? 游标分页、禁止跨库 OFFSET 大分页、搜索引擎(ES)
索引下推(ICP)? 5.6+ 在存储引擎层先过滤,减少回表
一个表多少数据该分表? 没有固定值,一般单表 500 万~2000 万 或索引/维护明显变慢时考虑

八、和 Redis 优化对比(串联记忆)

维度 MySQL Redis
瓶颈 磁盘 I/O、锁、复杂 SQL 内存、单线程、热 key
核心手段 索引、SQL、架构 数据结构、Pipeline、集群
缓存角色 被缓存的对象 做缓存的层
典型组合 MySQL + Redis + 读写分离 缓存热点,保护 MySQL
相关推荐
xlq223223 小时前
11.表的内外连接,索引
mysql
AllData公司负责人3 小时前
数据库同步平台|AIIData数据中台实现OceanBase、达梦数据库、OpenGauss、人大金仓、Hive、TDengine 一键接入Doris
大数据·数据库·hive·mysql·oceanbase·tdengine
wefg14 小时前
【MySQL】事务
数据库·mysql
黑白极客4 小时前
mysql的高可用性
数据库·mysql
heimeiyingwang7 小时前
【架构实战】MySQL索引优化:从慢查询到毫秒响应
数据库·mysql·架构
渣渣灰飞7 小时前
MySQL 系统学习 第二阶段 第四章:DCL(Data Control Language)第二节:权限管理与角色(Role)
数据库·学习·mysql
宠友信息8 小时前
多端即时通讯源码技术实践,Redis路由、MySQL持久化与Socket接入
spring boot·websocket·mysql·uni-app
渣渣灰飞8 小时前
MySQL 系统学习 第四阶段:MySQL 高级 第一节:索引(Index)
数据库·学习·mysql
一孤程9 小时前
PerfDog性能测试实战——从入门到性能优化全覆盖
性能优化·性能测试·测试·perfdog