16
PostgreSQL: настройка производительности Алексей Борзов (Sad Spirit) borz_off@cs.msu.su Содержание 1 Введение 2 1.1 Не используйте настройки по умолчанию .............. 2 1.2 Используйте актуальную версию сервера .............. 3 1.3 Стоит ли доверять тестам производительности ........... 3 2 Настройка сервера 3 2.1 Используемая память .......................... 4 2.1.1 Общий буфер сервера: shared_buffers ........... 4 2.1.2 Память для сортировки результата запроса: sort_mem .. 4 2.1.3 Память для работы команды VACUUM: vacuum_mem ... 5 2.2 Журнал транзакций и контрольные точки ............. 5 2.2.1 fsync и стоит ли его трогать ................. 5 2.2.2 Уменьшение количества контрольных точек ........ 6 2.2.3 Прочие параметры ....................... 6 2.3 Free Space Map: как избавиться от VACUUM FULL ........ 6 2.4 Прочие настройки ............................ 7 2.4.1 Оценка объёма кэша ОС: effective_cache_size ...... 7 2.4.2 Сбор статистики ......................... 7 2.5 Диски и файловые системы ...................... 8 2.5.1 Перенос журнала транзакций на отдельный диск ..... 8 3 Оптимизация БД и приложения 9 3.1 Поддержание базы в порядке ..................... 9 3.1.1 Команда VACUUM ....................... 9 3.1.2 Команда ANALYZE ....................... 10 3.1.3 pg_autovacuum .......................... 10 3.1.4 Команда REINDEX ....................... 10 3.2 Использование индексов ........................ 11 3.2.1 Команда EXPLAIN [ANALYZE] ................ 11 3.2.2 Использование собранной статистики ............ 12 3.2.3 Возможности индексов в PostgreSQL ............. 12 3.3 Перенос логики на сторону сервера .................. 13 3.4 Оптимизация конкретных запросов .................. 14 3.4.1 SELECT max(...)/min(...) FROM <огромная таблица> .. 14 3.4.2 SELECT count(*) FROM <огромная таблица> ....... 14 3.4.3 SELECT ... WHERE ... IN (SELECT ...) ........... 15 4 Заключение 16 1

Postgresql Performance

Embed Size (px)

Citation preview

Page 1: Postgresql Performance

PostgreSQL: настройка производительности

Алексей Борзов (Sad Spirit)[email protected]

Содержание1 Введение 2

1.1 Не используйте настройки по умолчанию . . . . . . . . . . . . . . 21.2 Используйте актуальную версию сервера . . . . . . . . . . . . . . 31.3 Стоит ли доверять тестам производительности . . . . . . . . . . . 3

2 Настройка сервера 32.1 Используемая память . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.1.1 Общий буфер сервера: shared_buffers . . . . . . . . . . . 42.1.2 Память для сортировки результата запроса: sort_mem . . 42.1.3 Память для работы команды VACUUM: vacuum_mem . . . 5

2.2 Журнал транзакций и контрольные точки . . . . . . . . . . . . . 52.2.1 fsync и стоит ли его трогать . . . . . . . . . . . . . . . . . 52.2.2 Уменьшение количества контрольных точек . . . . . . . . 62.2.3 Прочие параметры . . . . . . . . . . . . . . . . . . . . . . . 6

2.3 Free Space Map: как избавиться от VACUUM FULL . . . . . . . . 62.4 Прочие настройки . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.4.1 Оценка объёма кэша ОС: effective_cache_size . . . . . . 72.4.2 Сбор статистики . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.5 Диски и файловые системы . . . . . . . . . . . . . . . . . . . . . . 82.5.1 Перенос журнала транзакций на отдельный диск . . . . . 8

3 Оптимизация БД и приложения 93.1 Поддержание базы в порядке . . . . . . . . . . . . . . . . . . . . . 9

3.1.1 Команда VACUUM . . . . . . . . . . . . . . . . . . . . . . . 93.1.2 Команда ANALYZE . . . . . . . . . . . . . . . . . . . . . . . 103.1.3 pg_autovacuum . . . . . . . . . . . . . . . . . . . . . . . . . . 103.1.4 Команда REINDEX . . . . . . . . . . . . . . . . . . . . . . . 10

3.2 Использование индексов . . . . . . . . . . . . . . . . . . . . . . . . 113.2.1 Команда EXPLAIN [ANALYZE] . . . . . . . . . . . . . . . . 113.2.2 Использование собранной статистики . . . . . . . . . . . . 123.2.3 Возможности индексов в PostgreSQL . . . . . . . . . . . . . 12

3.3 Перенос логики на сторону сервера . . . . . . . . . . . . . . . . . . 133.4 Оптимизация конкретных запросов . . . . . . . . . . . . . . . . . . 14

3.4.1 SELECT max(...)/min(...) FROM <огромная таблица> . . 143.4.2 SELECT count(*) FROM <огромная таблица> . . . . . . . 143.4.3 SELECT ... WHERE ... IN (SELECT ...) . . . . . . . . . . . 15

4 Заключение 16

1

Page 2: Postgresql Performance

1 ВведениеСкорость работы, вообще говоря, не является основной причиной использова-ния реляционных СУБД. Более того, первые реляционные базы работали мед-леннее своих предшественников. Выбор этой технологии был вызван скорее

• возможностью возложить поддержку целостности данных на СУБД;

• независимостью логической структуры данных от физической.

Эти особенности позволяют сильно упростить написание приложений, но тре-буют для своей реализации дополнительных ресурсов.

Таким образом, прежде, чем искать ответ на вопрос «как заставить РСУБДработать быстрее в моей задаче?» следует ответить на вопрос «нет ли болееподходящего средства для решения моей задачи, чем РСУБД?» Иногда ис-пользование другого средства потребует меньше усилий, чем настройка произ-водительности.

Данная статья посвящена возможностям повышения производительностисвободной РСУБД PostgreSQL. Статья не претендует на исчерпывающее из-ложение вопроса, наиболее полным и точным руководством по использова-нию PostgreSQL является, конечно, официальная документация [1] и офици-альный FAQ [2]. Также существует англоязычный список рассылки postgresql-performance, посвящённый именно этим вопросам.

Статья состоит из двух разделов, первый из которых ориентирован ско-рее на администратора, второй — на разработчика приложений. Рекомендуетсяпрочесть оба раздела: отнесение многих вопросов к какому-то одному из нихвесьма условно. Большая часть раздела, посвящённого настройке сервера, яв-ляется переводом материалов [3], [4], [5]. В разделе, посвящённом оптимизацииБД и приложения, использовались [6], [7], [8] и личный опыт.

1.1 Не используйте настройки по умолчаниюПо умолчанию PostgreSQL сконфигурирован таким образом, чтобы он мог бытьзапущен практически на любом компьютере и не слишком мешал при этомработе других приложений. Это особенно касается используемой памяти.

Настройки по умолчанию подходят только для следующего использования:с ними вы сможете проверить, работает ли установка PostgreSQL, создать те-стовую базу уровня записной книжки и потренироваться писать к ней запросы.Если вы собираетесь разрабатывать (а тем более запускать в работу) реальныеприложения, то настройки придётся радикально изменить.

В дистрибутиве PostgreSQL, к сожалению, не поставляется файлов с «реко-мендуемыми» настройками. Вообще говоря, такие файлы создать весьма слож-но, т.к. оптимальные настройки конкретной установки PostgreSQL будут опре-деляться:

• конфигурацией компьютера;

• объёмом и типом данных, хранящихся в базе;

• отношением числа запросов на чтение и на запись;

• тем, запущены ли другие требовательные к ресурсам процессы (например,вебсервер).

2

Page 3: Postgresql Performance

1.2 Используйте актуальную версию сервераЕсли у вас стоит устаревшая версия PostgreSQL, то наибольшего ускоренияработы вы сможете добиться, обновив её до текущей. Укажем лишь наиболеезначительные из связанных с производительностью изменений.

• В версии 7.1 появился журнал транзакций, до того данные в таблицусбрасывались каждый раз при успешном завершении транзакции.

• В версии 7.2 появились:

– новая версия команды VACUUM, не требующая блокировки;– команда ANALYZE, строящая гистограмму распределения данных в

столбцах, что позволяет выбирать более быстрые планы выполнениязапросов;

– подсистема сбора статистики.

• В версии 7.4 была ускорена работа многих сложных запросов (включаяпечально известные подзапросы IN/NOT IN).

Следует также отметить, что большая часть изложенного в статье материалаотносится к версии сервера не ниже 7.2.

1.3 Стоит ли доверять тестам производительностиПеред тем, как заниматься настройкой сервера, вполне естественно ознако-миться с опубликованными данными по производительности, в том числе всравнении с другими СУБД. К сожалению, многие тесты служат не столькодля облегчения вашего выбора, сколько для продвижения конкретных продук-тов в качестве «самых быстрых».

При изучении опубликованных тестов в первую очередь обратите внима-ние, соответствует ли величина и тип нагрузки, объём данных и сложностьзапросов в тесте тому, что вы собираетесь делать с базой? Пусть, например,обычное использование вашего приложения подразумевает несколько одновре-менно работающих запросов на обновление к таблице в миллионы записей. Вэтом случае СУБД, которая в несколько раз быстрее всех остальных ищет за-пись в таблице в тысячу записей, может оказаться не лучшим выбором.

Ну и наконец, вещи, которые должны сразу насторожить:

• Тестирование устаревшей версии СУБД.

• Использование настроек по умолчанию (или отсутствие информации онастройках).

• Тестирование в однопользовательском режиме (если, конечно, вы не пред-полагаете использовать СУБД именно так).

• Использование расширенных возможностей одной СУБД при игнориро-вании расширенных возможностей другой.

• Использование заведомо медленно работающих запросов (см. пункт 3.4).

2 Настройка сервераВ этом разделе описаны рекомендуемые значения параметров, влияющих напроизводительность СУБД. Эти параметры обычно устанавливаются в конфи-гурационном файле postgresql.conf и влияют на все базы в текущей установ-ке.

3

Page 4: Postgresql Performance

2.1 Используемая память2.1.1 Общий буфер сервера: shared_buffers

PostgreSQL не читает данные напрямую с диска и не пишет их сразу на диск.Данные загружаются в общий буфер сервера, находящийся в разделяемой па-мяти, серверные процессы читают и пишут блоки в этом буфере, а затем ужеизменения сбрасываются на диск.

Если процессу нужен доступ к таблице, то он сначала ищет нужные блоки вобщем буфере. Если блоки присутствуют, то он может продолжать работу, еслинет — делается системный вызов для их загрузки. Загружаться блоки могуткак из файлового кэша ОС, так и с диска, и эта операция может оказатьсявесьма «дорогой».

Если объём буфера недостаточен для хранения часто используемых рабочихданных, то они будут постоянно писаться и читаться из кэша ОС или с диска,что крайне отрицательно скажется на производительности.

Объём задаётся параметром shared_buffers в файле postgresql.conf.Единица измерения параметра — блоки величиной 8 кБ. По умолчанию зна-чение параметра составляет 641, что соответствует 512 кБ памяти. Это весьмамало, и для полноценной работы значение параметра следует увеличить.

В то же время не следует устанавливать это значение слишком большим:PostgreSQL полагается на то, что операционная система кэширует файлы (см.пункт 2.4.1), и не старается дублировать эту работу. Кроме того, чем большепамяти будет отдано под буфер, тем меньше останется операционной системеи другим приложениям, что может привести к своппингу.

В качестве начальных значений можете попробовать следующие:

• Начните с 4 МБ (512) для рабочей станции

• Средний объём данных и 256–512 МБ доступной памяти: 16–32 МБ (2048–4096)

• Большой объём данных и 1–4 ГБ доступной памяти: 64–256 МБ (8192–32768)

Для тонкой настройки параметра установите для него большое значение и поте-стируйте базу при обычной нагрузке. Проверяйте использование разделяемойпамяти при помощи ipcs или других утилит. Рекомендуемое значение парамет-ра будет примерно в 1,2–2 раза больше, чем максимум использованной памяти.

Обратите внимание, что память под буфер выделятся при запуске сервера, иеё объём при работе не изменяется. Учтите также, что настройки ядра операци-онной системы могут не дать вам выделить большой объём памяти. В руковод-стве администратора PostgreSQL описано, как можно изменить эти настройки:http://developer.postgresql.org/docs/postgres/kernel-resources.html

2.1.2 Память для сортировки результата запроса: sort_mem

Этот параметр определяет объём памяти, которую процесс может использо-вать для сортировки результата запроса. Учтите, что такой объём может бытьиспользован каждым процессом для каждой сортировки (в сложных запросахих может быть несколько).

Если объём памяти недостаточен для сортироки некоторого результата, тосерверный процесс будет использовать временные файлы. Если же объём па-мяти слишком велик, то это может привести к своппингу.

1актуально для версий до 7.4

4

Page 5: Postgresql Performance

Объём памяти задаётся параметром sort_mem в файле postgresql.conf.Единица измерения параметра — 1 кБ. Значение по умолчанию — 1024.

В качестве начального значения для параметра можете взять 2–4% доступ-ной памяти.

Этот параметр может также быть задан для отдельного соединения. Есливы знаете, что в конкретном соединении будет выполняться запрос, требую-щий сортировки значительного объёма данных, то можете поднять значениеsort_mem перед выполнением запроса.

2.1.3 Память для работы команды VACUUM: vacuum_mem

Этот параметр задаёт объём памяти, используемый командой VACUUM.Обычно эта команда больше нагружает диски, но увеличение vacuum_mem поз-волит ускорить процесс за счёт хранения в памяти больших объёмов инфор-мации об удалённых записях.

Объём памяти задаётся параметром vacuum_mem в файле postgresql.conf.Единица измерения параметра — 1 кБ. Значение по умолчанию — 8192.

Этот параметр может также быть задан для отдельного соединения. Можетесделать его поменьше для частых регулярных запусков VACUUM и большимдля ежедневных/еженедельных запусков VACUUM FULL.

2.2 Журнал транзакций и контрольные точкиЖурнал транзакций PostgreSQL работает следующим образом: все изменения вфайлах данных (в которых находятся таблицы и индексы) производятся толькопосле того, как они были занесены в журнал транзакций, при этом записи вжурнале должны быть гарантированно записаны на диск.

В этом случае нет необходимости сбрасывать на диск изменения данныхпри каждом успешном завершении транзакции: в случае сбоя БД может бытьвосстановлена по записям в журнале. Таким образом, данные из буферовсбрасываются на диск при проходе контрольной точки: либо при заполне-нии нескольких (параметр checkpoint_segments, по умолчанию 3) сегментовжурнала транзакций, либо через определённый интервал времени (параметрcheckpoint_timeout, измеряется в секундах, по умолчанию 300).

Изменение этих параметров прямо не повлияет на скорость чтения, но мо-жет принести большую пользу, если данные в базе активно изменяются.

2.2.1 fsync и стоит ли его трогать

Наиболее радикальное из возможных решений — выставить значение No пара-метру fsync. При этом записи в журнале транзакций не будут принудительносбрасываться на диск, что даст большой прирост скорости записи. Учтите: выжертвуете надёжностью, в случае сбоя целостность базы будет нарушена, и еёпридётся восстанавливать из резервной копии!

Использовать этот параметр рекомендуется лишь в том случае, если вы все-цело доверяете своему «железу» и своему источнику бесперебойного питания.Ну или если данные в базе не представляют для вас особой ценности. . .

В пункте 2.5.1 описано менее радикальное решение, позволяющее, тем неменее, добиться хорошего прироста производительности.

5

Page 6: Postgresql Performance

2.2.2 Уменьшение количества контрольных точек

Если в базу заносятся большие объёмы данных, то контрольные точки могутпроисходить слишком часто2. При этом производительность упадёт из-за по-стоянного сбрасывания на диск данных из буфера.

Для увеличения интервала между контрольными точками нужно увеличитьколичество сегментов журнала транзакций (checkpoint_segments). Каждыйсегмент занимает 16 МБ, так что на диске будет занято дополнительное место.Обычно на диске будет не менее одного и не более 2*checkpoint_segments+1сегментов журнала.

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

2.2.3 Прочие параметры

wal_sync_method определяет метод, при помощи которого записи в журна-ле транзакций принудительно сбрасываются на диск. Значение по умол-чанию зависит от платформы. Возможно, изменение этого параметра поз-волит увеличить производительность (а возможно, и не позволит).

wal_buffers (в блоках по 8 кБ, 8 по умолчанию) определяет размер буферажурнала транзакций3, в котором накапливаются записи перед сбросомих на диск. Стоит увеличить буфер до 256–512 кБ, что позволит лучшеработать с большими транзакциями.

commit_delay (в микросекундах, 0 по умолчанию) и

commit_siblings (5 по умолчанию) определяют задержку между попадани-ем записи в буфер журнала транзакций и сбросом её на диск. Еслипри успешном завершении транзакции активно не менее commit_siblingsтранзакций, то запись будет задержана на время commit_delay. Если заэто время завершится другая транзакция, то их изменения будут сбро-шены на диск вместе, при помощи одного системного вызова. Эти пара-метры позволят ускорить работу, если параллельно выполняется много«мелких» транзакций.

2.3 Free Space Map: как избавиться от VACUUM FULLОсобенностями версионных движков БД (к которым относится и используемыйв PostgreSQL) является следующее:

• Транзакции, изменяющие данные в таблице, не блокируют транзакции,читающие из неё данные, и наоборот (это хорошо);

• При изменении данных в таблице (командами UPDATE или DELETE)накапливается мусор4 (а это плохо).

В каждой СУБД сборка мусора реализована особым образом, в PostgreSQL дляэтой цели применяется команда VACUUM (описана в пункте 3.1.1).

2«слишком часто» можно определить как «чаще раза в минуту». Вы также можете задатьпараметр checkpoint_warning (в секундах): в журнал сервера будут писаться предупрежде-ния, если контрольные точки происходят чаще заданного.

3буфер находится в разделяемой памяти и является общим для всех процессов4под которым понимаются старые версии изменённых/удалённых записей

6

Page 7: Postgresql Performance

До версии 7.2 команда VACUUM полностью блокировала таблицу. Начинаяс версии 7.2, команда VACUUM накладывает более слабую блокировку, поз-воляющую параллельно выполнять команды SELECT, INSERT, UPDATE иDELETE над обрабатываемой таблицей. Старый вариант команды называетсятеперь VACUUM FULL.

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

max_fsm_relations максимальное количество таблиц, для которых будет от-слеживаться свободное место.

max_fsm_pages количество блоков, для которых будет хранится информа-ция о свободном месте. Информация хранится в разделяемой памяти, длякаждой записи требуется по 6 байт.

Если информация обо всех изменениях помещается в FSM, то командыVACUUM будет достаточно для сборки мусора, если нет — понадобитсяVACUUM FULL, во время работы которой нормальное использование БД силь-но затруднено.

Параметр max_fsm_relations должен быть не меньше общего количестватаблиц во всех базах данной установки. В качестве начального приближениядля max_fsm_pages можно взять половину от среднего количества записей, из-меняемых (UPDATE или DELETE) между запусками команды VACUUM.

2.4 Прочие настройки2.4.1 Оценка объёма кэша ОС: effective_cache_size

Этот параметр сообщает PostgreSQL примерный объём файлового кэша опе-рационной системы, оптимизатор использует эту оценку для построения планазапроса.

Объём задаётся параметром effective_cache_size в postgresql.conf.Единица измерения — блоки величиной 8 кБ. По умолчанию значение пара-метра составляет 1000.

Пусть в вашем компьютере 1,5 ГБ памяти, параметр shared_buffers уста-новлен в 32 МБ, а параметр effective_cache_size в 800 МБ. Если запросунужно 700 МБ данных, то PostgreSQL оценит, что все нужные данные ужеесть в памяти и выберет более агрессивный план с использованием индексови merge joins. Но если effective_cache_size будет всего 200 МБ, то оптими-затор вполне может выбрать более эффективный для дисковой системы план,включающий полный просмотр таблицы.

В качестве начального значения можете использовать 25–50% доступной5

памяти.

2.4.2 Сбор статистики

default_statistics_target задаёт объём по умолчанию статистики, собира-емой командой ANALYZE (см. пункт 3.1.2). Увеличение параметра за-ставит эту команду работать дольше, но может позволить оптимизато-ру строить более быстрые планы, используя полученные дополнительныеданные. Объём статистики для конкретного поля может быть задан ко-мандой ALTER TABLE . . . SET STATISTICS.

5т.е. не занятой операционной системой и приложениями

7

Page 8: Postgresql Performance

У PostgreSQL также есть специальная подсистема — сборщик статистики, —которая в реальном времени собирает данные об активности сервера. Эта под-система контролируется следующими параметрами, принимающими значенияtrue/false:

stats_start_collector включать ли сбор статистики. По умолчанию вклю-чён, отключайте, только если статистика вас совершенно не интересует.

stats_reset_on_server_start обнулять ли статистику при перезапуске сер-вера. По умолчанию — обнулять.

stats_command_string передавать ли сборщику статистики информациюо текущей выполняемой команде и времени начала её выполнения. Поумолчанию эта возможность отключена. Следует отметить, что эта ин-формация будет доступна только привилегированным пользователям ипользователям, от лица которых запущены команды, так что проблем сбезопасностью быть не должно.

stats_row_level, stats_block_level собирать ли информацию об активно-сти на уровне записей и блоков соответственно. По умолчанию сбор от-ключён.

Данные, полученные сборщиком статистики, доступны через специальные си-стемные представления. При установках по умолчанию собирается очень малоинформации, рекомендуется включить все возможности: дополнительная на-грузка будет невелика, в то время как полученные данные позволят оптимизи-ровать использование индексов (см. пункт 3.2.2).

2.5 Диски и файловые системыОчевидно, что от качественной дисковой подсистемы в сервере БД зависитнемалая часть производительности. Вопросы выбора и тонкой настройки «же-леза», впрочем, не являются темой данной статьи, ограничимся уровнем фай-ловой системы.

Единого мнения насчёт наиболее подходящей для PostgreSQL файловойсистемы нет, поэтому рекомендуется использовать ту, которая лучше все-го поддерживается вашей операционной системой. При этом учтите, что со-временные журналирующие файловые системы не намного медленнее не-журналирующих, а выигрыш — быстрое восстановление после сбоев — от ихиспользования велик.

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

2.5.1 Перенос журнала транзакций на отдельный диск

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

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

6при этом не будет отслеживаться время последнего доступа к файлу7несколько логических разделов на одном диске здесь, очевидно, не помогут: головка всё

равно будет одна

8

Page 9: Postgresql Performance

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

Порядок действий:

• Остановите сервер (!).

• Перенесите каталог pg_xlog, находящийся в каталоге с базами данных,на другой диск.

• Создайте на старом месте символическую ссылку.

• Запустите сервер.

Примерно таким же образом можно перенести и часть файлов, содержащихтаблицы и индексы, на другой диск, но здесь потребуется больше кропотливойручной работы, а при внесении изменений в схему базы процедуру, возможно,придётся повторить.

3 Оптимизация БД и приложенияДля быстрой работы каждого запроса в вашей базе в основном требуется сле-дующее:

1. Отсутствие в базе мусора, мешающего добраться до актуальных данных.Можно сформулировать две подзадачи:

• Грамотное проектирование базы. Освещение этого вопроса выходитдалеко за рамки этой статьи.

• Сборка мусора, возникающего при работе СУБД.

2. Наличие быстрых путей доступа к данным — индексов.

3. Возможность использования оптимизатором этих быстрых путей.

4. Обход известных проблем.

3.1 Поддержание базы в порядкеВ данном разделе описаны действия, которые должны периодически выпол-няться для каждой базы. От разработчика требуется только настроить их ав-томатическое выполнение (при помощи cron) и опытным путём подобрать егооптимальную частоту.

3.1.1 Команда VACUUM

Используется для «сборки мусора» в базе данных. Начиная с версии 7.2, суще-ствует в двух вариантах:

• VACUUM FULL (VACUUM до 7.2) пытается удалить все старые версии за-писей и, соответственно, уменьшить размер файла, содержащего таблицу.Этот вариант команды полностью блокирует обрабатываемую таблицу.

• VACUUM (начиная с 7.2) помечает место, занимаемое старыми версиямизаписей, как свободное (см. также пункт 2.3). Использование этого вари-анта команды, как правило, не уменьшает размер файла, содержащеготаблицу, но позволяет не дать ему бесконтрольно расти, зафиксировав нанекотором приемлемом уровне. При работе VACUUM возможен парал-лельный доступ к обрабатываемой таблице.

9

Page 10: Postgresql Performance

При использовании в форме VACUUM [FULL] ANALYZE, после сборки мусорабудет обновлена статистика по данной таблице, используемая оптимизатором.В абсолютном большинстве случаев имеет смысл использовать именно эту фор-му.

Рекомендуется достаточно частое — в [7] и [8], например, раз в несколь-ко минут (!) — выполнение VACUUM ANALYZE для часто обновляемых баз(или отдельных таблиц). В обыкновенных случаях достаточно ежедневного8

выполнения этой команды. При этом обратите внимание: если «бутылочноегорлышко» вашего сервера находится в районе дисковой подсистемы, то выпол-нение VACUUM параллельно с обычной работой может крайне отрицательносказаться на производительности.

Команду VACUUM FULL стоит запускать достаточно редко, не чаще разав неделю. Её также имеет смысл запускать вручную для конкретной таблицыпосле удаления или обновления большой части записей в ней.

3.1.2 Команда ANALYZE

Служит для обновления информации о распределении данных в таблице. Этаинформация используется оптимизатором для выбора наиболее быстрого планавыполнения запроса.

Обычно команда используется в связке VACUUM ANALYZE. Если в ба-зе есть таблицы, данные в которых не изменяются и не удаляются, а лишьдобавляются, то для таких таблиц можно использовать отдельную командуANALYZE. Также стоит использовать эту команду для отдельной таблицы по-сле добавления в неё большого количества записей.

3.1.3 pg_autovacuum

Начиная с версии 7.4, в дистрибутиве PostgreSQL поставляется программаpg_autovacuum, которая отслеживает изменения в таблицах и автоматическизапускает команды VACUUM и/или ANALYZE для этих таблиц по достиженииопределённого предела.

Использование этой программы позволяет отказаться от настройки пери-одического выполнения команд VACUUM и ANALYZE. Более того, в случаеиспользования pg_autovacuum ресурсы не тратятся впустую на обработку та-блиц, которые практически не подвергались изменениям.

Для работы pg_autovacuum должен быть включён сборщик статистики (см.пункт 2.4.2) и включён параметр stats_row_level.

3.1.4 Команда REINDEX

Команда REINDEX используется для перестройки существующих индексов.Использовать её имеет смысл в случае

• порчи индекса;

• постоянного увеличения его размера.

Второй случай требует пояснений. Индекс, как и таблица, содержит блоки состарыми версиями записей. PostgreSQL не всегда может заново использоватьэти блоки9, и поэтому файл с индексом постепенно увеличивается в размерах.Если данные в таблице часто меняются, то расти он может весьма быстро.

8точнее еженощного, т.к. по ночам нагрузка на базу меньше9в версии 7.4 были сделаны существенные улучшения

10

Page 11: Postgresql Performance

Если вы заметили подобное поведение какого-то индекса, то стоит настро-ить для него периодическое выполнение команды REINDEX. Учтите: командаREINDEX, как и VACUUM FULL, полностью блокирует таблицу, поэтому вы-полнять её надо тогда, когда загрузка сервера минимальна.

3.2 Использование индексовОпыт показывает, что наиболее значительные проблемы с производительно-стью вызываются отсутствием нужных индексов. Поэтому столкнувшись с мед-ленным запросом, в первую очередь проверьте, существуют ли индексы, кото-рые он может использовать. Если нет — постройте их.

Излишек индексов, впрочем, тоже чреват проблемами:

• Команды, изменяющие данные в таблице, должны изменить также и ин-дексы. Очевидно, чем больше индексов построено для таблицы, тем мед-леннее это будет происходить.

• Оптимизатор перебирает возможные пути выполнения запросов. Если по-строено много ненужных индексов, то этот перебор будет идти дольше.

Единственное, что можно сказать с большой степенью определённости — поля,являющиеся внешими ключами, и поля, по которым объединяются таблицы,индексировать надо обязательно.

3.2.1 Команда EXPLAIN [ANALYZE]

Команда EXPLAIN [запрос] показывает, каким образом PostgreSQL собирает-ся выполнять ваш запрос. Команда EXPLAIN ANALYZE [запрос] выполняетзапрос10 и показывает как изначальный план, так и реальный процесс его вы-полнения.

Чтение вывода этих команд — искусство, которое приходит с опытом. Дляначала обращайте внимание на следующее:

• Использование полного просмотра таблицы (seq scan).

• Использование наиболее примитивного способа объединения таблиц(nested loop).

• Для EXPLAIN ANALYZE: нет ли больших отличий в предполагаемомколичестве записей и реально выбранном? Если оптимизатор используетустаревшую статистику, то он может выбирать не самый быстрый планвыполнения запроса.

Следует отметить, что полный просмотр таблицы далеко не всегда медлен-нее просмотра по индексу. Если, например, в таблице-справочнике несколькосотен записей, умещающихся в одном-двух блоках на диске, то использованиеиндекса приведёт лишь к тому, что придётся читать ещё и пару лишних блоковиндекса. Если в запросе придётся выбрать 80% записей из большой таблицы,то полный просмотр опять же получится быстрее.

При тестировании запросов с использованием EXPLAIN ANALYZE мож-но воспользоваться настройками, запрещающими оптимизатору использоватьопределённые планы выполнения. Например,

SET enable_seqscan=false;

10и поэтому EXPLAIN ANALYZE DELETE ... — не слишком хорошая идея

11

Page 12: Postgresql Performance

запретит использование полного просмотра таблицы, и вы сможете выяснить,прав ли был оптимизатор, отказываясь от использования индекса. Ни в коемслучае не следует прописывать подобные команды в postgresql.conf! Это мо-жет ускорить выполнение нескольких запросов, но сильно замедлит все осталь-ные!

3.2.2 Использование собранной статистики

Результаты работы сборщика статистики (см. пункт 2.4.2) доступны через спе-циальные системные представления. Наиболее интересны для наших целей сле-дующие:

pg_stat_user_tables содержит — для каждой пользовательской таблицы втекущей базе данных — общее количество полных просмотров и просмот-ров с использованием индексов, общие количества записей, которые быливозвращены в результате обоих типов просмотра, а также общие количе-ства вставленных, изменённых и удалённых записей.

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

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

Из этих представлений можно узнать, в частности

• Для каких таблиц стоит создать новые индексы (индикатором служитбольшое количество полных просмотров и большое количество прочитан-ных блоков).

• Какие индексы вообще не используются в запросах. Их имеет смысл уда-лить, если, конечно, речь не идёт об индексах, обеспечивающих выполне-ние ограничений PRIMARY KEY и UNIQUE.

• Достаточен ли объём буфера сервера.

Также возможен «дедуктивный» подход, при котором сначала создаётся боль-шое количество индексов, а затем неиспользуемые индексы удаляются.

3.2.3 Возможности индексов в PostgreSQL

Функциональные индексы Вы можете построить индекс не только по по-лю/нескольким полям таблицы, но и по выражению, зависящему от полей.Пусть, например, в вашей таблице foo есть поле foo_name, и выборки частоделаются по условию «первая буква foo_name = ’буква’, в любом регистре».Вы можете создать индекс

CREATE INDEX foo_name_first_idxON foo ((lower(substr(foo_name, 1, 1))));

12

Page 13: Postgresql Performance

и запрос вида

SELECT * FROM fooWHERE lower(substr(foo_name, 1, 1)) = ’ы’;

будет его использовать.Следует отметить, что возможности задания подобных индексов были зна-

чительно расширены в версии 7.4, и приведённый пример может потребоватьдоработки, чтобы быть запущенным на более старой версии.

Частичные индексы (partial indexes) Под частичным индексом понима-ется индекс с предикатом WHERE. Пусть, например, у вас есть в базе таблицаscheta с параметром uplocheno типа boolean. Записей, где uplocheno = falseменьше, чем записей с uplocheno = true, а запросы по ним выполняются зна-чительно чаще. Вы можете создать индекс

CREATE INDEX scheta_neuplocheno ON scheta (id)WHERE NOT uplocheno;

который будет использоваться запросом вида

SELECT * FROM scheta WHERE NOT uplocheno AND ...;

Достоинство подхода в том, что записи, не удовлетворяющие условию WHERE,просто не попадут в индекс.

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

Наиболее продвинутым из имеющихся средств является tsearch2 http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/ Он поставляется в дис-трибутиве PostgreSQL версии 7.4 в каталоге contrib/tsearch2, вариант дляверсии 7.3 можно скачать на указанном сайте.

За полным описанием возможностей tsearch2 обратитесь к поставляемой сним документации.

3.3 Перенос логики на сторону сервераЭтот пункт очевиден для опытных пользователей PostrgeSQL и предназначендля тех, кто использует или переносит на PostgreSQL приложения, написанныеизначально для более примитивных СУБД.

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

• пересылка промежуточных запросов на сервер;

• получение промежуточных результатов на клиент и их обработка.

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

11RULE — реализованное в PostgreSQL расширение стандарта SQL, позволяющее, в част-ности, создавать обновляемые представления

13

Page 14: Postgresql Performance

3.4 Оптимизация конкретных запросовВ этом разделе описываются запросы, для которых по разным причинам нельзязаставить оптимизатор использовать индексы, и которые будут всегда вызы-вать полный просмотр таблицы. Таким образом, если вам требуется использо-вать эти запросы в требовательном к быстродействию приложении, то придётсяих изменить.

3.4.1 SELECT max(...)/min(...) FROM <огромная таблица>

Все агрегатные функции в PostgreSQL реализованы одинаково: сначала выби-раются все записи, удовлетворяющие условию, а потом к полученному наборузаписей применяется агрегатная функция. У такого подхода есть достоинства— вы можете легко написать собственную агрегатную функцию — но есть инедостаток, который заключается в том, что для работы функций типа min()/ max() весь набор записей совершенно не нужен.

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

Проблема Запрос вида

SELECT max(field) FROM foo;

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

Решение Запрос вида

SELECT field FROM foo ORDER BY field DESC LIMIT 1;

вернёт то же самое значение12, но при этом сможет использовать индекс поfield, если таковой существует.

3.4.2 SELECT count(*) FROM <огромная таблица>

К функции count() относится всё вышесказанное по поводу реализации агрегат-ных функций в PostgreSQL. Кроме того, информация о видимости записи длятекущей транзакции (а конкурентным транзакциям может быть видимо разноеколичество записей в таблице!) не хранится в индексе. Таким образом, даже ес-ли использовать для выполнения запроса индекс первичного ключа таблицы,всё равно потребуется чтение записей собственно из файла таблицы.

Проблема Запрос вида

SELECT count(*) FROM foo;

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

12на самом деле почти то же самое: отличие будет в случае, если в таблице нет записей

14

Page 15: Postgresql Performance

Решение Простого решения проблемы, к сожалению, нет. Возможны следу-ющие подходы:

1. Если точное число записей не важно, а важен порядок13, то можно ис-пользовать информацию о количестве записей в таблице, собранную привыполнении команды ANALYZE:

SELECT reltuples FROM pg_class WHERE relname = ’foo’;

2. Если подобные выборки выполняются часто, а изменения в таблице до-статочно редки, то можно завести вспомогательную таблицу, хранящуючисло записей в основной. На основную же таблицу повесить триггер,который будет уменьшать это число в случае удаления записи и увеличи-вать в случае вставки. Таким образом, для получения количества записейпотребуется лишь выбрать одну запись из вспомогательной таблицы.

3. Вариант предыдущего подхода, но данные во вспомогательной таблицеобновляются через определённые промежутки времени (cron).

3.4.3 SELECT ... WHERE ... IN (SELECT ...)

Сразу отметим, что в версии 7.4 в обработку подзапросов с IN / NOT IN бы-ли внесены изменения, и теперь они работают (как минимум) не медленнее,чем подзапросы с EXISTS / NOT EXISTS. Если вы по каким-то причинам неможете обновить версию сервера до 7.4, то читайте дальше.

Проблема При использовании подзапроса вида

SELECT ...FROM fooWHERE foo_field IN (

SELECT bar_fieldFROM bar...

);

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

Решение Перепишите подзапрос с использованием конструкции EXISTS:

SELECT ...FROM fooWHERE EXISTS (

SELECT bar_fieldFROM barWHERE bar.bar_field = foo.foo_field...

);

Аналогично можно переписать подзапрос с NOT IN, используя конструкциюNOT EXISTS.

13«на нашем форуме более 10000 зарегистрированных пользователей, оставивших более50000 сообщений!»

15

Page 16: Postgresql Performance

4 ЗаключениеК счастью, PostgreSQL не требует особо сложной настройки. В большинствеслучаев вполне достаточно будет увеличить объём выделенной памяти, настро-ить периодическое поддержание базы в порядке и проверить наличие необходи-мых индексов. Более сложные вопросы можно обсудить в специализированномсписке рассылки.

Благодарю разработчиков PostgreSQL за создание и поддержку замечатель-ной свободной РСУБД, разработчиков редактора LYX, в котором была написа-на эта статья, Александра Смирнова (PHPClub) за побуждение к её написанию,Елену Теслю за редакторскую работу.

Текущая версия статьи доступна в интернете по адресу [вставить адрес].Замечания и исправления просьба направлять на email автору.

Список литературы[1] PostgreSQL documentation, http://www.postgresql.org/docs/

[2] Momjian B., et. al. PostgreSQL FAQ, http://www.postgresql.org/docs/faqs/FAQ.html

[3] Momjian B. PostgreSQL Hardware Performance Tuning, http://www.ca.postgresql.org/docs/momjian/hw_performance/

[4] Berkus J., Daithankar S. Tuning PostgreSQL for performance, http://www.varlena.com/varlena/GeneralBits/Tidbits/perf.html

[5] Berkus J. Annotated postgresql.conf and Global User Configuration(GUC) Guide, http://www.varlena.com/varlena/GeneralBits/Tidbits/annotated_conf_e.html

[6] Berkus J. Adventures in PostgreSQL Episode 2: The Joy of Index, http://techdocs.postgresql.org/techdocs/pgsqladventuresep2.php

[7] Trout J. PostgreSQL: Features, Installation, Administration and Optimization,http://postgres.jefftrout.com/

[8] Argudo J.-P. PostgreSQL Database Performance Tuning, http://www.argudo.org/postgresql/soft-tuning.html

16