Appearance
索引入门:前端也该懂的“为什么查询这么慢”
更新: 7/19/2026 字数: 0 字 时长: 0 分钟
你有没有遇到过这种情况:一个列表接口,数据量小的时候唰一下就出来了,等数据涨到几十万条,同样的接口突然要转好几秒;或者一个筛选下拉,选了某个条件后页面就卡住不动。你去问后端,对方来一句"这个查询没走索引"。索引到底是什么,为什么它一失效查询就慢成这样?这篇就从前端能听懂的角度讲清楚。

一、从一个前端场景说起
先看两个你天天遇到的现象:
- 列表接口越用越慢:表里 1 万条数据时接口很快,涨到 50 万条后同样的查询要等 3 秒。
- 加了筛选就卡:列表全量拉取正常,一旦带上"按手机号搜索""按状态过滤",响应时间反而暴涨。
这背后往往是同一个原因:数据库在"一条条翻着找"你要的数据,而不是"直接跳到"目标位置。 而决定它是翻找还是直达的,就是索引。
二、索引的本质:用前端最熟的两个类比
类比一:数组遍历 vs 对象查找
这个类比前端一秒就懂。假设你要从 10 万条用户里找 id === 88888 的那个:
js
// 没有索引:全表扫描 = 数组 find,最坏遍历 10 万次
const user = users.find(u => u.id === 88888);
// 有索引:哈希/树查找 = 对象按 key 取值,几乎一步到位
const user = usersMap[88888];find 要从头遍历,数据量翻倍耗时就翻倍(O(n));而 usersMap[key] 不管有多少数据,几乎一步命中(接近 O(1) 或 O(log n))。数据库没有索引时的查询,就等于在跑那个 find;建了索引,就变成了那个 usersMap 取值。 数据量越大,两者差距越夸张——这就是"数据量小不卡、数据量大就慢"的根本原因。
类比二:书的目录

想在一本 500 页的书里找"索引"这一节:
- 没有目录:从第 1 页开始一页页翻,直到翻到为止——这就是全表扫描(Full Table Scan)。
- 有目录:翻到目录,看到"索引 → 第 210 页",直接跳过去——这就是走索引。
数据库的索引(常见是 B+ 树结构)就是这本"目录"。它把某个字段(比如 phone)提前排好序、建成一棵便于快速定位的树。查询时不用扫全表,顺着树几步就能定位到数据所在的位置。
关键认知:索引是"额外维护的一份排好序的目录",它加快了查询,但也有代价——每次增删改数据,这份目录也要跟着更新。所以索引不是越多越好,这点后面会讲。
三、为什么"加了筛选反而更慢"——索引失效场景
很多前端会疑惑:后端明明说这个字段建了索引,为什么带上它查询还是慢?答案通常是:你的查询方式让索引"失效"了,数据库有目录却用不上,只能退回全表扫描。下面这几种是最常见的,而且不少和前端传参、拼接查询条件直接相关。

1. 前导模糊查询(LIKE '%xxx')
sql
-- 走不了索引:开头是 %,等于目录没法用首字母定位
SELECT * FROM users WHERE name LIKE '%三';
-- 能走索引:左边固定,相当于"按姓氏查目录"
SELECT * FROM users WHERE name LIKE '张%';类比:目录是按字母/拼音顺序排的,你知道开头才能快速定位。%三 相当于"我不知道开头,只知道结尾",目录帮不上忙,只能一条条翻。前端做搜索框时,如果是这种"包含匹配"的模糊搜索,要意识到它在大表上天然慢,量大时应该考虑搜索引擎(如 ES)而不是硬用 LIKE。
2. 在字段上做运算或用函数
sql
-- 索引失效:对字段本身做了计算
SELECT * FROM orders WHERE YEAR(created_at) = 2026;
-- 改写后能走索引:让字段保持"干净"
SELECT * FROM orders WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01';类比:目录是按"字段原本的值"排序的。你一旦对字段做了加工(YEAR(created_at)),就好比问"哪些书名的字数是 5"——目录是按书名排的,不是按字数排的,自然用不上。
3. 类型不匹配(前端最容易踩)
sql
-- phone 字段是字符串类型,但传了数字,发生隐式类型转换,索引失效
SELECT * FROM users WHERE phone = 13800138000;
-- 正确:类型对齐
SELECT * FROM users WHERE phone = '13800138000';这条和前端强相关。当你把参数传给后端时,一个本该是字符串的手机号/ID 传成了数字(或反过来),数据库做隐式转换,索引就失效了。联调时如果某个带参查询莫名很慢,先怀疑传参的类型对不对。
4. 复合索引不满足"最左匹配"
如果后端在 (status, created_at) 上建了复合索引(联合索引),那么:
sql
-- 能用上索引:从最左边的 status 开始
SELECT * FROM orders WHERE status = 1 AND created_at > '2026-01-01';
-- 用不上(或只能部分用):跳过了最左的 status
SELECT * FROM orders WHERE created_at > '2026-01-01';类比:复合索引像"先按省份、再按城市"排序的通讯录。你知道省份能快速缩小范围;但如果只知道城市不知道省份,这个排序方式就帮不上忙了。前端的启示:筛选条件的组合不是随便传的,后端建的索引决定了"哪些筛选组合快、哪些慢",遇到某个筛选组合特别慢,可以和后端确认是不是缺了对应的索引。
5. 其他常见失效情况(快速了解)
OR连接的条件里有未建索引的字段 → 整条可能退化为全表扫描。!=、NOT IN、IS NOT NULL这类"否定"条件 → 往往用不上索引(目录擅长"找到什么",不擅长"排除什么")。- 返回字段太多、数据量太大 → 即使命中索引,回表和传输开销也会拖慢。
四、前端能做的优化与排查思路
索引主要是后端的活,但前端并非只能干等。以下几点在日常开发和联调中很实用:
- 分页别偷懒:确保每个列表接口都带
limit,不要让后端一次返回上万条。深翻页(offset很大)本身也慢,超大数据量列表建议改用"游标/上一页最后一条 id"的方式,和后端约定好。 - 筛选条件要克制:前端提供的筛选/排序维度越随意,后端越难为每种组合都建索引。高频筛选组合提前和后端对齐,让它们能命中索引;低频的花式筛选要有"可能会慢"的心理预期。
- 搜索框区分场景:精确匹配(手机号、订单号)交给索引没问题;"包含关键词"的模糊搜索在大表上要慎用,数据量大时推动后端上搜索方案。
- 传参类型对齐:ID、手机号这类字段,和后端约定好到底传字符串还是数字,避免隐式转换导致的索引失效。
- 联调时学会看一句话:后端排查慢查询会用
EXPLAIN(执行计划)。你不用会写,但看到结果里type=ALL基本就是"全表扫描没走索引",key那列为空说明没用上索引——知道这两个信号,就能和后端高效对话,而不是只会说"这个接口好慢"。

五、总结
- 索引的本质:一份"排好序的目录",让数据库从"数组
find全表遍历"变成"对象按 key 直取",数据量越大优势越明显。 - 查询慢的常见根因:没建索引,或者查询写法让索引失效了——前导模糊
%xxx、字段上做运算/函数、类型不匹配、复合索引不满足最左匹配、否定条件等。 - 和前端强相关的两个点:传参类型要对齐(别让字符串字段收到数字),模糊搜索在大表上天然慢(该上搜索引擎就别硬用
LIKE)。 - 前端能做的:分页限流、克制筛选维度、精确/模糊搜索分场景、联调时看懂
EXPLAIN里的type和key,把"这个接口好慢"升级成"这个查询好像没走索引"。
理解索引之后,你会发现很多"玄学卡顿"其实都有迹可循——它不是前端渲染的锅,而是数据在"翻书"还是"查目录"的区别。