我正在尝试利用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)索引?我只是做错了吗?
答案 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);