📄 一句话概括
索引像书的目录——查得快,代价是写入慢一点、多占一块空间;常查的列才值得建。
📝 本节和前面几节的关系
表设计好了、数据也进去了,本节解决【查得慢】这个最日常的痛点。
一、两条路:逐行翻还是直达
没有索引时,DBMS 只能全表扫描:一行行比对,找到为止——数据一多就慢得明显; 建了索引,DBMS 顺着索引的结构直扑目标——和 6.3 的二分查找是同一个思路:先有顺序结构,再有对数级查找; 数据库自己会告诉你走了哪条路——查询计划里 SCAN 是全表扫、SEARCH 是走索引。
二、索引的代价
写入变慢:每插一条、改一条,索引也要跟着维护更新; 多占空间:索引是额外存的一份数据结构,不是白来的; 所以索引不是越多越好——只为【经常用来查】的列建。
三、什么时候建索引
常出现在 WHERE 条件、JOIN 关联、排序(ORDER BY)里的列,优先考虑; 几乎不查的列、本身就很大的文本列,建了也白占地方; 【⚠️ 2026 现状】现代数据库还有自动索引推荐、覆盖索引等技巧,但【用空间与写入换读取】这个权衡,几十年没变。
四、动手 Demo:有索引与没索引的查询计划
同一张表同一句查询,加索引前后让数据库自己报告它走了哪条路。
import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT)")
con.executemany("INSERT INTO books (title) VALUES (?)",
[("书%02d" % i,) for i in range(1, 31)])
plan = "EXPLAIN QUERY PLAN SELECT id FROM books WHERE title='书17'"
for p in con.execute(plan): print(p[3])
con.execute("CREATE INDEX idx_title ON books(title)")
for p in con.execute(plan): print(p[3])
实跑输出:
--------------------------------------------------------------------
索引能够提升数据的检索速度
--------------------------------------------------------------------
查找数据有两条路: 逐行翻(全表扫描) 和 按索引直达。
同一张表, 一次无索引、一次有索引, 看数据库自己的说法:
[无索引] 找 title=书17 的执行计划:
SCAN books
[建索引后] 同样的查询:
SEARCH books USING COVERING INDEX idx_title (title=?)
SCAN = 一行行扫; SEARCH ... USING INDEX = 拿索引直扑目标。
方面 有索引 没索引
---------------------------------------------
读取速度 快(直达) 慢(逐行扫)
写入速度 稍慢(要维护索引) 快
占用空间 额外存一份索引 不占
索引像书的目录: 查得快, 但多印了几页纸——常查的列才值得建。
SCAN 是逐行翻、SEARCH ... USING INDEX 是拿索引直扑目标——查询计划是判断索引有没有生效的第一手证据。 索引与 6.3 的二分查找同源:都是【先花代价造好有顺序的结构,换取查得快】。
30 秒实验
- 说出索引的两项代价:为什么它会让写入变慢、还多占空间?
- 给一张订单表挑一列:哪列最该建索引?(常出现在查询条件里的那列)
常见误区
- 以为索引越多越好——每个索引都要占空间、拖慢写入。
- 以为索引能解决一切慢查询——查整个范围、查不常查的列,索引可能帮不上。
自测三问(点开对答案)
1. 索引的收益与代价各是什么? —— 收益是查得快,代价是写入变慢、多占空间。 2. 数据库怎么告诉你它走没走索引? —— 查询计划:SCAN 是全表扫,SEARCH 是走索引。 3. 索引和二分查找的共同思路? —— 先用代价造好有序结构,换取对数级的查找速度。- ☐ 下一节(8.7 设计用户界面)
相关笔记
- LX_CH6_63 — 二分查找的舞台就是索引思想的原型
- LX_CH5_54 — 典型算法里找数据的老招式
- 💻 计算机是怎样跑起来的 · 索引