Postgres: поиск по полю массива jsonb идет медленно - PullRequest
0 голосов
/ 16 января 2020

Мы меняем структуру БД (PostgreSQL 10.11) для одного из наших проектов. И одно из изменений - это перемещение поля типа uuid [] (называемого «areaoflawid») в поле jsonb (называемое «data»).

Итак, у нас есть таблица, которая выглядит следующим образом:

CREATE TABLE public.documents
(
  id serial,
  areasoflawid uuid[], --the field to be moved into the ‘data’
  data jsonb,
  ….
)

Мы не меняем значения массива или его структуры. т.е. Documents.data -> 'metadata' -> 'areaoflawids' содержит те же элементы, что и documents.areasoflawid) После переноса данных JSON, хранящиеся в поле «data», имеют следующую структуру:

{
   ...
   "metadata": {
      ...
      "areaoflawids": [
         "e34e0ee5-78e0-4d92-9186-ac69c109408b",
         "b3af9163-d910-4d19-8f40-0602b75c25b0",
         "50dc7fd8-ebdf-4cd2-bcab-b8d755fe96e8",
         "8955c062-363f-4a1a-ac3c-d1c2ffe96c9b",
         "bdb79f9f-4539-45f5-ac82-92baaf915f6c"
      ],
      ....
   },
   ...
}

Итак, после переноса данных мы начали сопоставлять запросы, связанные с полями jsonb, и выяснили, что поиск по полю массива documents.data -> 'metadata' -> 'areaoflawids' НАМНОГО дольше, чем поиск по полю uuid [] documents.areasoflawid. 1009 *

Вот запросы:

--search over jsonb array field, takes 6.2 sec, returns 13615 rows
SELECT id FROM documents WHERE data->'metadata'->'areaoflawids' @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'

--search over uuid[] field, takes 600ms, returns 13615 rows
SELECT id FROM documents WHERE areasoflawid @> ARRAY['e34e0ee5-78e0-4d92-9186-ac69c109408b']::uuid[]

Вот поле индекса над jsonb:

CREATE INDEX test_documents_aols_gin_idx
  ON public.documents
  USING gin
  (((data -> 'metadata'::text) -> 'areaoflawids'::text) jsonb_path_ops);

А вот и план выполнения:

EXPLAIN ANALYZE SELECT id FROM documents WHERE data->'metadata'->'areaoflawids' @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'


"Bitmap Heap Scan on documents  (cost=6.31..390.78 rows=201 width=4) (actual time=2.297..5859.886 rows=13614 loops=1)"
"  Recheck Cond: (((data -> 'metadata'::text) -> 'areaoflawids'::text) @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'::jsonb)"
"  Heap Blocks: exact=4859"
"  ->  Bitmap Index Scan on test_documents_aols_gin_idx  (cost=0.00..6.30 rows=201 width=0) (actual time=1.608..1.608 rows=13614 loops=1)"
"        Index Cond: (((data -> 'metadata'::text) -> 'areaoflawids'::text) @> '"e34e0ee5-78e0-4d92-9186-ac69c109408b"'::jsonb)"
"Planning time: 0.133 ms"
"Execution time: 5862.807 ms"

Другие запросы к полю jsonb работают с приемлемой скоростью, но этот конкретный поиск примерно в 10 раз медленнее, чем поиск по разделенному полю. Мы ожидали, что это будет немного медленнее, но не так уж плохо. Мы рассматриваем возможность оставить это поле «Площадь незаконного» как отдельное поле, но мы определенно предпочли бы переместить его в json. Я играл с разными индексами и операциями (также использовал? И? |), Но поиск все еще медленный. Любая помощь приветствуется!

Ответы [ 2 ]

0 голосов
/ 17 января 2020

спасибо за ваш ответ! Мы придумали другое решение, взятое из этого поста: https://www.postgresql.org/message-id/CAONrwUFOtnR909gs+7UOdQQB12+pXsGUYu5YHPtbQk5vaE9Gaw@mail.gmail.com. Теперь выполнение запроса занимает около 600-800 мс. Итак, вот решение:

CREATE OR REPLACE FUNCTION aol_uuids(data jsonb) RETURNS TEXT[] AS
$$
    SELECT 
        array_agg(value::TEXT) as val
    FROM 
        jsonb_array_elements(case jsonb_typeof(data) when 'array' then data else '[]' end)
$$ LANGUAGE SQL IMMUTABLE;


SELECT id FROM documents WHERE aol_uuids(data->'metadata'->'areaoflawids')@>ARRAY['"e34e0ee5-78e0-4d92-9186-ac69c109408b"']

0 голосов
/ 16 января 2020

Поиск 13 614 совпадений кандидатов в индексе выполняется очень быстро (1,608 миллисекунд). Медленная часть читает все эти строки из самой таблицы. Если вы включите track_io_timing, а затем выполните EXPLAIN (ANALYZE, BUFFERS), я уверен, что вы обнаружите, что ожидаете ввода-вывода. Если вы выполняете запрос несколько раз подряд, он становится быстрее?

Я думаю, что вы делаете неравный тест здесь, где одна таблица уже находится в кеше, а альтернативная таблица - нет. Но может также оказаться, что новая таблица слишком велика, чтобы помещаться в кеш.

...