PostgreSQL 9.3 分页:计算页数

Ale*_*kii 6 postgresql index postgresql-9.3

我执行分页和,让我们说,行的排序products表,使用上多列索引categoryscoreid

-- index
create index category_score_id on products(category, score, id)
  where active = true;

-- selecting first page
select * from products where
  category = 1
  and active = true
order by score desc, id desc
limit 30;

-- selecting following pages
select * from products where
  category = 1
  and active = true
  and (score, id) < (893, 102458)  -- values from previous page
order by score desc, id desc
limit 30;
Run Code Online (Sandbox Code Playgroud)

仅使用索引扫描,就可以正常工作,并且符合预期:

-- explain analyze of 2-nd query:
Limit  (cost=0.00..99.52 rows=30 width=2591) (actual time=0.090..0.179 rows=30 loops=1)
  ->  Index Scan Backward using category_score_id on products  (cost=0.00..76435.07 rows=23041 width=2591) (actual time=0.089..0.172 rows=30 loops=1)
        Index Cond: ((category = 1) AND (ROW(score, id) < ROW(893, 102458)))
Total runtime: 0.085 ms
Run Code Online (Sandbox Code Playgroud)

我很好奇的是为什么以下查询需要额外的 Bitmap Heap Scan

-- explain analyze of 2-nd query:
Limit  (cost=0.00..99.52 rows=30 width=2591) (actual time=0.090..0.179 rows=30 loops=1)
  ->  Index Scan Backward using category_score_id on products  (cost=0.00..76435.07 rows=23041 width=2591) (actual time=0.089..0.172 rows=30 loops=1)
        Index Cond: ((category = 1) AND (ROW(score, id) < ROW(893, 102458)))
Total runtime: 0.085 ms
Run Code Online (Sandbox Code Playgroud)
select count(*) from products where
  category = 1
  and active = true;
Run Code Online (Sandbox Code Playgroud)

jja*_*nes 3

它没有进行额外的位图扫描。它正在执行位图扫描而不是常规索引扫描。

为什么它要进行位图扫描?

因为它认为那样会更快。如果没有 LIMIT 和 ORDER BY,它预计使用位图扫描将使其以更有效的方式在表堆上执行 IO。您可以通过临时执行以下操作来查看 PostgreSQL 在此评估中是否正确set enable_bitmapscan TO off;(尽管缓存效应可能会使该实验难以正确解释。)

另外,该行:

Rows Removed by Index Recheck: 93006
Run Code Online (Sandbox Code Playgroud)

建议您的 work_mem 设置可能太小而无法有效地容纳位图。

为什么它在解释计划中显示为两个条目而不是一个?

普通的索引扫描实际上是两个扫描合二为一,先扫描索引,然后嵌套查找表。但这两个部分是一体的,在执行计划中只作为一个条目出现。

位图案例也有这两个步骤,但是它们没有集成。可以在它们之间插入额外的步骤,例如 aBitmapOr或 a BitmapAnd。它将它们报告为两个单独的条目以适应这种可能性。