【问题标题】:Why are indexed ORDER BY queries matching many rows a LOT faster than queries matching only a few?为什么索引 ORDER BY 查询匹配许多行比只匹配少数行的查询快很多?
【发布时间】: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


【解决方案1】:

这是因为不来梅的前 14 个匹配行是在前 3539 个索引行中找到的,而对于 Sachsen,必须扫描 963374 行。

我建议在(seller_region, rank) 上建立索引。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-05
    • 1970-01-01
    • 2019-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    相关资源
    最近更新 更多