У меня есть таблица событий с 30 миллионами строк. Следующий запрос возвращает в 25 секундМедленный запрос PostgreSQL с (неверными?) Индексами
SELECT DISTINCT "events"."id", "calendars"."user_id"
FROM "events"
LEFT JOIN "calendars" ON "events"."calendar_id" = "calendars"."id"
WHERE "events"."deleted_at" is null
AND tstzrange('2016-04-21T12:12:36-07:00', '2016-04-21T12:22:36-07:00') @> lower(time_range)
AND ("status" is null or (status->>'pre_processed') IS NULL)
status
является jsonb
колонки с индексом на status->>'pre_processed'
. Вот другие индексы, которые были созданы в таблице событий. time_range
имеет тип TSTZRANGE
.
CREATE INDEX events_time_range_idx ON events USING gist (time_range);
CREATE INDEX events_lower_time_range_index on events(lower(time_range));
CREATE INDEX events_upper_time_range_index on events(upper(time_range));
CREATE INDEX events_calendar_id_index on events (calendar_id)
Я определенно вышел из своей зоны комфорта и стараюсь сократить время запроса. Вот выход объяснить анализ
HashAggregate (cost=7486635.89..7486650.53 rows=1464 width=48) (actual time=26989.272..26989.306 rows=98 loops=1)
Group Key: events.id, calendars.user_id
-> Nested Loop Left Join (cost=0.42..7486628.57 rows=1464 width=48) (actual time=316.110..26988.941 rows=98 loops=1)
-> Seq Scan on events (cost=0.00..7475629.43 rows=1464 width=50) (actual time=316.049..26985.344 rows=98 loops=1)
Filter: ((deleted_at IS NULL) AND ((status IS NULL) OR ((status ->> 'pre_processed'::text) IS NULL)) AND ('["2016-04-21 19:12:36+00","2016-04-21 19:22:36+00")'::tstzrange @> lower(time_range)))
Rows Removed by Filter: 31592898
-> Index Scan using calendars_pkey on calendars (cost=0.42..7.50 rows=1 width=48) (actual time=0.030..0.031 rows=1 loops=98)
Index Cond: (events.calendar_id = (id)::text)
Planning time: 1.468 ms
Execution time: 26989.370 ms
А вот объяснить анализ с events.deleted_at
части запроса удаляют,
HashAggregate (cost=7487382.57..7487398.33 rows=1576 width=48) (actual time=23880.466..23880.503 rows=115 loops=1)
Group Key: events.id, calendars.user_id
-> Nested Loop Left Join (cost=0.42..7487374.69 rows=1576 width=48) (actual time=16.612..23880.114 rows=115 loops=1)
-> Seq Scan on events (cost=0.00..7475629.43 rows=1576 width=50) (actual time=16.576..23876.844 rows=115 loops=1)
Filter: (((status IS NULL) OR ((status ->> 'pre_processed'::text) IS NULL)) AND ('["2016-04-21 19:12:36+00","2016-04-21 19:22:36+00")'::tstzrange @> lower(time_range)))
Rows Removed by Filter: 31592881
-> Index Scan using calendars_pkey on calendars (cost=0.42..7.44 rows=1 width=48) (actual time=0.022..0.023 rows=1 loops=115)
Index Cond: (events.calendar_id = (id)::text)
время планирования: 0,372 мс Время Исполнение: 23880.571 мс
I добавили индекс в столбец status
. Все остальное, что уже есть, и я не уверен, как двигаться дальше. Любые предложения о том, как получить время запроса до более управляемого номера?
Структуры событий и таблицы календарей будет полезно. Если вы можете опубликовать результат анализа объяснения, а не просто объяснение, которое может помочь. – e4c5
@ e4c5 Спасибо. Я добавил анализ объяснений. Я могу добавить структуру позже. Я упомянул поля, на которые я обращаюсь, это TSTZRANGE и JSONB. deleted_at - это только временная метка – FajitaNachos
Не уверен, что вам нужно '@> lower (time_range)' не будет «перекрывать» то же самое? 'where ... @> time_range' - который может использовать индекс gist для этого столбца. Также какое из условий удаляет большую часть строк? Условие на 'status', то есть на' time_range' или на 'deleted_at'? –