У меня есть экземпляр PostgreSQL 9.2, работающий на RHEL 6.3, 8-ядерный компьютер с 16 ГБ ОЗУ. Сервер выделен для этой базы данных. Учитывая, что файл postgresql.conf по умолчанию довольно консервативен в отношении настроек памяти, я подумал, что было бы неплохо разрешить Postgres использовать больше памяти. К моему удивлению, следующий совет на wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server значительно замедлил практически каждый выполняемый мной запрос, но он явно более заметен в более сложных запросах.
Я также попытался запустить pgtune, который дал следующую рекомендацию с более настроенными параметрами, но это ничего не изменило. Он предлагает shared_buffers размером 1/4 ОЗУ, что, по-видимому, согласуется с рекомендациями в других местах (и в частности в PG wiki).
default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80
Я попытался переиндексировать всю базу данных после изменения настроек (используя reindex database
), но это тоже не помогло. Я играл с shared_buffers и work_mem. Постепенно изменяя их с очень консервативных значений по умолчанию (128 КБ / 1 МБ), постепенно снижается производительность.
Я запустил EXPLAIN (ANALYZE,BUFFERS)
несколько запросов, и виновник, похоже, в том, что Hash Join значительно медленнее. Мне не понятно почему.
Чтобы привести конкретный пример, у меня есть следующий запрос. Он работает ~ 2100 мс в конфигурации по умолчанию и ~ 3300 мс в конфигурации с увеличенным размером буфера:
select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';
EXPLAIN (ANALYZE,BUFFERS)
для запроса выше:
- Буферы по умолчанию: http://explain.depesz.com/s/xaHJ
- Большие буферы: http://explain.depesz.com/s/Plk
Вопрос в том, почему я наблюдаю снижение производительности при увеличении размеров буфера? У машины точно не хватает памяти. Выделение, если для разделяемой памяти в ОС задано ( shmmax
и shmall
) очень большое значение, это не должно быть проблемой. Я не получаю никаких ошибок в журнале Postgres либо. Я использую автовакуум в конфигурации по умолчанию, но не ожидаю, что это как-то связано с этим. Все запросы выполнялись на одной и той же машине с интервалом в несколько секунд, только с измененной конфигурацией (и перезапускали PG).
Изменить: Я только что нашел один особенно интересный факт: когда я выполняю тот же тест на моем iMac в середине 2010 года (OSX 10.7.5) также с Postgres 9.2.1 и 16 ГБ оперативной памяти, я не испытываю замедления. В частности:
set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms
Когда я делаю точно такой же запрос (тот, что выше) с точно такими же данными на сервере, я получаю 2100 мс с work_mem = 1 МБ и 3200 мс с 96 МБ.
У Mac есть SSD, так что это понятно быстрее, но он демонстрирует поведение, которое я ожидаю.
Смотрите также последующее обсуждение pgsql-performance .
источник
Ответы:
Прежде всего, имейте в виду, что work_mem выполняется для каждой операции, и поэтому он может быстро стать чрезмерным. В общем, если у вас нет проблем с медленной сортировкой, я оставляю work_mem в покое, пока она вам не понадобится.
Глядя на ваши планы запросов, меня поражает то, что обращения к буферу сильно различаются, если смотреть на эти два плана, и что даже последовательное сканирование выполняется медленнее. Я подозреваю, что проблема связана с кэшированием с упреждающим чтением и нехваткой места для этого. Это означает, что вы смещаете память для повторного использования индексов и чтения таблиц на диске.
Насколько я понимаю, PostgreSQL будет смотреть на кеш для страницы перед чтением ее с диска, потому что он не знает, будет ли кеш ОС содержать эту страницу. Поскольку страницы затем остаются в кеше и поскольку этот кеш медленнее, чем кеш ОС, это меняет виды запросов, которые выполняются быстро, по сравнению с типами, которые выполняются медленно. На самом деле, читая планы, кроме проблем с work_mem, похоже, что вся информация вашего запроса поступает из кеша, но вопрос в том, какой именно кеш.
work_mem : сколько памяти мы можем выделить для сортировки или связанной операции соединения. Это для каждой операции, а не для оператора или серверной части, поэтому один сложный запрос может многократно использовать этот объем памяти. Не ясно, что вы достигаете этого предела, но это стоит отметить и знать. если вы увеличите это значение слишком сильно, вы потеряете память, которая может быть доступна для кэша чтения и общих буферов.
shared_buffers : сколько памяти выделить для действительной очереди страниц PostgreSQL. Теперь, в идеале, интересный набор вашей базы данных должен оставаться в кеше памяти здесь и в буферах чтения. Тем не менее, это гарантирует, что наиболее часто используемая информация во всех бэкэндах кэшируется, а не сбрасывается на диск. В Linux этот кеш значительно медленнее, чем дисковый кеш ОС, но он гарантирует, что дисковый кеш ОС не работает и прозрачен для PostgreSQL. Это довольно ясно, где ваша проблема.
Так что получается, что когда у нас есть запрос, мы сначала проверяем общие буферы, так как PostgreSQL хорошо знает этот кеш, и ищем страницы. Если их там нет, мы просим ОС открыть их из файла, и если ОС кэширует результат, она возвращает кэшированную копию (это быстрее, чем общие буферы, но Pg не может определить, кэширована она или включена. диск и диск намного медленнее, поэтому PostgreSQL обычно не использует этот шанс). Имейте в виду, что это также влияет на случайный и последовательный доступ к страницам. Таким образом, вы можете получить лучшую производительность при более низких настройках shared_buffers.
У меня есть ощущение, что вы, вероятно, получите лучшую или, по крайней мере, более стабильную производительность в средах с высоким уровнем параллелизма и большими настройками shared_buffer. Также имейте в виду, что PostgreSQL захватывает эту память и удерживает ее, поэтому, если в системе выполняются другие функции, буферы чтения будут хранить файлы, прочитанные другими процессами. Это очень большая и сложная тема. Большие параметры общего буфера обеспечивают лучшие гарантии производительности, но могут в некоторых случаях обеспечивать меньшую производительность.
источник
Помимо, казалось бы, парадоксального эффекта увеличения
work_mem
производительности (у @Chris может быть объяснение), вы можете улучшить свою функцию по крайней мере двумя способами.LEFT JOIN
сJOIN
. Это может запутать планировщик запросов и привести к худшим планам.pi.firstname
иpi.lastname
поддерживать , не привязанныеLIKE
поиски. (Более короткие шаблоны, такие'%a%'
как также поддерживаются, но индекс вряд ли поможет для неселективных предикатов.):Или один многоколонный индекс:
Должен сделать ваш запрос немного быстрее. Для этого вам нужно установить дополнительный модуль pg_trgm . Подробности по этим связанным вопросам:
Кроме того, вы пытались установить
work_mem
локально - только для текущей транзакции ?Это удерживает параллельные транзакции от того, что они потребляют больше оперативной памяти, возможно, от голода друг друга.
источник