Как улучшить производительность запросов на основе дат для большой таблицы? - PullRequest
2 голосов
/ 05 апреля 2019

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

Стратегия индексирования для различных комбинаций предложений WHERE, вкл. текстовые шаблоны

Как получить запрос date_part для индекса попадания?

Подробное описание на этот раз - надеюсь, это поможет точно определить проблему:

  • PG версия 10.7 (работает на герою
  • Общий размер БД: 18,4 ГБ (содержит данные за 2 месяца и будет расти примерно с одинаковой скоростью каждый месяц)
  • 15 ГБ ОЗУ
  • Общее доступное хранилище: 512 ГБ
  • Самая большая таблица (та, над которой работает самый медленный запрос) - 9,6 ГБ (это самый большой кусок всей БД) - около 10 миллионов записей

Схема самой большой таблицы:

-- Table Definition ----------------------------------------------

CREATE TABLE reportimpression (
    datelocal timestamp without time zone,
    devicename text,
    network text,
    sitecode text,
    advertisername text,
    mediafilename text,
    gender text,
    agegroup text,
    views integer,
    impressions integer,
    dwelltime numeric
);

-- Indices -------------------------------------------------------

CREATE INDEX reportimpression_feb2019_index ON reportimpression(datelocal timestamp_ops) WHERE datelocal >= '2019-02-01 00:00:00'::timestamp without time zone AND datelocal < '2019-03-01 00:00:00'::timestamp without time zone;
CREATE INDEX reportimpression_mar2019_index ON reportimpression(datelocal timestamp_ops) WHERE datelocal >= '2019-03-01 00:00:00'::timestamp without time zone AND datelocal < '2019-04-01 00:00:00'::timestamp without time zone;
CREATE INDEX reportimpression_jan2019_index ON reportimpression(datelocal timestamp_ops) WHERE datelocal >= '2019-01-01 00:00:00'::timestamp without time zone AND datelocal < '2019-02-01 00:00:00'::timestamp without time zone;

Медленный запрос:

SELECT
    date_part('hour', datelocal) AS hour,
    SUM(CASE WHEN gender = 'male' THEN views ELSE 0 END) AS male,
    SUM(CASE WHEN gender = 'female' THEN views ELSE 0 END) AS female
FROM reportimpression
WHERE
    datelocal >= '3-1-2019' AND
    datelocal < '4-1-2019'
GROUP BY date_part('hour', datelocal)
ORDER BY date_part('hour', datelocal)

Диапазон дат в этом запросе обычно равен целому месяцу (он принимает пользовательский ввод из веб-отчета) - как вы можете видеть, я попытался создать индекс для данных за каждый месяц. Это помогло, но, насколько я могу судить, если запрос не был запущен недавно (помещение результатов в кэш), он все равно может занять до минуты.

Объяснить результаты анализа:

Finalize GroupAggregate  (cost=1035890.38..1035897.86 rows=1361 width=24) (actual time=3536.089..3536.108 rows=24 loops=1)
  Group Key: (date_part('hour'::text, datelocal))
  ->  Sort  (cost=1035890.38..1035891.06 rows=1361 width=24) (actual time=3536.083..3536.087 rows=48 loops=1)
        Sort Key: (date_part('hour'::text, datelocal))
        Sort Method: quicksort  Memory: 28kB
        ->  Gather  (cost=1035735.34..1035876.21 rows=1361 width=24) (actual time=3535.926..3579.818 rows=48 loops=1)
              Workers Planned: 1
              Workers Launched: 1
              ->  Partial HashAggregate  (cost=1034735.34..1034740.11 rows=1361 width=24) (actual time=3532.917..3532.933 rows=24 loops=2)
                    Group Key: date_part('hour'::text, datelocal)
                    ->  Parallel Index Scan using reportimpression_mar2019_index on reportimpression  (cost=0.09..1026482.42 rows=3301168 width=17) (actual time=0.045..2132.174 rows=2801158 loops=2)
Planning time: 0.517 ms
Execution time: 3579.965 ms

Я бы не подумал, что 10 миллионов записей будет слишком много, особенно если учесть, что я недавно увеличил план PG, на который я нацелился, чтобы попытаться использовать ресурсы, поэтому я предполагаю, что проблема все еще не решена. либо мои индексы, либо мои запросы не очень эффективны.

Ответы [ 2 ]

2 голосов
/ 06 апреля 2019

A материализованное представление - путь к тому, что вы наметили. Запрос данных за последние месяцы только для чтения работает без их обновления. Возможно, вы захотите сделать специальный случай текущего месяца, если вам тоже нужно это покрыть.

Базовый запрос все еще может получать выгоду от индекса, и вы можете выбрать два направления:

Во-первых, частичные индексы , как у вас сейчас, не будут много покупать в вашем сценарии, не стоит. Если вы собираете намного больше данных за месяц и в основном делаете запрос по месяцам (и добавляете / удаляете строки по месяцам) разбиение таблицы может быть идеей, тогда ваши индексы также будут автоматически разделены. Я бы подумал о Postgres 11 или даже о следующем Postgres 12 для этого.)

Если ваши строки широкие , создайте индекс, который разрешает сканирование только по индексу . Как:

CREATE INDEX reportimpression_covering_idx ON reportimpression(datelocal, views, gender);

Связанный:

или INCLUDE дополнительные столбцы в Postgres 11 или более поздней версии:

CREATE INDEX reportimpression_covering_idx ON reportimpression(datelocal) INCLUDE (views, gender);

Иначе , если ваши строки физически отсортированы по datelocal, рассмотрите индекс BRIN . Это очень мало и, вероятно, примерно так же быстро, как индекс B-дерева для вашего случая. (Но будучи таким маленьким, он будет намного легче кэшироваться и не будет выталкивать другие данные так же.)

CREATE INDEX reportimpression_brin_idx ON reportimpression USING BRIN (datelocal);

Возможно, вас заинтересует CLUSTER или pg_repack для физической сортировки строк таблицы. pg_repack может сделать это без исключительных блокировок таблицы и даже без индекса btree (требуется CLUSTER). Но это дополнительный модуль, не входящий в стандартную поставку Postgres.

Связанный:

2 голосов
/ 05 апреля 2019

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

Вещи, которые вы можете улучшить, в порядке убывания эффективности:

  • Использовать материализованное представление, которое предварительно агрегирует данные

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

  • Используйте только один индекс вместо нескольких секционированных. Это в первую очередь не рекомендация по производительности (запрос, вероятно, не будет заметно медленнее, если у вас нет большого числа индексов), но это облегчит бремя управления.

...