MySQL 如何提高查询速度:从索引、SQL、表结构到实战调优

前言

MySQL 查询慢是后端开发最常遇到的问题。很多人遇到慢SQL第一反应就是加索引,但索引不是万能解药,不合理的索引、糟糕的SQL写法、不当的表结构,依旧会导致全表扫描。

本文从原理到实战,梳理MySQL查询优化整套思路,覆盖索引、SQL编写、表设计、配置、业务架构层面,附带常见踩坑案例,适合开发自查慢查询。

环境:MySQL5.7 / 8.0,InnoDB引擎。

一、先找到慢SQL:优化的前提

优化不是凭感觉,第一步是定位慢查询。

1. 开启慢查询日志

sql 复制代码
-- 查看慢查询配置
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';
  • slow_query_log=ON 开启慢日志
  • long_query_time:阈值,单位秒,超过该时间的SQL会被记录,开发环境建议设置0.5s
  • log_queries_not_using_indexes=ON:记录没有使用索引的SQL

2. explain 分析执行计划

EXPLAIN 是最核心工具,放在SELECT前面,看SQL如何执行。

sql 复制代码
EXPLAIN SELECT * FROM user WHERE phone = '13800138000';

重点看这几个字段:

  1. type:访问类型,性能从优到劣:system > const > eq_ref > ref > range > index > ALL,ALL代表全表扫描,需要优化
  2. key:实际使用的索引,NULL代表没有用到索引
  3. rows:预估扫描行数,数值越大越危险
  4. Extra:额外信息,Using filesort(文件排序)、Using temporary(临时表)都是性能杀手

优化目标:尽量避免 type=ALL,消除 Using filesort、Using temporary。

二、索引优化:提升查询最核心手段

InnoDB是B+树索引,索引的本质是减少扫描行数。

1. 最左前缀原则(联合索引重中之重)

联合索引 idx_a_b_c(a,b,c),生效顺序:从左往右匹配,遇到范围查询(> < like %xx)停止。

✅ 可以命中索引:

where a=?、where a=? and b=?、where a=? and b=? and c=?

❌ 无法命中:

where b=?、where c=?,跳过最左列,索引失效。

建索引顺序原则:等值条件放前面,范围条件放最后。

2. 索引失效常见场景(高频踩坑)

  1. 索引列参与运算、函数操作
sql 复制代码
-- 失效!索引列做函数
SELECT * FROM user WHERE DATE(create_time) = '2026-10-01';
-- 改写,把函数移到常量一侧
SELECT * FROM user WHERE create_time >= '2026-10-01' AND create_time < '2026-10-02';
  1. 隐式类型转换:字符串字段用数字查询
sql 复制代码
-- phone是varchar,传入数字,触发隐式转换,索引失效
SELECT * FROM user WHERE phone = 13800138000;
  1. LIKE '%关键词',前缀模糊匹配,索引失效;LIKE '关键词%' 可以走索引
  2. OR 两边字段有一方无索引,整个索引失效
  3. 优化器判断全表扫描更快,主动放弃索引(数据量占比过高)

3. 不要盲目建索引

索引优点:加快查询;缺点:占用磁盘,降低写入性能(INSERT/UPDATE/DELETE要维护索引)。

  • 一张表索引不宜过多,建议不超过6个;
  • 区分度低的字段不要建索引(性别、状态只有0/1,索引几乎无效);
  • 优先使用联合索引,减少单字段冗余索引。

4. 覆盖索引(避免回表)

InnoDB二级索引叶子节点存主键,查到索引后需要回主键索引读取完整行,叫回表。

覆盖索引:索引包含查询需要的所有字段,不需要回表,性能极高。

sql 复制代码
-- 需求:只查id,username,phone
-- 建立联合索引 idx_username_phone (username, phone)
SELECT id,username,phone FROM user WHERE username='zhangsan';

Explain Extra列会出现 Using index,代表命中覆盖索引。

三、SQL写法优化:改掉坏习惯

1. 禁止 SELECT *

只查询业务真正需要的字段。

  • 减少网络传输;
  • 有机会触发覆盖索引,避免回表;
  • 减少内存占用。

2. 分页大偏移优化

OFFSET 100000 LIMIT 10,会先扫描10万行再丢弃,性能极差。

❌ 低效写法

sql 复制代码
SELECT * FROM order LIMIT 100000,10;

✅ 主键书签优化(推荐)

sql 复制代码
SELECT * FROM order WHERE id > 100000 LIMIT 10;

如果主键不连续,也可以用子查询先定位主键:

sql 复制代码
SELECT t.* FROM order t
INNER JOIN (SELECT id FROM order ORDER BY id LIMIT 100000,10) AS tmp ON t.id = tmp.id;

3. 优化JOIN关联查询

  1. 小表驱动大表,INNER JOIN 优化器会自动选择,但尽量保证关联字段建立索引;
  2. JOIN字段类型、字符集必须一致,防止隐式转换;
  3. 避免多表JOIN,超过3张表关联建议业务层拆分;
  4. 不要在子查询里写大表,EXISTS 适合大表,IN 适合小集合。

原则:IN 集合数量不要过大,建议控制在1000以内。

4. 排序与分组优化

ORDER BY、GROUP BY 尽量利用索引有序特性,避免 Using filesort。

  • 排序字段放到联合索引末尾;
  • 尽量减少排序字段;
  • 大数据量分组优先在业务层处理。

四、表结构设计优化(从源头减少慢查询)

1. 字段类型选择,越小越好

  • 整型优先用 TINYINT / INT,不要全部用BIGINT;
  • 字符串优先 VARCHAR,不要用TEXT;
  • 非必要不使用大字段TEXT/BLOB,大字段会单独存储,读取开销大;
  • 时间优先DATETIME,不要用VARCHAR存储时间。

2. 主键设计

InnoDB主键是聚簇索引:

  • 主键推荐自增INT/BIGINT,无序主键(UUID)会造成页分裂,写入变慢;
  • 主键尽量短,二级索引会携带主键,主键越长,二级索引体积越大。

3. 分表分区

单表数据量千万级以上,索引体积变大,查询性能下降。

  • 分区表:适合单表冷热数据分离,按时间分区(日志、订单);
  • 水平分表:按用户ID、订单ID哈希拆分,适合超大数据量;

注意:分区、分表会增加业务复杂度,不要过早优化。

五、MySQL配置层面调优

核心参数(根据服务器内存调整)

  1. innodb_buffer_pool_size:最重要参数,InnoDB缓存池,推荐服务器物理内存的50%~70%,缓存索引和数据页,减少磁盘IO;
  2. innodb_log_file_size:redo log大小,提升写入性能;
  3. sort_buffer_size、join_buffer_size:会话级缓存,不要设置过大,容易内存溢出;
  4. max_connections:连接数,按需调整,不要盲目开大。

调优原则:优先调 innodb_buffer_pool_size,大部分性能瓶颈是磁盘IO。

六、业务架构层面优化(终极方案)

当SQL、索引、参数优化到极限,还慢,就要考虑架构方案:

  1. 读写分离:主库写,从库承担查询压力;
  2. 缓存:热点数据放Redis,拦截高频查询,直接绕开MySQL;
  3. 预计算:报表、统计类查询不要实时查MySQL,定时预计算结果存入汇总表;
  4. 搜索引擎:全文检索、多条件复杂检索,迁移到Elasticsearch,减轻MySQL压力。

七、常见误区

  1. 索引越多查询越快:错,索引会拖慢写入;
  2. EXPLAIN看到索引就一定很快:要看扫描rows,索引区分度差依旧慢;
  3. 调优只改参数,不改SQL:绝大多数慢查询根源是SQL和索引;
  4. 用缓存就不用优化SQL:缓存失效瞬间,大量请求打满数据库,雪崩风险。

八、优化通用步骤总结

  1. 开启慢查询日志,捕获慢SQL;
  2. EXPLAIN分析执行计划,定位全表扫描、文件排序;
  3. 优化SQL写法,调整索引,优先利用覆盖索引;
  4. 优化表结构,精简字段;
  5. 调整MySQL核心配置;
  6. 业务层引入缓存、读写分离、分表等架构方案。

小结

MySQL查询优化是分层的:优先优化SQL和索引,其次优化表结构,再调整数据库参数,最后才考虑架构改造 。

很多时候一个不合理的WHERE条件、失效的索引,就足以让数据库性能下降几十倍。优化前一定要用explain验证,不要凭经验猜测。

相关推荐
郑州光合科技余经理1 小时前
出海外卖平台:支付对接为什么要和下单收款分开
android·java·开发语言·前端·后端·php·ai编程
终端安全笔记1 小时前
配置锁状态那一栏查的是什么:报告里几项各自指向什么风险
android·前端·安全·ios
恋猫de小郭1 小时前
Kotlin Toolchain 0.13,JetBrains 推荐 KMP 都切换到到全新工具链
android·前端·flutter
高级程序源1 小时前
hive电商平台商品销售数据分析与趋势预测45659-计算机课程设计、毕业设计
vue.js·hive·spring boot·mysql·数据分析·django·课程设计
for_ever_love__2 小时前
LangChain Text-to-SQL:让不会写 SQL 的人查数据库
数据库·sql·langchain·大模型·agent·rag·问答
安卓机器2 小时前
rom定制系列------小米8 安卓16线刷原生固件 体验新版本
android·小米8安卓16
cyy2982 小时前
小米电视安装tts引擎
android·tv
小锋学长生活大爆炸2 小时前
【工具】在 Android 手机上运行 Docker
android·docker·智能手机
Carson带你学Android2 小时前
OpenAI Dots 登陆手机端:手机上派任务,AI 在云端写代码
android·ios