【发布时间】:2016-12-29 07:12:52
【问题描述】:
好的,我有以下问题:
explain analyze SELECT seller_region FROM "products"
WHERE "products"."seller_region" = 'Bremen'
AND "products"."state" = 'active'
ORDER BY products.rank DESC,
products.score ASC NULLS LAST,
GREATEST(products.created_at, products.price_last_updated_at) DESC
LIMIT 14 OFFSET 0
查询过滤匹配围绕11.000 rows。如果我们查看查询计划器,我们可以看到查询使用索引index_products_active_for_default_order,并且非常快:
Limit (cost=0.43..9767.16 rows=14 width=36) (actual time=1.576..6.711 rows=14 loops=1)
-> Index Scan using index_products_active_for_default_order on products (cost=0.43..4951034.14 rows=7097 width=36) (actual time=1.576..6.709 rows=14 loops=1)
Filter: ((seller_region)::text = 'Bremen'::text)
Rows Removed by Filter: 3525
Total runtime: 6.724 ms
现在,如果我在查询中将'Bremen' 替换为'Sachsen':
explain analyze SELECT seller_region FROM "products"
WHERE "products"."seller_region" = 'Sachsen'
AND "products"."state" = 'active'
ORDER BY products.rank DESC,
products.score ASC NULLS LAST,
GREATEST(products.created_at, products.price_last_updated_at) DESC
LIMIT 14 OFFSET 0
相同的查询只匹配 70 rows 周围,并且现在一直非常慢,即使它以完全相同的方式使用相同的索引:
Limit (cost=0.43..1755.00 rows=14 width=36) (actual time=2.498..1831.737 rows=14 loops=1)
-> Index Scan using index_products_active_for_default_order on products (cost=0.43..4951034.14 rows=39505 width=36) (actual time=2.496..1831.727 rows=14 loops=1)
Filter: ((seller_region)::text = 'Sachsen'::text)
Rows Removed by Filter: 963360
Total runtime: 1831.760 ms
我不明白为什么会这样?我会出于直觉认为匹配更多行的查询会更慢,但事实恰恰相反。我已经用我表上其他列的其他查询对此进行了测试,现象是一样的。两个具有与上述相同排序的类似查询,使匹配更多行的查询比过滤只匹配少数几行的查询快 100 倍。为什么会这样,我该如何避免这种行为?
PS:我用的是postgres 9.3,索引定义如下:
CREATE INDEX index_products_active_for_default_order
ON products
USING btree
(rank DESC, score COLLATE pg_catalog."default", (GREATEST(created_at, price_last_updated_at)) DESC)
WHERE state::text = 'active'::text;
【问题讨论】:
-
注意:在这两种情况下,实际匹配的行数都是 14,但估计值不同。你有有效的统计数据吗?
-
14是因为每次查询都有限制
-
我觉得令人费解的地方。你说不来梅有 11,000 行。那么为什么过滤器只删除了 3,525 行,而 Sachsen 过滤器删除了 963,360 行?表中有多少条记录。为什么 Sachsen 必须删除这么多?
-
是的,没错;如果生成了 14 个元组,则停止扫描。但在第二种情况下,963360 必须被过滤器扫描并拒绝。也许在 {status, region} 上添加额外的非条件索引,反之亦然,可能会触发状态列的统计信息收集(IIRC,索引的前提条件不是统计信息收集的候选对象。如果我是,请纠正我错误)顺便说一句,这个
rank DESC,在索引中做什么? -
@JonathanWillcock 大约有 160 万行匹配 state = 'active'。 Seller_region 列是一个 varchar(30) 并采用大约 20 个不同的值,分布非常不同。所以应该是这样的
标签: performance postgresql indexing postgresql-9.3