Индексы JSONB медленнее, чем собственные индексы? - PullRequest
0 голосов
/ 28 сентября 2018

У меня есть большая таблица (30M строк), в которой ~ 10 jsonb индексы B-дерева.

Когда я создаю запрос, используя несколько условий, запрос выполняется относительно быстро.

Когда я добавляю больше условий, особенно с редким индексом jsonb (например, целое число от 0 до 1 000 000), скорость запроса резко падает.

Мне интересно, медленнее ли индексы jsonb, чемнативные индексы?Можно ли ожидать повышения производительности путем переключения на собственные столбцы, а не на JSON?

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

id  integer 
type    text    
data    jsonb   
company_index   ARRAY   
exchange_index  ARRAY   
eligible boolean

Пример запроса:

SELECT id, data, type 
FROM collection.bundles    
WHERE ( (ARRAY['.X'] && bundles.exchange_index)  AND   
type IN ('discussion') AND  
( ((data->>'sentiment_score')::bigint > 0 AND 
(data->'display_tweet'->'stocktwit'->'id') IS NOT NULL) )  AND  
(  eligible = true  )  AND  
((data->'display_tweet'->'stocktwit')->>'id')::bigint IS NULL )  
ORDER BY id DESC   
LIMIT 50

Вывод:

Limit  (cost=0.56..16197.56 rows=50 width=212) (actual time=31900.874..31900.874 rows=0 loops=1)
  Buffers: shared hit=13713180 read=1267819 dirtied=34 written=713
  I/O Timings: read=7644.206 write=7.294
  ->  Index Scan using bundles2_id_desc_idx on bundles  (cost=0.56..2401044.17 rows=7412 width=212) (actual time=31900.871..31900.871 rows=0 loops=1)
        Filter: (eligible AND ('{.X}'::text[] && exchange_index) AND (type = 'discussion'::text) AND ((((data -> 'display_tweet'::text) -> 'stocktwit'::text) -> 'id'::text) IS NOT NULL) AND (((data ->> 'sentiment_score'::text))::bigint > 0) AND (((((data -> 'display_tweet'::text) -> 'stocktwit'::text) ->> 'id'::text))::bigint IS NULL))
        Rows Removed by Filter: 16093269
        Buffers: shared hit=13713180 read=1267819 dirtied=34 written=713
        I/O Timings: read=7644.206 write=7.294
Planning time: 0.366 ms
Execution time: 31900.909 ms

Примечание: Имеются jsonb индексов B-дерева на каждое условие jsonb, используемое в этомзапрос.exchange_index и company_index имеют индексы GIN.

ОБНОВЛЕНИЕ После изменения запроса Лоренца:

Limit  (cost=150634.15..150634.27 rows=50 width=211) (actual time=15925.828..15925.828 rows=0 loops=1)
  Buffers: shared hit=1137490 read=680349 written=2
  I/O Timings: read=2896.702 write=0.038
  ->  Sort  (cost=150634.15..150652.53 rows=7352 width=211) (actual time=15925.827..15925.827 rows=0 loops=1)
        Sort Key: bundles.id DESC
        Sort Method: quicksort  Memory: 25kB
        Buffers: shared hit=1137490 read=680349 written=2
        I/O Timings: read=2896.702 write=0.038
        ->  Bitmap Heap Scan on bundles  (cost=56666.15..150316.40 rows=7352 width=211) (actual time=15925.816..15925.816 rows=0 loops=1)
              Recheck Cond: (('{.X}'::text[] && exchange_index) AND (type = 'discussion'::text))
              Filter: (eligible AND ((((data -> 'display_tweet'::text) -> 'stocktwit'::text) -> 'id'::text) IS NOT NULL) AND (((data ->> 'sentiment_score'::text))::bigint > 0) AND (((((data -> 'display_tweet'::text) -> 'stocktwit'::text) ->> 'id'::text))::bigint IS NULL))
              Rows Removed by Filter: 273230
              Heap Blocks: exact=175975
              Buffers: shared hit=1137490 read=680349 written=2
              I/O Timings: read=2896.702 write=0.038
              ->  BitmapAnd  (cost=56666.15..56666.15 rows=23817 width=0) (actual time=1895.890..1895.890 rows=0 loops=1)
                    Buffers: shared hit=37488 read=85559
                    I/O Timings: read=325.535
                    ->  Bitmap Index Scan on bundles2_exchange_index_ops_idx  (cost=0.00..6515.57 rows=863703 width=0) (actual time=218.690..218.690 rows=892669 loops=1)
                          Index Cond: ('{.X}'::text[] && exchange_index)
                          Buffers: shared hit=7 read=313
                          I/O Timings: read=1.458
                    ->  Bitmap Index Scan on bundles_eligible_idx  (cost=0.00..23561.74 rows=2476877 width=0) (actual time=436.719..436.719 rows=2569331 loops=1)
                          Index Cond: (eligible = true)
                          Buffers: shared hit=37473
                    ->  Bitmap Index Scan on bundles2_type_idx  (cost=0.00..26582.83 rows=2706276 width=0) (actual time=1052.267..1052.267 rows=2794517 loops=1)
                          Index Cond: (type = 'discussion'::text)
                          Buffers: shared hit=8 read=85246
                          I/O Timings: read=324.077
Planning time: 0.433 ms
Execution time: 15928.959 ms

1 Ответ

0 голосов
/ 28 сентября 2018

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

Здесь есть несколько вещей:

  1. Видя страницы dirtied и written во время сканирования индекса, я подозреваю, что в вашей таблице довольно много «мертвых кортежей».Когда сканирование индекса посещает их и замечает, что они мертвы, оно «убивает» эти записи индекса, так что последующие сканирования индекса не должны повторять эту работу.

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

    Вы можете уменьшить эту проблему, запустив VACUUM для таблицы или убедившись, что автоочистка обрабатывает таблицу достаточно часто.

  2. Однако ваша основная проблема заключается в том, что предложение LIMIT побуждает PostgreSQL использовать следующую стратегию:

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

    К сожалению, ему нужно сканировать 16093319 строк, пока не найдет 50 совпадений.Строки в конце таблицы «high id» не соответствуют условию.PostgreSQL не знает об этой корреляции.

    Решение состоит в том, чтобы отговорить PostgreSQL идти по этому пути.Самый простой способ - сбросить все индексы на id, но, учитывая его имя, возможно, неосуществимо.

    Другой способ - не дать PostgreSQL «увидеть» предложение LIMIT при планировании сканирования.:

    SELECT id, data, type
    FROM (SELECT id, data, type
          FROM collection.bundles
          WHERE /* all your complicated conditions */
          OFFSET 0) subquery
    ORDER BY id DESC
    LIMIT 50;
    

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

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