首页 游戏 软件 资讯 排行榜 专题
首页
业界动态
十个高效技巧助你快速掌握索引优化方法

十个高效技巧助你快速掌握索引优化方法

热心网友
97
转载
2026-05-18

聊到数据库性能优化,“索引”这个话题永远绕不开。大家都知道索引能加速查询,但具体怎么建、怎么用才能让查询效率最大化,很多人可能只停留在“给WHERE条件加索引”的层面。其实,索引优化是一门精细活儿,用好了是性能利器,用不好反而会成为负担。今天,我们就来系统梳理一下工作中最高频、最实用的十个索引优化技巧。

技巧一:选择高选择性的列作为索引

一个常见的误区是,给所有出现在WHERE子句里的字段都建上索引。比如给“性别”列建索引,想查询所有男性用户,结果发现速度并没提升,甚至更慢了。这背后的关键,在于列的“选择性”。

什么是选择性?

选择性 = COUNT(DISTINCT col) / COUNT(*)。这个比值越接近1,说明该列的唯一值越多,选择性越高。高选择性的列(比如用户ID、订单号)能快速过滤掉大量无关数据,索引价值巨大。而低选择性的列(比如性别、状态码),即便走了索引,每个键值背后也可能对应海量数据,导致大量回表操作,其代价有时甚至超过全表扫描,优化器就会明智地放弃使用索引。

-- 查看字段选择性示例
SELECT
    COUNT(DISTINCT gender) / COUNT(*) AS gender_selectivity,
    COUNT(DISTINCT user_id) / COUNT(*) AS user_selectivity
FROM users;

正确做法

优先在高选择性列上建立索引。如果业务查询必须用到低选择性字段(例如按状态筛选),一个更优的策略是将其放在联合索引的末尾,作为二次过滤条件。

-- 不推荐:单独为低选择性字段建索引
ALTER TABLE orders ADD INDEX idx_status (status);

-- 推荐:将低选择性字段置于联合索引末尾
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);

B+树索引结构示意图

技巧二:联合索引遵循最左前缀法则

建了联合索引 INDEX(a, b, c),查询时却写了 WHERE b = 1 AND c = 2,发现索引失效,这就是没吃透“最左前缀原则”。

最左前缀原则详解

联合索引 (a, b, c) 在物理上是一棵B+树,其数据是先按a排序,a相同再按b排序,b相同再按c排序。因此,查询条件必须从索引的最左列开始,才能利用这棵树的排序结构进行高效查找。跳过最左列,索引就失去了作用。

联合索引最左前缀匹配示意图

查询示例分析

-- 正确:完全匹配所有列,索引效果最佳
SELECT * FROM t WHERE a = 1 AND b = 2 AND c = 3;

-- 正确:匹配最左前列a和b,c虽未用但已过滤大量数据
SELECT * FROM t WHERE a = 1 AND b = 2;

-- 正确:仅匹配最左列a,依然可以使用索引
SELECT * FROM t WHERE a = 1;

-- 错误:跳过了最左列a,索引失效
SELECT * FROM t WHERE b = 2 AND c = 3;

-- 注意:a列使用范围查询后,其后的b、c列无法再使用索引进行精确匹配
SELECT * FROM t WHERE a = 1 AND b > 2 AND c = 3; -- 仅a和b的范围部分走索引

因此,设计联合索引时,应将等值查询的列放在左边,范围查询的列放在右边,并尽量让查询条件覆盖索引的最左前缀。

技巧三:尽量使用覆盖索引,避免回表

“回表”是影响索引查询性能的主要瓶颈之一。InnoDB的二级索引叶子节点存储的是主键值,通过二级索引找到主键后,还需要回到主键索引(聚簇索引)去查找完整的行数据,这个额外的随机IO操作非常耗时。

什么是覆盖索引?

如果一个索引包含了查询语句所需要的全部字段,那么MySQL就可以直接从索引中取得数据,无需回表,这就是“覆盖索引”。它的效率提升往往是数量级的。

-- 低效查询:仅凭id_card索引找到主键后,需回表获取name和age
SELECT name, age FROM user WHERE id_card = 'xxx';

-- 高效方案:创建覆盖索引 (id_card, name, age)
ALTER TABLE user ADD INDEX idx_id_card_name_age (id_card, name, age);
-- 再次执行上述查询,所需数据全在索引中,无需回表

覆盖索引虽好,但会增大索引体积,增加写入时的维护成本。它最适合用于那些查询字段固定且访问频率极高的场景。

技巧四:避免在索引列上使用函数或表达式

在索引列上进行计算或函数操作,会让索引立刻失效。因为B+树是按照列的原始值排序的,对值进行任何加工都会破坏这种有序性。

-- 错误:对索引列使用DATE函数,导致索引失效
SELECT * FROM orders WHERE DATE(create_time) = '2026-01-01';

-- 正确:改为范围查询,可利用索引
SELECT * FROM orders
WHERE create_time >= '2026-01-01 00:00:00'
  AND create_time < '2026-01-02 00:00:00';

其他常见陷阱

-- 陷阱1:隐式类型转换(假设phone字段为VARCHAR类型)
SELECT * FROM user WHERE phone = 13800000000; -- 错误,数字与字符串比较
SELECT * FROM user WHERE phone = '13800000000'; -- 正确

-- 陷阱2:在索引列上进行运算
SELECT * FROM product WHERE price * 0.8 > 100; -- 错误
SELECT * FROM product WHERE price > 100 / 0.8; -- 正确,将运算移至右侧

核心原则是:保持索引列在查询条件中的“纯洁性”,让比较操作直接作用于列本身。

技巧五:利用索引下推减少回表

索引下推(Index Condition Pushdown, ICP)是MySQL 5.6引入的一项重要优化。在没有ICP时,对于联合索引 (name, age) 的查询 WHERE name LIKE '张%' AND age = 25,存储引擎会先通过索引找到所有姓“张”的主键,然后回表取出完整记录,再由Server层过滤age=25的条件。

开启ICP后,存储引擎可以在索引遍历过程中,直接利用索引中包含的列(本例中的age)进行过滤,只对同时满足 name LIKE '张%'age=25 的记录进行回表,从而大幅减少不必要的IO。

-- 查看ICP是否开启
SHOW VARIABLES LIKE 'optimizer_switch';
-- 开启ICP(通常默认已开启)
SET optimizer_switch='index_condition_pushdown=on';

ICP特别适用于联合索引中,前导列使用范围查询(如LIKE)而后续列有等值过滤的场景。

技巧六:避免使用OR,改用UNION或IN

当WHERE条件中使用OR连接不同字段时,很可能导致索引失效,优化器转而选择全表扫描。

-- 可能导致索引失效
SELECT * FROM user WHERE status = 1 OR status = 2;

优化方案通常有两种:

  1. 使用INWHERE status IN (1, 2)。优化器对IN的处理更友好,通常能有效利用索引。
  2. 使用UNION:将OR拆分成多个独立的查询,利用各自的索引,再合并结果。
-- 使用UNION优化
SELECT * FROM user WHERE status = 1
UNION ALL
SELECT * FROM user WHERE status = 2;

注意,UNION 会去重,如果确定结果集无重复,使用 UNION ALL 性能更佳,因为它省去了去重步骤。

技巧七:使用前缀索引节省空间

对于像邮箱、地址这类长字符串字段,建立完整索引会占用大量磁盘空间,并影响写入速度。此时,可以考虑前缀索引,即只对字段的前N个字符建立索引。

-- 为email字段创建前缀索引,只索引前10个字符
ALTER TABLE user ADD INDEX idx_email_prefix (email(10));

关键是如何确定合适的前缀长度?目标是找到选择性接近完整列的最小长度。

-- 计算不同前缀长度的选择性
SELECT
    COUNT(DISTINCT LEFT(email, 5))/COUNT(*) AS sel5,
    COUNT(DISTINCT LEFT(email, 10))/COUNT(*) AS sel10,
    COUNT(DISTINCT email)/COUNT(*) AS total_sel
FROM user;

需要警惕的是,前缀索引无法用于 ORDER BYGROUP BY 操作,也无法实现覆盖索引(因为索引中不包含完整字段值)。

技巧八:定期监控并删除未使用的索引

无效或冗余的索引不仅浪费存储空间,还会降低DML(增删改)语句的性能。常见的冗余情况包括:

  • 已有联合索引 (a,b),又单独创建了索引 (a),后者通常是冗余的。
  • 索引 (a,b)(b,a) 同时存在,虽然顺序不同适用场景不同,但需根据实际查询模式评估是否都需要。

如何查找?

-- 1. 查找可能未使用的索引(需开启performance_schema)
SELECT *
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE object_schema = 'your_db'
  AND index_name IS NOT NULL
ORDER BY count_read, count_write;
-- 长期 count_read 为0的索引可考虑删除

-- 2. 使用sys库查找冗余索引(MySQL 5.7+/MariaDB)
SELECT * FROM sys.schema_redundant_indexes WHERE table_schema = 'your_db';

技巧九:深分页查询优化(延迟关联)

深分页查询 LIMIT 100000, 10 性能极差,因为它需要先扫描并排序100010行,然后丢弃前100000行,只返回最后10行。

一个高效的优化方法是“延迟关联”:先利用覆盖索引快速定位到需要的主键,再通过主键关联回原表获取完整数据。

-- 原始慢查询
SELECT * FROM orders ORDER BY id LIMIT 100000, 10;

-- 优化后的延迟关联写法
SELECT *
FROM orders t1
JOIN (
    SELECT id FROM orders ORDER BY id LIMIT 100000, 10
) t2 ON t1.id = t2.id;

对于数据只增不减的场景(如按时间或自增ID排序),“游标分页”是更优解:记录上一页最后一条记录的ID,下次查询时直接定位。

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

技巧十:定期分析表,更新统计信息

MySQL优化器依赖表的统计信息(如数据分布、基数)来选择它认为最优的执行计划。如果统计信息过时,优化器就可能做出错误判断,例如选择了全表扫描而非更快的索引。

在以下情况后,建议手动更新统计信息:

-- 更新表的统计信息
ANALYZE TABLE orders;

-- 重建表并整理碎片(适用于大量删除操作后)
OPTIMIZE TABLE orders;

虽然MySQL 8.0默认开启了统计信息的持久化和自动更新,但在进行大规模数据变更后,手动执行一次 ANALYZE TABLE 仍是稳妥的做法。

总结

说到底,索引是一把双刃剑。设计得当,它是提升查询性能的“屠龙刀”;盲目滥用,它就会变成拖慢系统、浪费资源的“自残剑”。最稳妥的策略是:创建索引前,务必用 EXPLAIN 分析执行计划;索引创建后,则要借助监控系统定期清理无效和冗余的索引。保持索引的简洁与高效,是数据库性能稳定的重要基石。

来源:https://www.51cto.com/article/843407.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

十个高效技巧助你快速掌握索引优化方法
业界动态
十个高效技巧助你快速掌握索引优化方法

索引优化是提升数据库查询性能的关键。应优先为高选择性列建立索引,联合索引需遵循最左前缀原则。尽量使用覆盖索引避免回表,并避免在索引列上进行函数运算。可利用索引下推减少回表,用IN或UNION替代OR条件。长字符字段可考虑前缀索引以节省空间。需定期清理未使用索引,优化深分页查询。

热心网友
05.18
MySQL 函数索引避坑指南:别让函数毁了你的索引!
业界动态
MySQL 函数索引避坑指南:别让函数毁了你的索引!

MySQL函数索引:解决“索引列被函数操作后失效”的利器 你是否遇到过这样的场景:明明给字段建了索引,可查询时只要加个简单的函数操作——比如用DATE(create_time)提取日期,或者用UPPER(name)转换大小写——执行速度就瞬间变慢?用EXPLAIN一看,key字段显示为NULL,索引

热心网友
04.17
卡帕西引爆硅谷!公开「第二大脑」黑科技,1250万人围观
AI
卡帕西引爆硅谷!公开「第二大脑」黑科技,1250万人围观

新智元报道编辑:犀牛【新智元导读】Karpathy公开个人知识管理新范式:让大模型把你的一切资料「编译」成一部活的百科全书——RAG已死,人类只需负责思考。就在这两天,AI圈又被一个人引爆了。不是S

热心网友
04.07
Karpathy知识库「LLM Wiki」火爆了,全网围观讨论
AI
Karpathy知识库「LLM Wiki」火爆了,全网围观讨论

机器之心编辑部还记得前几天,AI 领域知名学者 Andrej Karpathy 做客一档节目时,半开玩笑地提到:token 用不完会让人焦虑,就像患上了某种「AI 精神病」。这句话当时听起来有点夸张

热心网友
04.07
Win11搜索功能即将优化!微软承诺简化结果、修复排序
科技数码
Win11搜索功能即将优化!微软承诺简化结果、修复排序

快科技3月30日消息,微软Windows Shell产品负责人Tali Roth在社交平台确认,Windows 11搜索功能将迎来大改进,重点解决搜索结果排序混乱、网页内容干扰本地结果、界面信息过载

热心网友
03.30

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

斯柯达晶锐Fabia Motorsport特别版车型正式发布
业界动态
斯柯达晶锐Fabia Motorsport特别版车型正式发布

为庆祝品牌投身赛车运动整整125年,斯柯达正式推出了晶锐Fabia Motorsport Edition特别版。这款车基于Fabia 130打造,设计灵感直接来源于征战赛场的Fabia RS Rally2拉力赛车,整体风格充满了对赛事历史的致敬意味。不过,得先说明白,它的升级重点主要落在了外观和底盘

热心网友
05.18
灰度以太坊质押ETF持仓超10万枚ETH 价值2.37亿美元
web3.0
灰度以太坊质押ETF持仓超10万枚ETH 价值2.37亿美元

Grayscale 通过其以太坊质押 ETF 质押了 102,400 个 ETH,价值 2 37 亿美元 先来看一组数据:资产管理巨头 Grayscale 最近通过其以太坊质押 ETF,一口气质押了超过10万个 ETH,价值约2 37亿美元。这个动作本身不小,但更有意思的是市场的后续反应——或者说,

热心网友
05.18
劳斯莱斯库里南防弹版发布 Inkas打造隐形防护座驾
业界动态
劳斯莱斯库里南防弹版发布 Inkas打造隐形防护座驾

劳斯莱斯库里南自问世以来,始终是超豪华全尺寸SUV领域的标杆。对于追求极致安全又不愿牺牲低调气质的高净值人士而言,如何实现“隐形”的顶级防护,一直是核心诉求。如今,加拿大专业防弹车制造商Inkas,以一款近乎“零痕迹”改装的库里南,给出了完美解决方案——一座移动的“隐形堡垒”。 区别于常见的外露装甲

热心网友
05.18
GTA5与荒野大镖客2高清复刻版或将登陆Switch平台
游戏资讯
GTA5与荒野大镖客2高清复刻版或将登陆Switch平台

新加坡维塔士工作室正考虑将《侠盗猎车手V》与《荒野大镖客:救赎2》移植至任天堂Switch平台。该团队拥有丰富的移植经验,曾成功负责多款游戏的跨平台适配。这两款作品全球销量巨大,若能登陆Switch,其便携特性可能成为新的市场增长点。

热心网友
05.18
大众ID. Polo GTI全球首发亮相 高尔夫GTI刷新纽北赛道纪录
业界动态
大众ID. Polo GTI全球首发亮相 高尔夫GTI刷新纽北赛道纪录

当高尔夫GTI迎来五十周年里程碑,传奇的纽博格林北环赛道成为其致敬历史与展望未来的最佳舞台。这里不仅铭刻了燃油性能图腾的巅峰时刻,也正式开启了电动GTI的新纪元。近日,大众汽车正式宣布,高尔夫GTI 50周年版在纽北创下全新纪录,荣膺最快前驱量产车称号;与此同时,品牌首款纯电动GTI车型——ID

热心网友
05.18