侧边栏壁纸
博主头像
一笑痕

仙人之下我无敌,
仙人之上一换一。

  • 累计撰写 62 篇文章
  • 累计收到 7 条评论

2000 行的小表加索引,count 快了 6 倍

2026-9-18 / 0 评论 / 3 阅读

2000 行的小表加索引,count 快了 6 倍

「表小就不用建索引」这句话传得很广。到底多小算小,没人说得清。下面把同一份 2000 行的数据在两种库里各查一遍,一个有索引一个没有,数据表放在前面,解释放在后面。环境是 Python 3.11 自带的 SQLite 3.53.1,macOS(APFS),验证脚本在 tools/verify-sqlite-index-count.py

查询是哪一条

两张库结构完全一样,一张的 cat 列建了索引,一张没建。查询是:

SELECT count(*) FROM t WHERE cat = 'b3';

cat 一共 7 种取值,其中 b3 占 286 行。每个库都连跑 5 次取最小值,避开首次缓存的干扰。

数据表

无索引: 命中 286 行,5 次取最小 0.071 ms
有索引: 命中 286 行,5 次取最小 0.011 ms

有索引库的查询计划:
  SEARCH t USING COVERING INDEX idx_t_cat (cat=?)

把行数抬到 20 万行,同一个查询再各跑一次:
无索引 20 万行: 命中 28571 行,5.0 ms
有索引 20 万行: 命中 28571 行,0.354 ms

数字在说什么

2000 行的表,有索引比没索引快 6 倍多一点。20 万行的时候差距拉开到 14 倍。两个比值都不是线性的,原因是无索引那条路的成本里有一块固定开销(打开表、准备扫描),行数少的时候这块占大头,行数多了扫描本身才显出来。

有索引那条路的查询计划值得看一眼:COVERING INDEX。查询只用到 cat 这一列,而索引里正好就存着这一列的值,整条查询根本没碰过表本身,只扫了索引的一小段。这就是 count 类查询从索引里占到便宜的原因,它要的恰好就是索引有的。

查询计划怎么看

无索引的库上跑 EXPLAIN QUERY PLAN,输出是 SCAN t,全表扫。有索引的库上是 SEARCH ... USING COVERING INDEX,按索引定位。建索引之前先跑一遍 explain,一眼就能看出这条查询走的是哪条路,不用猜。

别急着给每列都建

索引不是白给的。每次 INSERT 和 UPDATE 都要顺手维护索引,写入越多的表,这层开销越明显。上面这个实验里的查询是固定的一条,实际业务里查询五花八门,给每种条件都建索引,最后就是一排没人用还得养着的索引。SQLite 可以用 EXPLAIN QUERY PLAN 逐条确认哪些查询真的用上了索引,用不上字的就别建。

这一点没实测写入侧的损耗数字,只测了读侧,写在前面的是读侧结论。

    🤞 分享