真正决定索引是否有用的,往往不是 DDL 那一刻建了什么,而是你在查询里把数据列写成了数据库还能不能理解的样子。
where 子句是主战场
use-the-index-luke 把 where 子句视为索引调优的核心,因为索引最擅长的就是“快速定位”。如果 where 写得模糊、绕弯或把列包进表达式里,数据库即使有索引,也可能只能做大范围扫描。对我来说,这一页真正重新定义的是“查询质量”本身: 功能正确远远不够,能不能让优化器看清你的意图才算写对了一半。
多列索引的基本顺序
最实用的经验法则是:先等值,后范围。 这类规则之所以重要,不是因为它能替代思考,而是因为它在大多数真实查询里都对应着 B-tree 怎样缩小搜索区间的物理事实。
- 等值条件越靠前,越能把多列索引收缩到更小子区间;
- 一旦遇到范围条件,后续列通常就更难继续参与真正的访问路径;
- 所以“把选择性最高的列放最左边”不是普适原则,关键是它在查询里扮演等值还是范围角色。
LIKE 只利用第一个通配符之前的前缀
LIKE 'WIN%D' 这类模式中,真正能帮助树遍历的是 WIN 之前的确定前缀;后面的模式匹配更像过滤。
因此我会把 LIKE 看成一个很好的“别把工具用错地方”的提醒:
- 前缀匹配 往往仍可索引;
- 前导通配符(如
'%term')通常破坏索引访问; - 如果本质是在做全文检索,应该考虑专门的全文索引,而不是强迫 B-tree 承担不擅长的任务。
绑定参数与执行计划
绑定参数默认仍然是正确选择,因为它同时解决:
- 安全性:防止 SQL 注入;
- 复用性:让数据库能够复用缓存执行计划。
但这份来源也提醒:当具体值会显著改变数据分布与最佳访问路径时,绑定参数会让优化器看不到那些差异。换句话说,绑定参数通常应该默认使用,但要知道它也会让优化器更“泛化”。 我很喜欢这种不走极端的判断,它不像很多经验贴那样把一个正确习惯误讲成绝对真理。
常见反模式
这些写法都容易让索引失效或弱化,而且很多都属于开发中非常常见的“看起来只是顺手一写”:
- 对列做函数变换再比较;
- 拼接列后再搜索;
- 把日期、数字写成需要隐式转换的形式;
- 用数学表达式要求数据库“反解方程”;
- 用复杂条件分支把真实意图藏起来。
如果不得不用函数,function-based index 是可选项,但它适合的是少数确实稳定、确定性的表达式,而不是把所有糟糕查询都硬塞给索引。我会把它理解成“为一个稳定表达式建访问路径”,而不是“为任意坏写法兜底”。
LIMIT 不会自动改变查询形状
clickhouse-13-mistakes 提醒我,查询形状不只体现在 where 里,也体现在执行是否能流式结束。LIMIT 看起来是在减少结果行数,但如果查询必须先完成非主键排序、全量聚合或跨 shard Top-N,它就只是最后一步裁剪,不会自动减少前面的扫描和计算。
这和索引主题里的判断很一致:只有当查询写法与物理顺序对齐时,数据库才有机会提前停止。否则,“只要 10 行”只是业务语义,不是执行路径。
cursor 条件也要被优化器看懂
这次 ClickHouse scalar 回灌给了我一个很有价值的反例。表的主键是 (projectId, experimentId, key, step),从语义上看,下面这个 cursor 条件完全正确:
WHERE tuple(projectId, experimentId, key, step) > tuple(...)但在 ClickHouse 24.3 的静态源上,EXPLAIN indexes = 1 显示它没有被转换成主键裁剪,结果后段批次每次都读到接近游标之前的全部历史。把同一个语义展开成字典序 OR 后,主键条件才被识别,读放大从十亿行级别降回二千万行级别。
我会把这条经验归纳成一句更通用的话:seek pagination 的正确性不等于访问路径正确。 如果 cursor 写法不能让数据库沿着索引或排序键定位,它就只是一个昂贵过滤器。大批量迁移尤其要用 EXPLAIN 和 query_log 验证 read_rows,不能只看 LIMIT 和业务结果行数。
部分索引
对固定常量条件反复出现的查询,可以考虑 partial / filtered index。例如只索引 processed = 'N' 的消息,而不是把整个消息表都塞进索引。这会同时缩小索引的行数和宽度。我觉得这类设计最能体现“索引不是表的附属物,而是查询模式的物化”。
一个实用判断句
写 SQL 时可以不断问自己:这个条件是在帮助数据库缩小扫描范围,还是只是在它扫完之后再帮忙筛掉一部分结果?
来源:use-the-index-luke-preface · use-the-index-luke-the-where-clause · use-the-index-luke-execution-plans · use-the-index-luke-partial-results · clickhouse-13-mistakes
相关页面:sql-indexing · b-tree-indexes · sql-execution-plans · index-supported-sorting-and-pagination · clickhouse-common-pitfalls · clickhouse-production-migration · clickhouse-13-mistakes