数据库索引必须匹配真实查询负载
说明索引如何缩小扫描范围,并从选择性、列顺序、写入成本和执行计划判断索引是否有效。
数据库索引必须匹配真实查询负载
数据库索引用额外的数据结构把键值映射到表中的行位置,使查询不必逐行扫描全部数据。它能显著降低某些读取成本,但也占用存储、增加写入和维护工作。索引设计不是“给常用列加索引”,而是让具体查询条件、排序和返回列与合适的访问路径匹配。
优化器会在索引扫描、顺序扫描、位图扫描和连接策略之间估算成本。索引存在不代表一定使用;当查询返回大部分表、统计信息不准确或随机读取更贵时,顺序扫描可能更合理。
B-tree 适合有顺序的等值和范围条件
B-tree 是 PostgreSQL 默认索引类型,适用于能够建立稳定排序的值。等值、范围、前缀排序和部分 ORDER BY 可以从中获益。日期区间、递增编号和精确状态查询常属于这一类。
哈希、GIN、GiST、SP-GiST 和 BRIN 服务于不同数据与操作。GIN 常用于包含多个成员的值或全文检索,GiST 是可扩展的索引框架并支持多类空间或近邻操作,BRIN 汇总连续物理块范围,适合值与存储顺序高度相关的超大表。类型选择必须基于操作符和数据分布,而不是名称印象。
索引还依赖操作符类。相同列上使用索引未支持的函数或比较语义,优化器可能无法利用它。表达式索引可以保存规范化结果,但表达式必须稳定,查询表达式也要与定义匹配。
复合索引的列顺序表达访问模式
复合索引同时包含多个列。对 B-tree 而言,前导列通常决定可有效缩小的范围。例如索引 (tenant_id, created_at) 适合先限定租户再按时间筛选或排序;只按 created_at 查询时未必能充分利用前导结构。
列顺序应来自真实谓词组合、选择性和排序要求。把“选择性最高的列放最前”当作绝对规则会忽略等值与范围条件的组合,也会忽略查询是否需要有序输出。
数据库有时可以组合多个单列索引形成位图,但组合会增加扫描成本,并通常失去原索引顺序。高频固定组合可能更适合复合索引,变化很多的低频条件则可能依赖多个单列索引。结论要由执行计划和负载测量决定。
部分索引只覆盖满足条件的行
部分索引用谓词限定进入索引的行。例如待处理任务只占全表很小一部分时,可以只索引 status = 'PENDING' 的记录。索引更小、维护成本更低,但只有查询条件能够推出索引谓词时才能使用。
部分索引也可用于排除普遍值,让稀有条件更快。它不是分区替代品,也不适合为大量互斥值各建一个索引;这种设计会增加规划和维护复杂度。
表达式索引适用于大小写归一化、提取 JSON 字段或计算稳定派生值。代价是每次相关写入都要计算表达式,并且查询必须采用兼容写法。业务语义改变时,索引定义也可能需要迁移。
覆盖索引减少回表但扩大维护成本
索引除了搜索键,还可以包含查询需要返回的其他列,使某些查询执行索引仅扫描。是否真正避免访问表,还受可见性信息和更新频率影响。频繁更新的表即使覆盖全部列,也不一定始终得到理想效果。
把很多宽列加入索引会增加尺寸、缓存压力和写放大。覆盖索引应针对稳定且高价值的读取路径设计,不能把整行复制进每个索引。
唯一索引同时承担约束。它不仅是性能工具,还保证键值唯一。业务唯一性若允许空值、大小写差异或条件范围,需要把这些语义准确编码,避免应用层检查与数据库并发之间产生竞态。
写入与清理成本不能忽略
插入、删除和更新都要维护相关索引。更新未被索引的列有时可以减少索引变更,但索引过多会降低这种机会。失效行版本需要 vacuum 清理,长期事务和高更新率可能造成索引膨胀。
在线创建大索引需要考虑锁、额外扫描、临时空间和失败恢复。并发创建降低对写入的阻塞,但过程更长且有自身限制。生产变更应记录预计尺寸、运行窗口、取消方式和回滚路径。
索引还占用共享缓存。一个很少使用的大索引会挤压更重要的数据页。定期检查索引使用统计有价值,但“扫描次数为零”不能立即证明可删除:统计可能刚重置,唯一约束、月度任务或灾备查询也可能依赖它。
EXPLAIN 验证的是计划而非愿望
设计索引后要用代表性参数查看 EXPLAIN,必要时使用 EXPLAIN ANALYZE 测量实际行数和耗时。生产数据分布、参数值和缓存状态会影响结果,小测试库上的计划不能直接代表线上。
估计行数与实际行数差异大时,应先检查统计信息、相关性和谓词,而不是继续加索引。多列统计可以帮助优化器理解列之间关系。参数化查询还可能在通用计划与定制计划之间取舍。
性能评价要包含写入吞吐、存储增长、尾延迟和维护时间。某个查询从一秒降到十毫秒,如果使关键写入下降一半,仍需整体权衡。
可靠的索引设计始于查询和数据分布,经过计划验证,再由运行统计持续校正。索引是负载的物化假设;负载改变后,这个假设也必须重新检查。
资料依据
- PostgreSQL Global Development Group:Indexes,https://www.postgresql.org/docs/current/indexes.html
- PostgreSQL Global Development Group:Transaction Isolation,https://www.postgresql.org/docs/current/transaction-iso.html