CREATE TABLE product (
product_id SERIAL,
factory_key VARCHAR(60),
relevant BOOLEAN
)
Indexes:
"product_factory_key_key" btree (factory_key);
"product_factory_key_relevant_key" btree (factory_key, relevant) WHERE relevant = false;
"product_relevant_key" btree (relevant);
事实:
product
我们的表中有大约 1 亿条记录- 有少量工厂。例如,1 个工厂可能有 500 万种产品。
- 有数百万个工厂密钥
- 只有少数行与每个工厂无关。例如,一家工厂有 500 万种产品,有大约 100 种不相关的产品。
- 但是,有数百万行不相关的行。因为,最常见的情况是一个工厂密钥,5 行产品,并且可能有 2 行不相关。
这是问题查询:
SELECT * FROM product WHERE factory_key='some_product_key' AND relevant=false LIMIT 10;
解释分析:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.00..23.06 rows=10 width=188) (actual time=2709.654..32252.961 rows=10 loops=1)
-> Seq Scan on product (cost=0.00..7366785.34 rows=3194759 width=188) (actual time=2709.634..32252.904 rows=10 loops=1)
Filter: ((NOT relevant) AND ((product_key)::text = 'some_product_key'::text))
Rows Removed by Filter: 449486
Total runtime: 32253.150 ms
(5 rows)
问题:
这是有问题的,因为:
我相信planner之所以选择使用seq scan,是因为匹配这个工厂的行太多了。(约 320 万行匹配该工厂或约 3%)
但是,因为只有极少数的行不相关。我正在寻找不相关的。seq 扫描最终会非常昂贵。
我已经创建了一个复合索引product_factory_key_relevant_key
,但是它没有利用索引。
编辑:
我试图强制 postgres 使用复合键:product_factory_key_relevant_key
SET enable_seqscan=off
虽然,它现在正在使用索引扫描。它实际上仍然比 seqscan 慢。(所以我猜计划者在进行 seq 扫描时是正确的)
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.57..34.03 rows=10 width=188) (actual time=8.088..469974.692 rows=10 loops=1)
-> Index Scan using product_factory_key_relevant_key on product (cost=0.57..10689307.49 rows=3194776 width=188) (actual time=8.083..469974.655 rows=10 loops=1)
Index Cond: (relevant = false)
Filter: ((NOT relevant) AND ((product_key)::text = 'some_product_key'::text))
Rows Removed by Filter: 2205295
Total runtime: 469974.820 ms
(6 rows)