Почему поиск в полях JSONB увеличивает время запроса в 62 раза в Postgres?

Я выполняю простой запрос, который извлекает данные из одной таблицы. Если я смотрю только не-JSON поля, которые находятся в таблице, запрос занимает 16 мс. Если я включу поля, которые ссылаются на поля в данных JSONB, то он увеличивается в 62 раза. Если я посмотрю два разных поля JSONB, то удвою это.

--EXPLAIN (ANALYZE,buffers)
SELECT 
  segment as segment_no,
  begin_time,
  segment_data::json->'summary'->'begin_milage' as begin_milage,
  segment_data::json->'summary'->'end_milage' as end_milage
FROM
  segments_table
WHERE
  vehicle=12 AND trip=3
ORDER BY
  begin_time;

Запрос занимает 2,0 секунды с двумя полями JSON, включенными в предложение SELECT. Если пропустить одно, это займет 1,0 с, если пропустить оба поля JSON, тогда запрос займет всего 16 мс.

Сама таблица имеет около 700 записей. Запрос возвращает 83 записи. Запуск разных запросов Я заметил, что чем больше записей возвращается, тем больше времени требуется для выполнения запроса (приблизительно 0,0066 * X1,32 мс) при запросе 2 полей JSON.

Я попытался добавить индекс для транспортного средства и поиска поездки, но это не имело большого значения (как ожидалось). Кажется, что это фактический поиск данных, а поиск данных в поле JSONB - это то, что требует времени. Теперь, если бы поля JSON были необходимы в предложении WHERE, было бы более понятно увидеть такое ухудшение, но это не так.

Конечно, простым решением было бы вытащить каждое поле из BLOB-объекта JSON и создать для него отдельные поля в самой таблице. Но прежде чем идти по этому пути, есть ли что-нибудь еще, что могло бы решить эту проблему производительности?

Вот результаты для АНАЛИЗА:

Sort  (cost=13.25..13.27 rows=10 width=28) (actual time=1999.899..1999.901 rows=71 loops=1)
  Sort Key: begin_time
  Sort Method: quicksort  Memory: 35kB
  Buffers: shared hit=5663
  ->  Bitmap Heap Scan on segments_table  (cost=4.38..13.08 rows=10 width=28) (actual time=1.332..1999.730 rows=71 loops=1)
        Recheck Cond: ((vehicle = 644) AND (trip = 3))
        Heap Blocks: exact=3
        Buffers: shared hit=5663
        ->  Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq  (cost=0.00..4.38 rows=10 width=0) (actual time=0.052..0.052 rows=71 loops=1)
              Index Cond: ((vehicle = 644) AND (trip = 3))
              Buffers: shared hit=2
Planning time: 0.368 ms
Execution time: 2000.000 ms

Еще одно интересное наблюдение состоит в том, что, выполняя один и тот же запрос несколько раз, я не вижу улучшения в кэшировании, которое я ожидаю получить при последующих идентичных запросах.

Единственные изменения, которые я внес в стандартную конфигурацию сервера postgres, - это увеличение shared_buffers от 128 МБ до 256 МБ и настройка effective_cache_size = 1GB, Я также уменьшил max_connections от 100 до 20.

Вышеуказанные результаты работают под Win7 на 8-ядерном процессоре i7. Также провели тот же тест под Ubuntu на двухъядерном процессоре, и запрос занял примерно столько же: 2,2 секунды (при включении двух полей JSONB в предложение SELECT).


Обновление:

Одно поле JSON в классе SELECT:

EXPLAIN (ANALYZE,buffers)
SELECT 
  segment as segment_no,
  begin_time, 
  segment_data::json->'summary'->'end_mileage' as end_mileage
FROM
  segments_table
WHERE
  vehicle=644 AND trip=3
ORDER BY
  begin_time;

Результат:

Sort  (cost=13.15..13.17 rows=10 width=28) (actual time=999.695..999.696 rows=71 loops=1)
  Sort Key: begin_time
  Sort Method: quicksort  Memory: 26kB
  Buffers: shared hit=2834
  ->  Bitmap Heap Scan on segments_table  (cost=4.38..12.98 rows=10 width=28) (actual time=0.781..999.554 rows=71 loops=1)
        Recheck Cond: ((vehicle = 644) AND (trip = 3))
        Heap Blocks: exact=3
        Buffers: shared hit=2834
        ->  Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq  (cost=0.00..4.38 rows=10 width=0) (actual time=0.052..0.052 rows=71 loops=1)
              Index Cond: ((vehicle = 644) AND (trip = 3))
              Buffers: shared hit=2
Planning time: 0.353 ms
Execution time: 999.777 ms

Нет поля JSON в предложении SELECT:

EXPLAIN (ANALYZE,buffers)
SELECT 
  segment as segment_no,
  begin_time
FROM
  segments_table
WHERE
  vehicle=644 AND trip=3
ORDER BY
  begin_time;

Результат:

Sort  (cost=13.05..13.07 rows=10 width=10) (actual time=0.194..0.205 rows=71 loops=1)
  Sort Key: begin_time
  Sort Method: quicksort  Memory: 19kB
  Buffers: shared hit=5
  ->  Bitmap Heap Scan on segments_table  (cost=4.38..12.88 rows=10 width=10) (actual time=0.088..0.122 rows=71 loops=1)
        Recheck Cond: ((vehicle = 644) AND (trip = 3))
        Heap Blocks: exact=3
        Buffers: shared hit=5
        ->  Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq  (cost=0.00..4.38 rows=10 width=0) (actual time=0.048..0.048 rows=71 loops=1)
              Index Cond: ((vehicle = 644) AND (trip = 3))
              Buffers: shared hit=2
Planning time: 0.590 ms
Execution time: 0.280 ms

Определение таблицы:

CREATE TABLE public.segments_table
(
  segment_id integer NOT NULL DEFAULT nextval('segments_table_segment_id_seq'::regclass),
  vehicle smallint NOT NULL,
  trip smallint NOT NULL,
  segment smallint NOT NULL,
  begin_time timestamp without time zone NOT NULL,
  segment_data jsonb,
  CONSTRAINT segments_table_pkey PRIMARY KEY (segment_id),
  CONSTRAINT segments_table_vehicle_64df5bc5_uniq UNIQUE (vehicle, trip, segment, begin_time)
)
WITH (
  OIDS=FALSE
);

CREATE INDEX segments
  ON public.segments_table
  USING btree
  (segment);

CREATE INDEX vehicles
  ON public.segments_table
  USING btree
  (vehicle);

CREATE INDEX trips
  ON public.segments_table
  USING btree
  (trip);

Обновление № 2:

Исправление проблемы с кастингом, как указано @Mark_M, изменение json to jsonb` уменьшает время запроса с 2 с до 300 мс:

EXPLAIN (ANALYZE,buffers)
SELECT 
  segment as segment_no,
  begin_time,
  segment_data::jsonb->'summary'->'begin_mileage' as begin_mileage,
  segment_data::jsonb->'summary'->'end_mileage' as end_mileage
FROM
  segments_table
WHERE
  vehicle=644 AND trip=3
ORDER BY
  begin_time;

  Sort  (cost=13.15..13.17 rows=10 width=28) (actual time=296.339..296.342 rows=71 loops=1)
  Sort Key: begin_time
  Sort Method: quicksort  Memory: 35kB
  Buffers: shared hit=5663
  ->  Bitmap Heap Scan on segments_table  (cost=4.38..12.98 rows=10 width=28) (actual time=0.275..296.229 rows=71 loops=1)
        Recheck Cond: ((vehicle = 644) AND (trip = 3))
        Heap Blocks: exact=3
        Buffers: shared hit=5663
        ->  Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq  (cost=0.00..4.38 rows=10 width=0) (actual time=0.045..0.045 rows=71 loops=1)
              Index Cond: ((vehicle = 644) AND (trip = 3))
              Buffers: shared hit=2
Planning time: 0.352 ms
Execution time: 296.473 ms

Это немного улучшилось, но все же в 18 раз просто поиск с использованием полей не-JSON, но это намного лучше. Это разумное снижение производительности для использования поля JSONB?

0 ответов

Другие вопросы по тегам