У меня есть стек 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
(в то время как рендеринг плитки это просто select
s).
Я также не заметил, 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 секунды. Пространственные индексы имеют значение! :)
источник
Ответы:
Выполнение выходных данных Explain Anlayze на веб-сайте объяснения.depesz.com подчеркивает, что основная часть медлительности связана с этим действием:
Это было проиндексировано раньше? Вы можете проиндексировать это сейчас?
В поисках этой проблемной области я также нашел соответствующие вопросы и ответы на сайте Open Street Map:
источник
PostgreSQL может использовать только одно ядро для любого запроса. Он обеспечивает хорошую параллельную производительность со многими параллельными запросами, но не получает большого количества ядер для рабочих нагрузок всего пары очень больших запросов. Так что, если вы выполняете только один запрос, то 5% не так уж и удивительно, хотя я ожидаю, что в 8-ядерных системах он составит 12%.
Отсутствие Iowait предполагает, что он, вероятно, не страдает от дискового ввода-вывода.
Так что - это не узкое место на процессоре или на вводе / выводе.
Возможно ли, что запрос просто временно заблокирован блокировкой? Проверьте
pg_stat_activity
запрос и присоединитесьpg_locks
к нему, чтобы увидеть, есть ли какие-либо непризнанные блокировки. (Есть постоянные запросы о мониторинге блокировки Pg).Следующее, что нужно сделать, - запустить системные тесты более низкого уровня. Запустите
pg_test_fsync
, используйте тесты sysbench на ЦП и ввод-вывод и т. Д. Если они также работают плохо, сообщите об этом вашему провайдеру хостинга.Вам также нужно
perf top -a
немного собрать вывод, посмотреть, что он на самом деле делает.источник