Тип данных или структура для поиска по дереву - PullRequest
0 голосов
/ 13 марта 2020

Я использую структуру больших данных, которая описывает дерево элементов с помощью строковых меток, например, path-string в файловой системе. Есть ~ 198 000 древовидных элементов и ~ 9 100 000 листовых элементов.

Варианты реализации:

  • на тип данных ltree, который кажется естественным выбором.

  • by нормализованная структура таблицы : создание последовательного индекса для каждого пути, et c. и рекурсивный поиск

Я предполагаю, что после внедрения я могу сравнить оба по EXPLAIN ANALYZE. Но, , прежде чем применять , можно предсказать разницу или оценить производительность (скорость и потребление вне диска)?


ЗАМЕЧАНИЯ И ИСПЫТАНИЯ

О запросах (требованиях) он открыт: «иерархическая поисковая система» должна быть выразительной , ltree, регулярным выражением и иногда Все операторы LIKE выразительны, когда вы представляете иерархическую информацию «строковыми путями».

Использование для проверки аналога CREATE TABLE test (path ltree) в руководстве с ~ 198 000 древовидных элементов:

    CREATE INDEX path_gist_idx ON test USING GIST (path)
    ; -- consumes ~3G
    SELECT count(*) n 
    FROM test WHERE path ~ 'first.second.*.etc_etc_etc.*'
    ; -- n= 149068
    EXPLAIN ANALYSE 
      SELECT count(*) n 
      FROM test WHERE path ~ 'first.second.*.etc_etc_etc.*'
    ;  -- Planning Time: 0.075 ms
       -- Execution Time: 1317.443 ms

1 Ответ

0 голосов
/ 14 марта 2020

Наилучшим решением для нормализованной таблицы является «Таблица закрытия», как рекомендовано этим этим вопросом / решением .

В других стратегиях индексирования мы можем использовать подсказки этого вопроса / ответа , которые предлагают попробовать pg_trgm модуль. См. Также https://www.postgresql.org/docs/current/pgtrgm.html

Давайте проверим все решения.


Грубая сила (индексация)

Преимущество "грубой силы" заключается в том, что отсутствие изменений в структуре данных, только добавление индекса, заключается в том, что в контексте больших данных у нас есть дополнительное дисковое пространство. Итак, только проверено индексирование, без разбиения на реальное дерево и указатели.

Тест ltree Поиск

См. Тесты по существу вопроса. Время выполнения: ~ 1300 мс.

Хорошие операторы иерархического поиска. Единственная проблема заключается в необходимости нового столбца для индекса, когда строка должна быть сохранена для другого использования или не в формате ltree -esestion. Я использовал преобразование

trim(
  replace( 
   regexp_replace(text_path,'[^A-Z0-9_/]+','_','ig'), -- clean
   '/',  -- the path separator at text_path
   '.'   -- the ltree separator
  ),
  '.'  -- clean
)::ltree

Так что, когда вы преобразуете, другая проблема, это не на 100% обратимо для повторного использования при приведении ltree2text().

Тест pg_trgm поиск с оператором LIKE

Это более быстрый способ: ~ 200 мс

CREATE EXTENSION pg_trgm;
CREATE INDEX textpath_gist_idx  ON t2_text USING gin (txt_path gin_trgm_ops);
EXPLAIN ANALYSE 
   SELECT count(*) n
   FROM   t2_text  
   WHERE  txt_path LIKE 'first/second/%/etc_etc_etc/%'
; --  Planning Time: 0.133 ms
  --  Execution Time: 195.524 ms
  -- ... BUT, returns zero on COUNT result!

При индексации USING gin (colName gin_trgm_ops) используется меньше места на диске, ~ 2G.

Проблема здесь не в производительности, а в что он не работает (!) и не является проблемой "two %", потому что отлично работает с подобным запросом, поэтому он не надежен :

  • COUNT WHERE txt_path LIKE 'first/second/%/etc_etc_etc/%' равно нулю.
  • COUNT WHERE txt_path LIKE '%etc_etc_etc%' правильно.

Кажется, ошибка PostgreSQL v12 (то же самое для других версий?).

Тест pg_trgm поиск по регулярному выражению

При времени выполнения ~ 1000 мс это немного быстрее (и меньше потребление дискового пространства), чем ltree и самый надежный оператор LIKE.

EXPLAIN ANALYSE 
   SELECT count(*) n
   FROM   t2_text  
   WHERE  txt_path ~ '^first/second/.+/etc_etc_etc/'
--  Timing: Generation 2.312 ms, ..., Total 27.853 ms
--  Execution Time: 1017.293 ms

Таблица закрытия

Необходимо добавить id bigserial, если не существует, и новую таблицу с предком и потомком , указывающим на оригинал Таблица. занимает больше дискового пространства и ... Могу ли я выполнить все запросы с этой моделью?

Он также потребляет больше времени программистов (!), Автоматизированных нет способ (это таблица шаблонов, но PostgreSQL не предлагает «стандартного решения») для создания дополнительной таблицы. Для запросов нет простого оператора .

... поэтому я не добавляю тест, это вики: если вы что-то здесь редактируете, я могу добавить тест, чтобы показать различия.

CREATE TABLE t2_treeclosure (
   ancestor bigint NOT NULL,
   descendant bigint NOT NULL,
   PRIMARY KEY (ancestor,descendant),
   FOREIGN KEY (ancestor) REFERENCES t2_text(id),
   FOREIGN KEY (descendant) REFERENCES t2_text(id)
 );

... иногда возможно уменьшить потребление диска оригинальной t2_text таблицей, уменьшая текст, после того, как он пропал. Нужна функция для восстановления пути, et c. но может быть быстрым, если '' закрытие таблицы '' быстрее, чем методы "грубой силы".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...