Почему Postgres бездействует на 95% без файлового ввода-вывода?

8

У меня есть стек TileMill / PostGIS, работающий на 8-ядерной виртуальной машине Ubuntu 12.04 в облаке OpenStack. Это перестройка очень похожей системы, которая прекрасно работала на очень похожем оборудовании (то же облако, но другое физическое оборудование, я полагаю) на прошлой неделе. Я попытался восстановить стек точно так же, как он был (используя некоторые скрипты, которые я создал).

Все работает, но база данных выполняет мучительно медленные запросы, что в конечном итоге проявляется в очень медленной генерации тайлов. Пример запроса (подсчитайте количество пабов в радиусе каждого города в Австралии), который раньше занимал около 10-20 секунд, теперь занимает более 10 минут:

explain (analyze, buffers) update places set pubs = 
(select count(*) from planet_osm_point p where p.amenity = 'pub' and st_dwithin(p.way,places.way,scope)) +
(select count(*) from planet_osm_polygon p where p.amenity = 'pub' and st_dwithin(p.way,places.way,scope)) ;
 Update on places  (cost=0.00..948254806.93 rows=9037 width=160) (actual time=623321.558..623321.558 rows=0 loops=1)
   Buffers: shared hit=132126300
   ->  Seq Scan on places  (cost=0.00..948254806.93 rows=9037 width=160) (actual time=68.130..622931.130 rows=9037 loops=1)
         Buffers: shared hit=132107781
         SubPlan 1
           ->  Aggregate  (cost=12.95..12.96 rows=1 width=0) (actual time=0.187..0.188 rows=1 loops=9037)
                 Buffers: shared hit=158171
                 ->  Index Scan using planet_osm_point_index on planet_osm_point p  (cost=0.00..12.94 rows=1 width=0) (actual time=0.163..0.179 rows=0 loops=9037)
                       Index Cond: (way && st_expand(places.way, (places.scope)::double precision))
                       Filter: ((amenity = 'pub'::text) AND (places.way && st_expand(way, (places.scope)::double precision)) AND _st_dwithin(way, places.way, (places.scope)::double precision))
                       Buffers: shared hit=158171
         SubPlan 2
           ->  Aggregate  (cost=104917.24..104917.25 rows=1 width=0) (actual time=68.727..68.728 rows=1 loops=9037)
                 Buffers: shared hit=131949237
                 ->  Seq Scan on planet_osm_polygon p  (cost=0.00..104917.24 rows=1 width=0) (actual time=68.138..68.716 rows=0 loops=9037)
                       Filter: ((amenity = 'pub'::text) AND (way && st_expand(places.way, (places.scope)::double precision)) AND (places.way && st_expand(way, (places.scope)::double precision)) AND _st_dwithin(way, places.way, (places.scope)::double precision))
                       Buffers: shared hit=131949237
 Total runtime: 623321.801 ms

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

Сервер имеет 32 ГБ ОЗУ, и я настроил Postgres следующим образом (следуя советам, найденным в Интернете):

shared_buffers = 8GB
autovacuum = on
effective_cache_size = 8GB
work_mem = 128MB
maintenance_work_mem = 64MB
wal_buffers = 1MB
checkpoint_segments = 10

iostat показывает, что ничего не читается, записывается немного данных (не знаю, где и почему) и 95% простаивает процессор:

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           5.40    0.00    0.00    0.11    0.00   94.49

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
vda               0.20         0.00         0.80          0          8
vdb               2.30         0.00        17.58          0        176

Пример вывода из vmstat:

  procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
...
 1  0      0 18329748 126108 12600436    0    0     0    18  148  140  5  0 95  0
 2  0      0 18329400 126124 12600436    0    0     0     9  173  228  5  0 95  0

Держась за соломинку, я переместил каталог данных Postgres из vda в vdb, но, конечно, это не имело никакого значения.

Так что я в растерянности. Почему Postgres использует только 5% доступного процессора, когда не ожидает ввода-вывода? Я приветствовал бы любые предложения для дальнейшего исследования, другие инструменты, случайные вещи, чтобы попробовать.

Обновить

Я сделал снимок сервера и запустил его в другой части того же облака (в другой зоне доступности). Результаты были немного странными. vmstatна этом сервере сообщается о 12% -ной загрузке ЦП (которую я теперь понимаю как ожидаемое значение для одного запроса Postgres на 8-ядерном ВМ), хотя фактическое время выполнения запроса практически идентично (630 секунд против 623).

Теперь я понимаю, что этот конкретный запрос, вероятно, не является хорошим примером по этой причине: он может использовать только одно ядро, и это update(в то время как рендеринг плитки это просто selects).

Я также не заметил, explainчто, по-видимому planet_osm_polygon, не использует индекс. Это вполне может быть причиной, поэтому я буду преследовать это дальше.

Update2

Проблема определенно заключается в том, что индекс (ы) planet_osm_polygon используется / не используется. Их два (один создан osm2pgsql, другой создан мной по случайному руководству):

CREATE INDEX idx_planet_osm_polygon_tags
  ON planet_osm_polygon
  USING gist
  (tags);


CREATE INDEX planet_osm_polygon_pkey
  ON planet_osm_polygon
  USING btree
  (osm_id);

Думаю, статистика на planet_osm_polygon и planet_osm_point довольно показательна:

planet_osm_polygon:

Sequential Scans    194204  
Sequential Tuples Read  60981018608 
Index Scans 1574    
Index Tuples Fetched    0

planet_osm_point:

Sequential Scans    1142    
Sequential Tuples Read  12960604    
Index Scans 183454  
Index Tuples Fetched    43427685

Если я правильно понял, Postgres 1574 раза обыскивал planet_osm_polygon, но на самом деле ничего не нашел, поэтому провел смехотворно большое количество обысков грубой силы.

Новый вопрос: почему?

Тайна раскрыта

Благодаря ответу Фредерика Рамма ответ оказался довольно простым: по какой-то причине не было пространственного индекса. Восстановить их было тривиально:

create index planet_osm_polygon_polygon on planet_osm_polygon using gist(way);
create index planet_osm_polygon_point on planet_osm_point using gist(way);

Выполнение этого запроса теперь занимает 4,6 секунды. Пространственные индексы имеют значение! :)

Стив Беннетт
источник
Я понимаю, что эта запись довольно старая, однако у меня возникла похожая проблема. Я не могу создать planet_osm_polygon_point дважды, так как индекс уже существует. Однако не имеет значения, как называется индекс, верно?
Себастьян Борггрев
Хорошо, если индекс существует, почему вы хотите создать еще один? Но в любом случае вы можете отказаться от старого или переименовать новый.
Стив Беннетт
Я просто спрашиваю, так как оба индекса: создайте индекс planet_osm_polygon_point на planet_osm_polygon, используя gist (way); создать индекс planet_osm_polygon_point для planet_osm_point с помощью gist (way); называются planet_osm_polygon_point, что кажется ошибкой, если я что-то упустил.
Себастьян Борггрев,
Ой! Я не поняла Да, в моем ответе есть опечатка.
Стив Беннетт
Спасибо Стив, не могли бы вы также исправить опечатку в своем ответе для дальнейшего использования. Спасибо.
Себастьян Борггрев

Ответы:

4

Выполнение выходных данных Explain Anlayze на веб-сайте объяснения.depesz.com подчеркивает, что основная часть медлительности связана с этим действием:

Seq Scan on planet_osm_polygon p 

Это было проиндексировано раньше? Вы можете проиндексировать это сейчас?

В поисках этой проблемной области я также нашел соответствующие вопросы и ответы на сайте Open Street Map:

Марк Стосберг
источник
Спасибо за указание на это - я пропустил это. На самом деле в этой таблице два индекса. Обновление моего вопроса с дополнительной информацией.
Стив Беннетт
Ох - эта ссылка имела ответ. Да, хотя был «индекс», он был только в поле идентификатора, а не в фактическом поле геометрии («путь») - так бесполезно для пространственной индексации. Комментарии Фредерика содержали ответ.
Стив Беннетт
4

PostgreSQL может использовать только одно ядро ​​для любого запроса. Он обеспечивает хорошую параллельную производительность со многими параллельными запросами, но не получает большого количества ядер для рабочих нагрузок всего пары очень больших запросов. Так что, если вы выполняете только один запрос, то 5% не так уж и удивительно, хотя я ожидаю, что в 8-ядерных системах он составит 12%.

Отсутствие Iowait предполагает, что он, вероятно, не страдает от дискового ввода-вывода.

Так что - это не узкое место на процессоре или на вводе / выводе.

Возможно ли, что запрос просто временно заблокирован блокировкой? Проверьте pg_stat_activityзапрос и присоединитесь pg_locksк нему, чтобы увидеть, есть ли какие-либо непризнанные блокировки. (Есть постоянные запросы о мониторинге блокировки Pg).

Следующее, что нужно сделать, - запустить системные тесты более низкого уровня. Запустите pg_test_fsync, используйте тесты sysbench на ЦП и ввод-вывод и т. Д. Если они также работают плохо, сообщите об этом вашему провайдеру хостинга.

Вам также нужно perf top -aнемного собрать вывод, посмотреть, что он на самом деле делает.

Крейг Рингер
источник