PostgreSQL多列索引连接与比较(“<”和“>”)运算符

时间:2011-02-03 20:13:42

标签: sql optimization postgresql indexing

我正在尝试利用PostgreSQL中的多列btree索引在两个表之间执行恼人的连接。

               Table "revision_main"
     Column     |          Type          | Modifiers 
----------------+------------------------+-----------
 revision_id    | integer                | 
 page_id        | integer                | 

Indexes:
    "revision_main_pkey" UNIQUE, btree (revision_id)
    "revision_main_cluster_idx" btree (page_id, "timestamp") CLUSTER

此表包含Wiki中页面的修订版(约3亿行)。我的表中有更多的列,但是我已经放弃了这个例子,因为它们无关紧要。

               Table "revert"
       Column       |  Type   | Modifiers 
--------------------+---------+-----------
 page_id            | integer | 
 revision_id        | integer | 
 reverted_to        | integer | 
Indexes:
    "revert_page_between_idx" btree (page_id, reverted_to, revision_id) CLUSTER

此表包含还原修订版(约2200万行)。如果已恢复修订,则该revision_id将在revision_main表中有一行,其revision_id将在reverted_to和revision_id之间,并共享相同的page_id。 (如果您感到好奇,请参阅http://en.wikipedia.org/wiki/Wikipedia:Revert。)

加入这两个表以获得恢复版本似乎很简单。以下是我的想法:

explain SELECT
    r.revision_id,
    rvt.revision_id
FROM revision_main r
INNER JOIN revert rvt 
    ON r.page_id = rvt.page_id 
    AND r.revision_id > rvt.reverted_to
    AND r.revision_id < rvt.revision_id;
                                       QUERY PLAN                                               
----------------------------------------------------------------------------------------------------
 Merge Join  (cost=4202878.87..15927491478.57 rows=88418194298 width=8)
   Merge Cond: (r.page_id = rvt.page_id)
   Join Filter: ((r.revision_id > rvt.reverted_to) AND (r.revision_id < rvt.revision_id))
   ->  Index Scan using revision_main_page_id_idx on revision_main r  (cost=0.00..9740790.61 rows=223163392 width=8)
   ->  Materialize  (cost=4201592.06..4536465.21 rows=26789852 width=12)
         ->  Sort  (cost=4201592.06..4268566.69 rows=26789852 width=12)
               Sort Key: rvt.page_id
               ->  Seq Scan on revert rvt  (cost=0.00..438534.52 rows=26789852 width=12)

即使revert上的聚集索引应该是Btree索引(因此支持比较运算符,如“&lt;”和“&gt;”),查询优化器不使用连接索引,“explain”预测a总成本超过150亿(可能在明年完成)。

比较运算符是否无法用于多列(btree)索引?我只是做错了吗?

2 个答案:

答案 0 :(得分:5)

看起来优化器比你更了解它的工作。

如果您选择的不只是表的一小部分(依赖于硬件的分数,假设为5%),那么选择和排序整个表的速度比使用索引要快。如果您只是选择几行,那么它应该使用索引。因此它为您提供了正确的数据查询计划。

至于总费用,这些数字都是BS,只有在单个查询中相互比较时才有用。 (两个非常相似的查询产生的总成本可能会有不同的规模。)执行时间和查询成本几乎无关。

答案 1 :(得分:0)

您的查询(基于SQL)看起来需要读取整个还原表,并为还原表中的每一行找到相应的修订行。

由于需要读取整个恢复表,因此对其进行顺序扫描是合适的。它似乎期望大致正确的行数。

然后每个恢复行将匹配许多修订,它认为最好通过索引扫描和合并连接来完成。它估计平均每个恢复行将匹配大约3300个修订版,从而产生880亿行。

我不知道有什么方法可以快速选择880亿行。

为了获得更准确的估算,您需要一种方法来说服PostgreSQL每个还原所涵盖的修订版本少于3300个。

您说您是在恢复修订后,表示每个修订版本只应出现一次,即使包含在多个恢复中也是如此。

因此,请尝试使用EXISTS (subquery)代替INNER JOIN

虽然这不会给你提供修改版本:

EXPLAIN
SELECT
    r.revision_id
FROM revision_main r
WHERE EXISTS (SELECT 1 FROM revert rvt 
    WHERE r.page_id = rvt.page_id 
    AND r.revision_id > rvt.reverted_to
    AND r.revision_id < rvt.revision_id);