Оптимизация поиска последних событий и использования кэша с помощью `random_page_cost` на postgres - PullRequest
0 голосов
/ 16 мая 2019

У меня есть таблица, в которой хранится информация о клиенте, временная метка и диапазон времени события.

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

event_index (идентификатор клиента, время)

state_index (customer_id, end, start desc)

Подавляющее большинство запросов в последние несколько дней запрашивают информацию о состоянии и событиях.

Это пример текста запроса (events имеет идентичную проблему, как я опишу для states):

SELECT "states".*
FROM "states"
WHERE ("states"."customer_id" = $1 AND "states"."start" < $2)
       AND ("states"."end" IS NULL OR "states"."end" > $3)
       AND ("states"."obsolete" = $4)
ORDER BY "states"."start" DESC

Я вижу, что иногда планировщик запросов использует только фильтр customer_id для фильтрации, а затем фильтрует с использованием кучи сканирования всех строк для клиента:

Sort  (cost=103089.00..103096.17 rows=2869 width=78)
  Sort Key: start DESC
  ->  Bitmap Heap Scan on states  (cost=1222.56..102924.23 rows=2869 width=78)
        Recheck Cond: (customer_id = '----'::bpchar)
        Filter: ((NOT obsolete) AND ((start)::double precision < '1557711009'::double precision) AND ((end IS NULL) OR ((end)::double precision > '1557666000'::double precision)))
        ->  Bitmap Index Scan on states_index  (cost=0.00..1221.85 rows=26820 width=0)
              Index Cond: (customer_id = '----'::bpchar)

Это в отличие от того, что я вижу в сеансе вручную:

Sort Key: start DESC
Sort Method: quicksort  Memory: 25kB
->  Bitmap Heap Scan on states  (cost=111.12..9338.04 rows=1 width=78) (actual time=141.674..141.674 rows=0 loops=1)
      Recheck Cond: (((customer_id = '-----'::bpchar) AND (end IS NULL) AND (start < '1557349200'::numeric)) OR ((customer_id = '----'::bpchar) AND (end > '1557249200'::numeric) AND (start < '1557349200'::numeric)))
      Filter: ((NOT obsolete) AND ((title)::text = '---'::text))
      Rows Removed by Filter: 112
      Heap Blocks: exact=101
      ->  BitmapOr  (cost=111.12..111.12 rows=2333 width=0) (actual time=4.198..4.198 rows=0 loops=1)
            ->  Bitmap Index Scan on states_index  (cost=0.00..4.57 rows=1 width=0) (actual time=0.086..0.086 rows=0 loops=1)
                  Index Cond: ((customer_id = '----'::bpchar) AND (end IS NULL) AND (start < '1557349200'::numeric))
            ->  Bitmap Index Scan on state_index  (cost=0.00..106.55 rows=2332 width=0) (actual time=4.109..4.109 rows=112 loops=1)
                  Index Cond: ((customer_id = '---'::bpchar) AND (end > '1557262800'::numeric) AND (start < '1557349200'::numeric))

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

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

Я использовал частичный индекс на прошлой неделе, чтобы посмотреть, падает ли время запроса, но postgres использует его только иногда. Это решает проблему, когда используется частичный индекс, так как в этом индексе не существует старых строк, но, к сожалению, postgres по-прежнему выбирает больший индекс, даже если это не нужно. Я пробежал vacuum analyze, но без видимого эффекта.

Я пытался увидеть попадания в кеш, используя это:

  Database Name   | Temporary files | Size of temporary files |  Block Hits   | Block Reads 
------------------+-----------------+-------------------------+---------------+-------------
 customers        |            1922 |             18784440622 |   69553504584 |  2401546773

А потом я вычислил (block_hits/(block_hits + block_reads)):

>>> 69553504584.0 / (69553504584.0 + 2401546773.0)
0.9666243477322406

Так что это показывает мне ~ 96,6% кеша (я хочу, чтобы он был намного ближе к 100, потому что я знаю природу запросов)

Я также пытался увеличить статистику (SET STATISTICS) для customer_id, start и end, так как это казалось рекомендацией для людей, сталкивающихся с проблемами планировщика запросов. Это также не помогло (и я побежал анализировать после ...).

После прочтения этой проблемы я понял, что есть способ заставить планировщик запросов предпочитать сканирование индекса с использованием меньшего значения random_page_cost, чем по умолчанию (4). Я также видел сообщение, поддерживающее это здесь:

https://amplitude.engineering/how-a-single-postgresql-config-change-improved-slow-query-performance-by-50x-85593b8991b0

Имеет ли это смысл для моего варианта использования? Позволит ли планировщик запросов использовать индекс чаще всего (желательно всегда)?

Если нет - могу ли я что-то еще сделать, чтобы уменьшить время запроса? Я знаю, что разбиение может быть очень эффективным, но кажется излишним и не полностью поддерживается в моей текущей версии postgres (9.5.9), насколько я могу судить по тому, что я прочитал.

Обновление: После понижения random_page_cost Я не вижу окончательной разницы. Есть еще моменты, когда планировщик запросов выбирает использовать только часть. индекс для гораздо более медленного результата.

Любые предложения приветствуются.

Спасибо:)

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