Administrator
Administrator
发布于 2026-10-27 / 0 阅读
0
0

数据库入门到初级(19):索引入门:查询为什么会变快,也可能变慢

数据库入门到初级 · 第 19/22 篇。实验口径:MySQL 8.4、InnoDB、utf8mb4。案例:小型商城。

查一本厚书,目录能帮你直接找到章节;没有目录,就可能从第一页往后翻。索引也保存便于查找的数据结构,但它并非免费:新增、修改和删除记录时,相关索引也需要维护。

1. 索引不是给字段加一个“加速开关”

MySQL InnoDB 的普通索引通常使用 B-tree 组织,常以 B+ 树结构解释。它按键值顺序组织条目,适合等值查找、范围查找和部分排序场景。

主键索引与二级索引有不同作用。InnoDB 主键索引叶子记录保存行数据,二级索引条目通常包含对应主键值;如果二级索引里没有查询所需的全部列,还可能需要再取行数据,常称“回表”。

联合索引的排序规则与维护成本

这里只建立直觉,不要求先背树的所有细节。要判断索引是否有用,最终仍需看查询条件、数据分布和执行计划。

2. 从一条真实查询开始

SELECT id, created_at
FROM orders
WHERE user_id = 1
ORDER BY created_at DESC, id DESC
LIMIT 20;

需求是“某用户最新的 20 张订单”。可以考虑:

CREATE INDEX idx_orders_user_created_id
ON orders (user_id, created_at, id);
SHOW INDEX FROM orders;

只创建一次,重复同名会报错。这个索引先按用户分区间,在相同用户内按时间和编号排列。虽然定义是默认升序,MySQL 对这个方向一致的倒序查询可以考虑反向扫描;是否采用还要看计划。

练习数据只有 4 张订单,即使出现全表扫描也不奇怪。小表扫描可能就是低成本选择,不能据此认定索引失效。

3. 联合索引与左侧前缀

对 (user_id, created_at, id),按 user_id 或 user_id + created_at 查找,通常能直接利用有序前缀。只按 created_at 查,不能简单假设与单独时间索引一样好用。

但也不能说“没用第一列就绝对不可能使用索引”:优化器可能选择索引扫描或跳跃扫描等策略。初级阶段先理解排序顺序,再用计划验证具体访问方式。

WHERE 中条件书写的先后顺序,不等于索引列顺序。把 created_at 写在 user_id 前面,不会自动让联合索引失效,优化器会分析条件。

4. 覆盖索引是什么

当查询所需信息已经包含在可用索引条目里,就可能无需额外取完整行。上面的查询只取 id 和 created_at,与索引比较匹配。

如果又加上 total_amount、status,可能需要读取行数据。不要为所有展示列建立一个极宽索引;覆盖带来的收益要与索引空间、写入成本一起权衡。

5. 这些写法需要特别留意

SELECT id FROM orders
WHERE created_at >= '2026-01-10 00:00:00'
  AND created_at <  '2026-01-11 00:00:00';

对时间范围,直接比较原字段通常更利于使用合适的时间索引。若写 DATE(created_at) = ...,普通字段索引通常不能以相同方式直接定位该表达式的范围;函数索引等是另一个设计问题。

此外,LIKE '%关键词%'、类型不匹配导致的隐式转换、返回绝大多数记录等情况,都可能使索引无法带来预期收益。不要用“某运算符一出现必然失效”代替执行计划分析。

6. 哪些索引不值得随手加

只有 0/1 的状态字段单独建索引,不一定能有效缩小扫描范围;若某值极少并匹配业务查询,仍可能有用。重复或被其他联合索引覆盖的索引也需要检查,但涉及外键依赖时不要直接删除。

每次加索引都应有目标查询、代表性数据和前后比较。查询变快,写入是否变慢,空间是否明显增加,也要纳入判断。

练习与答案

练习:经常查某用户某个时间区间的订单,先考虑什么联合索引? 可从 (user_id, created_at) 开始评估;若还要稳定排序和分页,再结合主键与实际计划考虑。它是候选方案,不是无需验证的固定答案。

延伸阅读


上一篇:第 18 篇 · 系列目录 · 下一篇:第 20 篇


评论