172 похожих чатов

Подскажите, почему jsonb -> 0 может быть на порядок медленнее,

чем jsonb_array_elements?

структура примерно такая:
data:jsonb
[
{"channel":"1","items": [...]},
{"channel":"2","items": [...]},
{"channel":"3","items": [...]}
]

Надо выбрать все items у первого channel. Сейчас использую два jsonb_array_elements - вначале по исходному массиву, потом по items. Поскольку значение всегда пишется по порядку, попытался упростить до
jsonb_array_elements(data -> 0 ->'items')
но получилось раз в 20 медленнее.
грубо - 600 исходных строк разворачиваются в ~18000 в первом случае за 80-100 мс, во втором за 1800 мс

UPD: подробности https://pastebin.com/ZY4FcSDC

1 ответов

13 просмотров

Хочу привести объяснение того загадочного случая, что произошел в январе, когда jsonb_array_elements() оказалась значительно быстрее jsonb -> 0. На эту проблему легко наткнуться при работе с большими jsonb. Для иллюстрации создадим таблицу с одной с строкой, содержащей jsonb массив из 10 000 элементов. Общий размер таблицы -- всего лишь 64 КБ (сам jsonb -- 120 КБ, сжат до 34 КБ в TOAST-таблице; TOAST-индекс -- 16 КБ). CREATE TABLE test AS SELECT jsonb_build_object( 'id', 1, 'a', (SELECT jsonb_agg(i) FROM generate_series(1, 10000) i) ) jb Извлечение самих элементов массива работает достаточно быстро: EXPLAIN (ANALYZE, BUFFERS) SELECT jsonb_array_elements(jb -> 'a') FROM test; ProjectSet (...) Buffers: shared hit=7 ... Execution Time: 4.104 ms Но стоит добавить к ним jb -> 'id', и все сразу замедляется аж в 300 раз, а количество прочитанных блоков возрастает в 8570 раз: EXPLAIN (ANALYZE, BUFFERS) SELECT jb -> 'id', jsonb_array_elements(jb -> 'a') FROM test; ProjectSet (...) Buffers: shared hit=60013 ... Execution Time: 1267.9 ms Причина этого заключается в том, что для каждого элемента массива, возвращаемого jsonb_array_elements(), вычисляется jb -> 'id'. А jb у нас достаточно большой и попадает в TOAST, откуда он будет 10000 раз извлекаться. Каждое такое извлечение приводит к чтению 6 блоков (2 -- TOAST-индекс, 4 -- TOAST-таблица). При первом вызове jsonb_array_elements() тоже делается detoast. Такое же поведение мы получим, если будем извлекать несколько ключей (или путей) из большого jsonb, и для каждого ключа тоже будет делаться detoast (`SELECT jb->'key1', jb->'key2', ...`). К сожалению, PostgreSQL пока совсем не умеет кэшировать результат detoast и каждый доступ к большому jsonb в функциях и операторах приводит к detoast. Проблему можно решить довольно общим методом, сделав принудительный detoast содержимого jsonb через применение к нему какого-то пустого оператора и materialized CTE. EXPLAIN (ANALYZE, BUFFERS) WITH detoasted AS MATERIALIZED ( SELECT jb || '{}' AS jb FROM test ) SELECT jb -> 'id', jsonb_array_elements(jb -> 'a') FROM detoasted; ProjectSet (...) Buffers: shared hit=7 ... Execution Time: 6.467 ms

Похожие вопросы

Обсуждают сегодня

Господа, а что сейчас вообще с рынком труда на делфи происходит? Какова ситуация?
Rꙮman Yankꙮvsky
29
А вообще, что может смущать в самой Julia - бы сказал, что нет единого стандартного подхода по многим моментам, поэтому многое выглядит как "хаки" и произвол. Короче говоря, с...
Viktor G.
2
30500 за редактор? )
Владимир
47
а через ESC-код ?
Alexey Kulakov
29
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
14
Добрый день! Скажите пожалуйста, а какие программы вы бы рекомендовали написать для того, чтобы научиться управлять памятью? Можно написать динамический массив, можно связный ...
Филипп
7
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Ребят в СИ можно реализовать ООП?
Николай
33
https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
Карта сайта