一个数据导出任务在数据量涨到百万行后,单次查询从几十毫秒涨到八百多毫秒。排查下来不是 SQLite 不行,而是查询写法没有匹配索引。
慢在哪里
原始查询长这样:
SELECT * FROM events
WHERE strftime('%Y-%m', created_at) = '2026-03'
ORDER BY created_at DESC;
问题在于对 created_at 做了函数包裹,索引无法使用,只能全表扫描。
改成范围查询
把函数比较改成范围条件,让索引直接命中:
SELECT * FROM events
WHERE created_at >= '2026-03-01'
AND created_at < '2026-04-01'
ORDER BY created_at DESC;
这一步之后,查询时间降到了 120ms 左右。
再补一个覆盖索引
导出只需要 id、时间和状态,那就建一个只包含这些字段的索引:
CREATE INDEX idx_events_export
ON events(created_at DESC, status, id);
SQLite 可以直接从索引里读取字段,不用回表,最终稳定在 30ms 左右。
| 阶段 | 耗时 |
|---|---|
| 原始查询 | 约 800ms |
| 范围查询 | 约 120ms |
| 覆盖索引 | 约 30ms |
排查工具是
EXPLAIN QUERY PLAN,先看扫描方式,再决定改 SQL 还是加索引,顺序不要反过来。
最后把结论写进规范:时间字段永远不要包在函数里比较,日期过滤一律用半开区间。