PostgreSQL 多列索引未完全使用
Posted
技术标签:
【中文标题】PostgreSQL 多列索引未完全使用【英文标题】:PostgreSQL multicolumn index not fully used 【发布时间】:2020-10-19 00:43:55 【问题描述】:我在 PostgreSQL 12.3 上有一个大型(约 1.1 亿行)表,其相关字段可以由以下 DDL 描述:
CREATE TABLE tbl
(
item1_id integer,
item2_id integer,
item3_id integer,
item4_id integer,
type_id integer
)
我们经常执行的查询之一是:
SELECT type_id, item1_id, item2_id, item3_id, item4_id
FROM tbl
WHERE
type_id IS NOT NULL
AND item1_id IN (1, 2, 3)
AND (
item2_id IN (4, 5, 6)
OR item2_id IS NULL
)
AND (
item3_id IN (7, 8, 9)
OR item3_id IS NULL
)
AND (
item4_id IN (10, 11, 12)
OR item4_id IS NULL
)
虽然我们对每个单独的列都有索引,但查询仍然相对较慢(几秒钟)。为了优化这一点,我创建了以下索引:
CREATE INDEX tbl_item_ids
ON public.tbl USING btree
(item1_id ASC, item2_id ASC, item3_id ASC, item4_id ASC)
WHERE type_id IS NOT NULL;
不幸的是,查询性能几乎没有提高 - EXPLAIN
告诉我这是因为虽然使用这个新创建的索引完成了索引扫描,但只有 item1_id
用作 Index Cond
,而所有其他过滤器都应用在表级别(即普通的Filter
)。
我不确定为什么没有完整使用索引(或者至少不使用item1_id
列)。这有明显的原因吗?有没有办法可以重组索引或查询本身以帮助提高性能?
【问题讨论】:
此查询平均返回多少行(或行的百分比)?数字 1 到 12 的参数是否可以具有任何值并经常更改? @TheImpaler 查询将返回 100 到 30k 行之间的任何位置。平均而言,大约 5k。 IN 子句中的值的数量最多会在几到 100 之间变化,平均约为 20(根据我的实验,这似乎对计划没有影响)。 我会做几件事:1)将item1_id IS NOT NULL
添加到部分索引的WHERE
子句中,以使索引更小。 2)我会将 item_ids 从最具选择性到最不选择性(根据您的统计数据和经验)排序,并将它们按该顺序放置在索引中。作为旁注,ASC
并没有真正产生任何影响,因为您没有执行范围扫描。
感谢@TheImpaler。根据我的经验, item1 应该是最具选择性的,但我尝试了其他一些排列都无济于事。到目前为止,我能做的最好的事情就是在item1_id, type_id
和INCLUDE (item2_id, item3_id, item4_id)
上建立索引。我使用了type_id
并且没有使用WHERE
子句,因此如果我选择此选项,我可以将单列索引放在item1_id
上。这导致仅索引扫描比我的原始索引加速约 33%。不过,我会尝试升级到 12.4 看看是否有什么不同。
【参考方案1】:
如果第一列的条件使用相等比较 (=
),则多列索引只能用于多于第一列。 IN
或 = ANY
不符合条件。
因此,您最好为每列使用单独的索引,这些索引可以与位图或结合使用。
你应该尽量避免 OR
在 WHERE
条件下,也许与
WHERE coalesce(item2_id, -1) IN (-1, 4, 5, 6)
其中 -1 是一个不存在的值。然后您可以在coalesce
表达式上使用索引。
【讨论】:
"如果第一列的条件使用相等比较,多列索引只能用于多于第一列" - 谢谢,我不是意识到这一点。这实际上有点令人失望。是否有技术限制,或者只是“没有人愿意实施它”的情况 当我说“只能使用”时,我的意思是“只能有效地使用”,我省略了仅索引扫描的用例。它需要像“索引跳过扫描”这样的东西才能做得更好;有一个补丁正在开发中,但我们还没有。以上是关于PostgreSQL 多列索引未完全使用的主要内容,如果未能解决你的问题,请参考以下文章