Многостолбцовый индексный индекс PostgreSQL с операторами сравнения (<<и>>) - PullRequest
4 голосов
/ 03 февраля 2011

Я пытаюсь воспользоваться многостолбцовым индексом btree в PostgreSQL для выполнения раздражающего соединения двух таблиц.

               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

Эта таблица содержит изменения (~ 300 миллионов строк) страниц в вики. В моей таблице больше столбцов, но я отбросил их для этого примера, потому что они не должны иметь значения.

               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

Эта таблица содержит возвращаемые ревизии (~ 22 миллиона строк). Если ревизия была отменена, этот 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)

Несмотря на то, что кластеризованный индекс при возврате должен быть индексом Btree (и, следовательно, поддерживать операторы сравнения, такие как «<» и «>»), оптимизатор запросов не использует индекс для объединения, а «объяснение» прогнозирует общую стоимость более 15 миллиардов (может быть сделано в следующем году).

Нельзя ли использовать операторы сравнения с многостолбцовыми (btree) индексами? Я просто делаю это неправильно?

Ответы [ 2 ]

5 голосов
/ 03 февраля 2011

Похоже, что оптимизатор знает свою работу лучше, чем вы.

Если вы выбираете более небольшую часть таблицы (какая доля зависит от оборудования, скажем, 5%), то этобыстрее выбрать и упорядочить всю таблицу, чем использовать индекс.Если вы просто выбираете несколько строк, то он должен использовать индекс.Таким образом, он дает вам правильный план запроса для ваших данных.

Что касается общей стоимости, все эти цифры являются БС и полезны только при сравнении по отношению друг к другу в рамках одного запроса.(Общие затраты, вызванные двумя очень похожими запросами, могут быть в совершенно другом масштабе.) Время выполнения и стоимость запроса в значительной степени не связаны.

0 голосов
/ 04 февраля 2011

Ваш запрос (на основе SQL) выглядит так, как будто ему нужно прочитать всю таблицу возврата и найти соответствующие строки редакции для каждой строки в таблице возврата.

Поскольку всю таблицу возврата необходимочитай, последовательное сканирование уместно.Кажется, он ожидает примерно правильное количество строк.

Каждая возвращаемая строка будет соответствовать количеству ревизий, что, по ее мнению, будет лучше всего сделать при сканировании индекса и объединении слиянием.По оценкам, в среднем каждая возвращенная строка будет соответствовать примерно 3300 ревизиям, в результате чего будет получено 88 миллиардов строк.

Я не знаю способов быстрого выбора 88 миллиардов строк.

ЧтобыЧтобы получить более точную оценку, вам потребуется способ убедить 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...