news 2026/6/9 20:01:08

MySQL索引设计避坑指南:这些错误别再犯了

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL索引设计避坑指南:这些错误别再犯了

同事写了个SQL,生产环境跑了8秒,被DBA追着骂。

一看执行计划,全表扫描,100万行数据一行行扫。

“不是加了索引吗?”
“加了,但没用上。”

索引这东西,加得不对比不加还糟糕。整理一下常见的索引坑。

一、索引失效的常见场景

1.1 对索引列做函数运算

-- 索引失效SELECT*FROMordersWHEREYEAR(create_time)=2024;-- 索引生效SELECT*FROMordersWHEREcreate_time>='2024-01-01'ANDcreate_time<'2025-01-01';

对索引列用函数,优化器没法用索引。

同理:

-- 失效SELECT*FROMusersWHEREUPPER(username)='ADMIN';-- 如果经常这样查,建函数索引(MySQL 8.0+)CREATEINDEXidx_username_upperONusers((UPPER(username)));

1.2 隐式类型转换

-- phone是varchar类型-- 失效:传入数字,会发生隐式转换SELECT*FROMusersWHEREphone=13812345678;-- 生效:传入字符串SELECT*FROMusersWHEREphone='13812345678';

类型不匹配,MySQL会做隐式转换,相当于对列做了函数操作。

1.3 前导模糊查询

-- 失效SELECT*FROMproductsWHEREnameLIKE'%手机%';-- 生效(前缀匹配)SELECT*FROMproductsWHEREnameLIKE'手机%';

%放前面,没法用B+树的有序性。

解决方案:

  • 用全文索引
  • 用Elasticsearch

1.4 OR条件

-- 假设只有name有索引,age没有-- 失效SELECT*FROMusersWHEREname='张三'ORage=25;-- 解决方案1:给age也加索引-- 解决方案2:改成UNIONSELECT*FROMusersWHEREname='张三'UNIONSELECT*FROMusersWHEREage=25;

OR条件会导致索引失效,除非OR两边的列都有索引。

1.5 不等于条件

-- 可能失效(取决于数据分布)SELECT*FROMordersWHEREstatus!='completed';-- 如果status只有几个值,考虑改成SELECT*FROMordersWHEREstatusIN('pending','processing','failed');

!=NOT IN通常无法利用索引,或者即使用了也是全索引扫描。

二、联合索引的坑

2.1 最左前缀原则

假设有索引:idx_abc (a, b, c)

-- 生效SELECT*FROMtWHEREa=1;SELECT*FROMtWHEREa=1ANDb=2;SELECT*FROMtWHEREa=1ANDb=2ANDc=3;SELECT*FROMtWHEREa=1ANDc=3;-- 只用到a-- 失效SELECT*FROMtWHEREb=2;SELECT*FROMtWHEREc=3;SELECT*FROMtWHEREb=2ANDc=3;

联合索引必须从最左列开始使用,中间不能跳过。

2.2 范围查询后的列失效

-- 索引:idx_abc (a, b, c)-- c用不到索引SELECT*FROMtWHEREa=1ANDb>10ANDc=3;-- 都能用到SELECT*FROMtWHEREa=1ANDb=10ANDc>3;

范围查询(>, <, BETWEEN, LIKE)会终止后续列的索引使用。

设计索引时,把等值查询的列放前面,范围查询的列放后面。

2.3 索引列顺序

-- 查询1:高频SELECT*FROMordersWHEREuser_id=1ANDstatus='pending';-- 查询2:低频SELECT*FROMordersWHEREstatus='pending';-- 正确设计:user_id放前面CREATEINDEXidx_user_statusONorders(user_id,status);-- 如果反过来,查询1能用,但查询1效率差(要扫描很多user_id)

高频查询的条件列放前面,区分度高的列放前面。

三、覆盖索引

3.1 什么是覆盖索引

-- 索引:idx_user_id_name (user_id, name)-- 覆盖索引:查询的列都在索引里,不用回表SELECTuser_id,nameFROMusersWHEREuser_id=1;-- 非覆盖:需要回表取phoneSELECTuser_id,name,phoneFROMusersWHEREuser_id=1;

覆盖索引避免回表,性能更好。

3.2 利用覆盖索引优化COUNT

-- 慢:需要扫描主键索引SELECTCOUNT(*)FROMusers;-- 快:选择最小的二级索引SELECTCOUNT(*)FROMusersFORCEINDEX(idx_status);

MySQL会自动选择最小的索引来COUNT,但有时选错了需要手动指定。

四、索引设计原则

4.1 选择性高的列优先

选择性 = 不重复的值 / 总行数

-- 查看列的选择性SELECTCOUNT(DISTINCTstatus)/COUNT(*)ASstatus_selectivity,COUNT(DISTINCTuser_id)/COUNT(*)ASuser_id_selectivityFROMorders;-- 假设结果-- status_selectivity: 0.0001(5个状态/10万行)-- user_id_selectivity: 0.8(8万用户/10万行)

user_id选择性高,更适合建索引。

status选择性低,单独建索引意义不大。

4.2 短索引优先

-- 对于很长的字符串,可以只索引前缀CREATEINDEXidx_titleONarticles(title(20));-- 确定前缀长度:保证足够的选择性SELECTCOUNT(DISTINCTLEFT(title,10))/COUNT(*)ASsel_10,COUNT(DISTINCTLEFT(title,20))/COUNT(*)ASsel_20,COUNT(DISTINCTtitle)/COUNT(*)ASsel_fullFROMarticles;

前缀索引更短,同样空间能存更多数据,效率更高。

4.3 避免冗余索引

-- 冗余:idx_a已经被idx_ab覆盖CREATEINDEXidx_aONt(a);CREATEINDEXidx_abONt(a,b);-- 不冗余:idx_ba的顺序不同CREATEINDEXidx_abONt(a,b);CREATEINDEXidx_baONt(b,a);

定期检查冗余索引:

-- MySQL 8.0+SELECT*FROMsys.schema_redundant_indexes;

4.4 避免过度索引

索引不是越多越好:

  • 占用磁盘空间
  • 插入/更新/删除都要维护索引
  • 优化器选择困难

一般一个表不超过5-6个索引。

五、EXPLAIN看执行计划

5.1 关键字段

EXPLAINSELECT*FROMordersWHEREuser_id=1;
字段含义关注点
type访问类型const > eq_ref > ref > range > index > ALL
key实际使用的索引是否用到预期索引
rows预估扫描行数越小越好
Extra额外信息Using index好,Using filesort/temporary不好

5.2 常见type解释

-- ALL:全表扫描,最差EXPLAINSELECT*FROMusersWHEREage=25;-- age没索引-- index:全索引扫描EXPLAINSELECTidFROMusers;-- range:范围扫描EXPLAINSELECT*FROMusersWHEREid>100;-- ref:非唯一索引等值查询EXPLAINSELECT*FROMordersWHEREuser_id=1;-- eq_ref:唯一索引等值查询EXPLAINSELECT*FROMusersWHEREid=1;-- const:主键/唯一索引等值,最多一行EXPLAINSELECT*FROMusersWHEREid=1;

5.3 Extra信息

-- Using index:覆盖索引,好-- Using where:用了WHERE过滤,正常-- Using temporary:用了临时表,需要优化-- Using filesort:用了文件排序,需要优化

看到Using temporary或Using filesort,基本都要优化。

六、真实案例

案例1:订单查询优化

原SQL(执行8秒):

SELECT*FROMordersWHEREuser_id=12345ANDstatus='pending'ANDcreate_time>'2024-01-01'ORDERBYcreate_timeDESCLIMIT20;

EXPLAIN显示:

  • type: ALL
  • rows: 1000000
  • Extra: Using where; Using filesort

问题:没用到索引,全表扫描+文件排序。

优化:

-- 建立联合索引CREATEINDEXidx_user_status_timeONorders(user_id,status,create_time);

优化后EXPLAIN:

  • type: range
  • rows: 234
  • Extra: Using index condition

执行时间:8ms

案例2:分页查询优化

原SQL:

SELECT*FROMlogsORDERBYidDESCLIMIT100000,20;

问题:深分页,要扫描10万行再丢弃。

优化方案1:记录上次ID

-- 前端传上一页最小IDSELECT*FROMlogsWHEREid<12345678ORDERBYidDESCLIMIT20;

优化方案2:延迟关联

SELECTl.*FROMlogs lINNERJOIN(SELECTidFROMlogsORDERBYidDESCLIMIT100000,20)AStONl.id=t.id;

子查询只查ID(覆盖索引),再关联取全量数据。

七、运维小技巧

大表加索引会锁表,生产环境要用pt-online-schema-change或gh-ost:

# pt-online-schema-changept-online-schema-change\--alter"ADD INDEX idx_user_id (user_id)"\--execute\D=mydb,t=orders,h=localhost

我们有几个数据库在不同机房,之前同步DDL操作很麻烦。现在用星空组网把几个节点连起来,统一用Ansible批量执行就方便多了。

总结

索引设计核心原则:

原则说明
最左前缀联合索引从左边开始匹配
范围后失效范围查询列放最后
覆盖索引查询列都在索引里最好
选择性区分度高的列建索引
不过度5-6个索引差不多了

索引失效常见原因:

原因解决方案
函数运算改写SQL或建函数索引
类型转换保持类型一致
前导模糊用全文索引/ES
OR条件改UNION或都加索引
不等于改写成IN

索引优化这块经验欢迎交流~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 3:55:50

从零实现施密特触发器:基于运放的硬件实践

从零实现施密特触发器&#xff1a;一次深入运放核心的硬件实践你有没有遇到过这样的问题——一个看似简单的传感器信号&#xff0c;接入单片机后却频频误触发&#xff1f;明明只按了一次按键&#xff0c;系统却记录了三四次&#xff1b;温度缓慢上升时&#xff0c;ADC读数像抽风…

作者头像 李华
网站建设 2026/6/6 14:01:58

【Open-AutoGLM插件安装全指南】:从零配置到高效运行的5大核心步骤

第一章&#xff1a;Open-AutoGLM插件安装概述Open-AutoGLM 是一款基于 AutoGLM 架构开发的开源自动化机器学习插件&#xff0c;旨在简化大语言模型在垂直场景中的部署与调用流程。该插件支持多种主流框架集成&#xff0c;提供命令行与API双模式操作接口&#xff0c;适用于本地开…

作者头像 李华
网站建设 2026/6/6 20:53:07

Open-AutoGLM沉思MCP落地难题全解析,90%团队忽略的3个致命陷阱

第一章&#xff1a;Open-AutoGLM沉思MCP落地难题全解析在大模型与自动化系统深度融合的背景下&#xff0c;Open-AutoGLM作为基于GLM架构的开源自动推理框架&#xff0c;其与MCP&#xff08;Model Control Protocol&#xff09;协议的集成面临多重现实挑战。协议语义不一致、控制…

作者头像 李华
网站建设 2026/6/6 21:05:25

Dify企业级实战深度解析 (20)

一、学习目标作为整个系列课程的终极收尾篇&#xff0c;本集核心目标是实现 “知识体系闭环、实战问题清零、进阶路径明确、职业发展落地”&#xff1a;系统梳理全系列核心技能与知识脉络&#xff0c;复盘企业级项目实战中的关键难点与解决方案&#xff0c;解答学习与落地中的高…

作者头像 李华
网站建设 2026/6/6 22:38:14

Open-AutoGLM设备选择难题,一文解决算力、存储与扩展性三大瓶颈

第一章&#xff1a;Open-AutoGLM设备需求概述 Open-AutoGLM 是一款面向自动化代码生成与模型推理的开源框架&#xff0c;其运行依赖于特定的硬件与软件环境配置。为确保系统稳定运行并充分发挥性能&#xff0c;部署前需满足一系列基础设备要求。 硬件配置建议 CPU&#xff1a…

作者头像 李华
网站建设 2026/6/7 2:33:48

还在用云端跑GLM?Open-AutoGLM本地部署教程来了,隐私+低延迟一步到位

第一章&#xff1a;Open-AutoGLM本地部署的时代已来随着大语言模型技术的飞速发展&#xff0c;Open-AutoGLM 作为一款开源、可定制的自动化语言生成工具&#xff0c;正逐步成为企业与开发者本地化部署的首选方案。其灵活性、隐私保护能力以及对离线环境的支持&#xff0c;使得在…

作者头像 李华