内容提要
在2M行PostgreSQL 17上,覆盖索引比单列索引写入慢28%(INSERT)和25%(UPDATE),但读取快1.26倍。当写入低于每分钟约4300次时,覆盖索引更划算;否则单列索引更优。存储占用112MB对18MB。建议先设置random_page_cost=1.1,默认用单列索引,仅在需要时添加覆盖索引。
延伸解读
写入代价的量化
文章通过实测给出了覆盖索引的具体写入代价:在2M行PostgreSQL 17上,相比单列索引,覆盖索引的INSERT慢28%,UPDATE慢25%。这打破了“覆盖索引影响写入”的模糊说法,为架构决策提供了可量化的依据。但需注意,这些数据基于特定工作负载(如状态更新频率、行数等),实际场景可能不同。
HOT优化与更新代价
覆盖索引之所以在UPDATE上代价更高,是因为它包含了status和shipped_at列,导致更新这些列时无法使用PostgreSQL的Heap-Only Tuple(HOT)优化。HOT允许在未索引列更新时避免索引维护,而覆盖索引禁用了这一优化,导致脏页数从686增至12,258。这解释了为何覆盖索引的更新代价显著高于单列索引。
权衡与建议
文章建议先设置random_page_cost=1.1,默认使用单列索引,仅在需要额外读性能且写入频率低于约4300次/分钟时添加覆盖索引。这一阈值基于特定工作负载,实际应用中应根据自身读写比例和查询频率重新计算。覆盖索引的存储开销(112MB vs 18MB)也是重要考量。
Q&A
覆盖索引相比单列索引在写入性能上具体慢多少?
在2M行PostgreSQL 17上,覆盖索引比单列索引写入慢28%(INSERT)和25%(UPDATE)。
覆盖索引在读取性能上比单列索引快多少?
覆盖索引比单列索引读取快1.26倍。
在什么写入频率下,覆盖索引比单列索引更划算?
当写入频率低于每分钟约4300次时,覆盖索引更划算;高于此阈值,单列索引更优。
覆盖索引和单列索引在存储空间上有多大差异?
覆盖索引占用112MB,单列索引占用18MB,覆盖索引是单列索引的6.2倍。
为什么覆盖索引会拖慢UPDATE操作?
因为覆盖索引包含了被更新的列(如status和shipped_at),这会禁用PostgreSQL的Heap-Only Tuple(HOT)优化,导致每次更新都需要维护索引,产生更多脏页和I/O。
在PostgreSQL中,设置random_page_cost=1.1有什么好处?
设置random_page_cost=1.1可以让查询规划器正确使用单列索引,避免因默认值4.0导致规划器忽略索引而强制使用覆盖索引,从而更准确地评估索引性能。
根据文章,应该如何选择索引策略?
建议先设置random_page_cost=1.1,默认使用单列索引,仅在需要额外读取性能且写入频率低于约4300次/分钟时,才添加覆盖索引。