看到jsonb列已经建立GIN索引,开发者可能以为围绕这列的所有查询都能直接使用它。但索引列存在,并不等于任意子表达式都已经被索引。排查时先看条件作用于什么对象,通常比反复确认“有没有索引”更有意义。
PostgreSQL 17的JSON类型文档给出一个具体例子:对jdoc整列建立GIN索引后,条件jdoc -> 'tags' ? 'qui'中的问号运算符作用于取出的tags表达式,而非直接作用于jdoc,因此该整列索引不能用于文中这一写法。这个解释只针对所示结构,不能推广成jsonb子对象查询一概无法使用索引。
同节继续说明,针对tags表达式建立相应索引是一种思路,改用文档包含条件则是另一种思路;两者保存的数据范围不同。对维护人员而言,这提示首先要确认查询意图和被索引对象是否对应,而不是在没有理解语义时只替换一个运算符。本文不为读者的实际数据库选择最终方案,也未执行任何索引变更。
准备复核资料时,可以同时保留表结构、索引定义、原始查询和计划输出。若只提供一张“没有走索引”的截图,别人可能无法知道截图对应哪个索引、哪个表达式以及哪一版查询。数据分布、统计信息和实际工作负载尚未提供时,也不宜从文档示例推导项目的速度收益。
执行计划的证据层次还应分清。17版EXPLAIN文档说明,普通计划中的成本是规划器使用的估计单位,不是直接测得的毫秒数;ANALYZE选项则会实际执行查询,报告实际行数和时间。后者不是一个只改变显示格式的开关,因此不能把未执行的计划阅读写成已经完成的性能实测。
技术交接时可以分别写下三个结论:表达式为何与索引不匹配,文档有哪些可供评估的方式,项目还缺什么验证。只有最后一步取得真实数据后,才讨论某种方案是否更适合当前场景。官方回归库的样例结果,也不能移植成自己项目的测试成绩。
这篇文章没有连接生产库、运行查询或建立索引。它解释的是一个定位顺序:先核对运算符作用的表达式,再看索引覆盖,最后区分估计与实测。每一层使用自己的证据,能避免把“存在GIN索引”误写成所有查询都已得到优化。
信息来源
- PostgreSQL Global Development Group:PostgreSQL 17 — 8.14. JSON Types
- PostgreSQL Global Development Group:PostgreSQL 17 — 14.1. Using EXPLAIN
本文基于上述公开资料整理,未使用来源页面的图片、视频或嵌入媒体。