【发布时间】: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 看看是否有什么不同。
标签: postgresql indexing query-optimization postgresql-12