Оптимизация индексов для большой таблицы PostgreSQL

У меня есть большая таблица (location_history) с uid и geohash, и они оба являются первичными ключами и проиндексированы.

Indexes:
    "location_history_pkey" PRIMARY KEY, btree (uid, geohash)
    "ix_location_history_geohash" btree (geohash)
    "ix_location_history_uid" btree (uid)

Но когда я запрашиваю данные со следующими ограничениями, они работают очень медленно (169 секунд!), Даже если два столбца уже проиндексированы:

EXPLAIN (analyze, verbose, buffers) SELECT * FROM location_history WHERE uid = 123 AND geohash >= 'hzzzzzzzzzz';
                                                                            QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Index Scan using location_history_pkey on public.location_history  (cost=0.71..84735.92 rows=22438 width=29) (actual time=1.434..114713.751 rows=189319 loops=1)
   Output: created_at, updated_at, uid, geohash, is_from_zenly
   Index Cond: ((location_history.uid = 123) AND ((location_history.geohash)::text >= 'hzzzzzzzzzz'::text))
   Buffers: shared hit=5822 read=179864 dirtied=132 written=19338
   I/O Timings: read=111433.601 write=2564.930
 Query Identifier: -7646491345250917333
 Planning Time: 0.103 ms
 Execution Time: 114746.908 ms
(8 rows)

А вот и результат от pgstattuple():

SELECT * FROM pgstattuple(416708);
  table_len   | tuple_count |  tuple_len   | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent
--------------+-------------+--------------+---------------+------------------+----------------+--------------------+------------+--------------
 812873588736 | 15570800464 | 685124233392 |         84.28 |             1506 |          66264 |                  0 |  394834592 |         0.05
(1 row)

bloat_ratio составляет около 34%:

SELECT 100-(pgstatindex('location_history_pkey')).avg_leaf_density;

      ?column?
--------------------
 34.260000000000005
(1 row)

Не могли бы вы поделиться советами по ускорению запроса? Спасибо.

Сколько всего строк в таблице? Сколько оперативной памяти на сервере?

kometen 17.07.2023 09:47

@kometen сейчас более 10 миллиардов строк, а объем оперативной памяти составляет 128 ГБ (экземпляр AWS db.m6i.8xlarge)

iForests 17.07.2023 09:50

Если у вас было много обновлений/удалений, возможно, стоит переиндексировать + вакуумный анализ. Что такое «геохэш» (довольно необычно иметь >= в тексте)?

Bruno 17.07.2023 09:53

Не могли бы вы поделиться результатами объяснения (анализа, подробных сведений, буферов) для этого утверждения? Это дает вам подробную информацию о плане запроса, выполнении и почему все так быстро/медленно, как есть.

Frank Heikens 17.07.2023 09:57

@Bruno В этой таблице есть только INSERT и SELECT. geohash — это строка из 7 цифр, например dr5rfk7, обозначающая местоположение (geohash.softeng.co/dr5rfk7)

iForests 17.07.2023 09:58

Я бы попробовал пару вещей. (1) В достаточно современной версии PostgreSQL разделите таблицу, по крайней мере, по uid (например, по диапазону или хешу). (2) Интересно, эффективен ли >= для текста, поэтому, возможно, его стоит преобразовать в int. Если я не ошибаюсь, каждый буквенно-цифровой символ в geohash представляет 5 бит: все должно легко уместиться в 64-битное целое число (преобразование должно быть легким, если все они имеют длину 7 символов).

Bruno 17.07.2023 10:15

@FrankHeikens Конечно, я запущу EXPLAIN и обновлю исходный пост после того, как всплеск трафика закончится. Спасибо за вашу помощь!

iForests 17.07.2023 10:27

@Bruno (1) Мы уже начали работать над секционированием, но перед этим мы хотели бы попробовать оптимизировать индекс. (2) Хорошо, попробую, спасибо!

iForests 17.07.2023 10:29

@FrankHeikens Я обновил исходный пост, включив его в результат объяснения (анализ, подробный, буферный), спасибо!

iForests 17.07.2023 10:48

IO довольно медленный, всего несколько МБ в секунду. Проверьте pgstattuple, чтобы узнать, есть ли у вас раздувание индекса: postgresql.org/docs/current/pgstattuple.html

Frank Heikens 17.07.2023 10:55

Сколько существует различных uid? С меньшим количеством uid индекс также становится менее полезным, планировщик запросов может в некоторых случаях прибегать к сканированию таблицы.

kometen 17.07.2023 11:36

@kometen Не уверен, что можно не иметь индекса (uid, geohash), так как это первичный ключ. Индекс только на geohash есть, но не используется.

Bruno 17.07.2023 12:31

@kometen uid около 10 миллионов

iForests 17.07.2023 12:42

@FrankHeikens Я обновил пост, указав результат от pgstattuple(). Я новичок в раздувании индекса, можете ли вы сказать мне, указывают ли результаты на раздувание индекса? Если да, то следующий шаг, который мне нужно сделать, чтобы «REINDEX»? Спасибо.

iForests 17.07.2023 12:47

@FrankHeikens Я также включил bloat_ratio, что составляет 34%

iForests 17.07.2023 16:47
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
15
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы могли бы получить большое улучшение, если бы у вас было сканирование только индекса, которое вы могли бы включить в индекс все столбцы, необходимые для запроса, по-видимому (uid, geohash, is_from_zenly, created_at, updated_at). В этом случае нужно будет посещать только страницы индекса, а не (случайно разбросанные) страницы таблицы, если таблица хорошо очищена. Вы можете получить этот «покрывающий» индекс без частичного дублирования индекса первичного ключа, выполнив:

alter table location_history add constraint location_history_pkey primary key (uid, geohash) include (is_from_zenly, created_at, updated_at)

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

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

Другие вопросы по теме