Перейти к содержимому

Huge pages что это

  • автор:

Преимущества и недостатки HugePages

Ранее я рассказал о том, как проверить и включить использование Hugepages в Linux.
Эта статья будет полезна, только если у вас действительно есть, где использовать Hugepages. Я встречал множество людей, которые обманываются перспективой того, что Hugepages волшебным образом повысят производительность. Тем не менее hugepaging является сложной темой, и при неправильном использовании он способен понизить производительность.

Часть 1: проверяем, что hugepages включены в Linux (оригинал здесь)

Проблема:
Необходимо проверить, включены ли HugePages в вашей системе.

Решение:
Оно довольно простое:

Вы получите что-то вроде этого:

Вы увидите список доступных опций (always, madvise, never), при этом текущая активная опция будет заключена в скобки (по умолчанию madvise).

madvise означает, что transparent hugepages включены только для областей памяти, которые явно запрашивают hugepages с помощью madvise(2).

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

never означает, что transparent hugepages не будут включаться даже при запросе с помощью madvise. Чтобы узнать больше, обратитесь к документации ядра Linux.

Как изменить значение по умолчанию

Вариант 1: Напрямую изменить sysfs (после перезагрузки параметр вернется к значению по умолчанию):

Вариант 2: Измените системное значение по умолчанию, перекомпилировав ядро с измененной конфигурацией (этот вариант рекомендуется только если вы используете собственное ядро):

  • Чтобы поставить always по умолчанию, используйте:
  • Чтобы поставить madvise по умолчанию, используйте:

Часть 2: Преимущества и недостатки HugePages

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

Обратите внимание, что мы говорим о 64-х разрядных x86 системах, работающих на Linux, и что я просто предполагаю, что система поддерживает transparent hugepages (так как не является недостатком то, что hugepages не подменяются), как это случается практически в любой современной среде Linux.

В ссылках ниже я прикреплю больше технического описания.

Виртуальная память

Если вы программист C++, вы знаете, что у объектов в памяти есть конкретные адреса (значения указателя).

Однако эти адреса необязательно отражают физические адреса в памяти (адреса в ОЗУ). Они представляют собой адреса в виртуальной памяти. Процессор имеет специальный модуль MMU (memory management unit), который помогает ядру сопоставлять виртуальную память с физическим местоположением.

Такой подход имеет множество преимуществ, но самые основные из них:

  • Производительность (по различным причинам);
  • Изоляция программ, то есть ни одна из программ не может читать из памяти другой программы.
Что такое страницы?

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

Большинство страниц, с которыми вы имеете дело, указывают либо на ОЗУ, либо подменяются (swap), то есть хранятся на жестком диске или SSD. Ядро управляет физическим расположением каждой страницы. Если осуществляется доступ к подмененной странице, ядро останавливает поток, который пытается получить доступ к памяти, считывает страницу с жесткого диска/SSD в оперативную память, а затем продолжает выполнение потока.

Этот процесс прозрачен для потока, то есть он не обязательно читает напрямую с жесткого диска/SSD. Размер нормальных страниц – 4096 байт. Размер Hugepages – 2 мегабайта.

Буфер ассоциативной трансляции (TLB)

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

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

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

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

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

Hugepages приходят на помощь

Итак, что мы можем сделать, чтобы избежать переполнения TLB? (Мы предполагаем, что программе все еще нужен тот же объем памяти).

Вот тут-то и появляются Hugepages. Вместо 4096 байт, требующих всего одну запись в TLB, одна запись в TLB теперь может указывать на колоссальные 2 мегабайта. Будем предполагать, что TLB имеет 512 записей, здесь без Hugepages мы можем сопоставить:

Тогда как с ними мы можем сопоставить:

Именно поэтому Hugepages – это круто. Они могут повысить производительность без значительного приложения усилий. Но здесь есть существенные оговорки.

Подмена Hugepages

Ядро автоматически отслеживает частоту использования каждой страницы памяти. Если физической памяти (ОЗУ) недостаточно, ядро переместит менее важные (реже используемые) страницы на жесткий диск, чтобы освободить часть ОЗУ для более важных страниц.
В принципе, то же самое касается и Hugepages. Однако ядро может менять местами только целые страницы, а не отдельные байты.

Предположим, у нас есть такая программа:

В этом случае ядру нужно будет подменить (прочитать) целых 2 мегабайта информации с жесткого диска/SSD только для того чтобы вы прочитали один байт. Что касается обычных страниц, с жесткого диска/SSD надо прочитать всего 4096 байт.

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

С другой стороны, если вам нужно получать доступ к большой части памяти последовательно, hugepages увеличат вашу производительность. Тем не менее, вам нужно проверить это самостоятельно (а не на примере абстрактного ПО) и посмотреть, что будет работать быстрее.

Аллокация в памяти

Если вы пишете на С, вы знаете, что вы можете запросить сколь угодно малые (или почти сколь угодно большие) объемы памяти из кучи с помощью malloc() . Допустим, вам нужно 30 байт памяти:

Программисту может показаться, что вы “запрашиваете” 30 байт памяти из операционной системы и возвращаете указатель на некоторую виртуальную память. Но на самом деле malloc () — это просто функция C, которая вызывает изнутри функции brk и sbrk для запроса или освобождения памяти из операционной системы.

Однако, запрашивать больше и больше памяти для каждой аллокации неэффективно; наиболее вероятно, что какой-либо сегмент памяти уже был освобожден (free()) , и мы можем повторно его использовать. malloc() реализует довольно сложные алгоритмы для повторного использования освобожденной памяти.

При этом для вас все происходит незаметно, так почему это должно вас волновать? А потому, что вызов free() не означает, что память обязательно возвращается сразу же операционной системе.

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

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

Выборочное применение hugepages

После прочтения статьи, вы определили, какие части вашей программы могут извлечь выгоду из применения hugepages, а какие – нет. Так следует ли вообще включать hugepages?

К счастью, вы можете использовать madvise() , чтобы включить hugepaging только для тех областей памяти, где это будет полезно.

Для начала, проверьте, что hugepages работают в режиме madvise(), с помощью инструкции в начале статьи.

Затем, используйте madvise() , чтобы указать ядру, где именно использовать hugepages.

Обратите внимание, что этот метод — просто рекомендации ядру по управлению памятью. Это не означает, что ядро будет автоматически использовать hugepages для заданной памяти.

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

Huge pages что это

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

19.4.1. Разделяемая память и семафоры

По умолчанию PostgreSQL запрашивает очень небольшой объём разделяемой памяти System V и намного больший объём анонимной разделяемой памяти mmap . Возможен также вариант использования одной большой области памяти System V (см. shared_memory_type). Помимо этого при запуске сервера создаётся значительное количество семафоров (в стиле System V или POSIX). В настоящее время семафоры POSIX используются в системах Linux и FreeBSD, а на других платформах используются семафоры System V.

Имя Описание Значения, необходимые для запуска одного экземпляра PostgreSQL
SHMMAX Максимальный размер сегмента разделяемой памяти (в байтах) как минимум 1 КБ, но значение по умолчанию обычно гораздо больше
SHMMIN Минимальный размер сегмента разделяемой памяти (в байтах) 1
SHMALL Общий объём доступной разделяемой памяти (в байтах или страницах) если в байтах, то же, что и SHMMAX ; если в страницах, то ceil(SHMMAX/PAGE_SIZE) , плюс потребность других приложений
SHMSEG Максимальное число сегментов разделяемой памяти для процесса требуется только 1 сегмент, но значение по умолчанию гораздо больше
SHMMNI Максимальное число сегментов разделяемой памяти для всей системы как SHMSEG плюс потребность других приложений
SEMMNI Максимальное число идентификаторов семафоров (т. е., их наборов) как минимум ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 5) / 16) плюс потребность других приложений
SEMMNS Максимальное число семафоров для всей системы ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 5) / 16) * 17 плюс потребность других приложений
SEMMSL Максимальное число семафоров в наборе не меньше 17
SEMMAP Число записей в карте семафоров см. текст
SEMVMX Максимальное значение семафора не меньше 1000 (по умолчанию оно обычно равно 32767; без необходимости менять его не следует)

PostgreSQL запрашивает небольшой блок разделяемой памяти System V (обычно 48 байт на 64-битной платформе) для каждой копии сервера. В большинстве современных операционных систем такой объём выделяется без проблем. Однако если запускать много копий сервера или явно настроить сервер для использования больших объёмов разделяемой памяти System V (см. shared_memory_type и dynamic_shared_memory_type), может понадобиться увеличить значение SHMALL , задающее общий объём разделяемой памяти System V, доступный для всей системы. Заметьте, что SHMALL во многих системах задаётся в страницах, а не в байтах.

Менее вероятны проблемы с минимальным размером сегментов разделяемой памяти ( SHMMIN ), который для PostgreSQL не должен превышать примерно 32 байт (обычно это всего 1 байт). Максимальное число сегментов для всей системы ( SHMMNI ) или для одного процесса ( SHMSEG ) тоже обычно не влияет на работоспособность сервера, если только это число не равно нулю.

Когда PostgreSQL использует семафоры System V, он занимает по одному семафору на одно разрешённое подключение (max_connections), на разрешённый рабочий процесс автоочистки (autovacuum_max_workers) и фоновый процесс (max_worker_processes), в наборах по 16. В каждом таком наборе есть также 17-ый семафор, содержащий « магическое число » , позволяющий обнаруживать коллизии с наборами семафоров других приложений. Максимальное число семафоров в системе задаётся параметром SEMMNS , который, следовательно, должен быть равен как минимум сумме max_connections , autovacuum_max_workers , max_wal_senders и max_worker_processes , плюс один дополнительный на каждые 16 семафоров подключений и рабочих процессов (см. формулу в Таблице 19.1). Параметр SEMMNI определяет максимальное число наборов семафоров, которые могут существовать в системе в один момент времени. Таким образом, его значение должно быть не меньше чем ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 5) / 16) . В качестве временного решения проблем, которые вызываются этими ограничениями, но обычно сопровождаются некорректными сообщениями функции semget , например, « No space left on device » (На устройстве не осталось места) можно уменьшить число разрешённых соединений.

В некоторых случаях может потребоваться увеличить SEMMAP как минимум до уровня SEMMNS . Если в системе есть такой параметр (а во многих системах его нет), он определяет размер карты ресурсов семафоров, в которой выделяется запись для каждого непрерывного блока семафоров. Когда набор семафоров освобождается, эта запись либо добавляется к существующей соседней записи, либо регистрируется как новая запись в карте. Если карта переполняется, освобождаемые семафоры теряются (до перезагрузки). Таким образом, фрагментация пространства семафоров может со временем привести к уменьшению числа доступных семафоров.

Другие параметры, связанные с « аннулированием операций » с семафорами, например, SEMMNU и SEMUME , на работу PostgreSQL не влияют.

При использовании семафоров POSIX требуемое их количество не отличается от количества для System V, то есть по одному семафору на разрешённое подключение (max_connections), на разрешённый рабочий процесс автоочистки (autovacuum_max_workers) и фоновый процесс (max_worker_processes). На платформах, где предпочитается этот вариант, отсутствует определённый лимит ядра на количество семафоров POSIX.

Для таких параметров, как SHMMAX , никакая дополнительная настройка не должна требоваться, так как система, похоже, позволяет использовать всю память в качестве разделяемой. Подобная конфигурация используется обычно и для других баз данных, например, для DB/2 .

Однако может понадобиться изменить глобальные параметры ulimit в /etc/security/limits , так как стандартные жёсткие ограничения на размер ( fsize ) и количество файлов ( nofiles ) могут быть недостаточно большими. FreeBSD

Параметры разделяемой памяти по умолчанию вполне приемлемы, если вы не выберете в shared_memory_type вариант sysv . Семафоры System V на этой платформе не используются.

Значения параметров IPC по умолчанию можно изменить, используя возможности sysctl или loader . С помощью sysctl можно задать следующие параметры:

Чтобы эти изменения сохранялись после перезагрузки, измените /etc/sysctl.conf .

Если вы выбрали в shared_memory_type вариант sysv , возможно, вы захотите настроить ядро так, чтобы разделяемая память System V всегда находилась в ОЗУ и никогда не выгружалась в пространство подкачки. Это можно сделать, установив с помощью sysctl параметр kern.ipc.shm_use_phys .

Если вы запускаете сервер в «камере» FreeBSD, установите для параметра sysvshm значение new , чтобы у сервера было собственное отдельное пространство имён разделяемой памяти System V. (До версии 11.0 во FreeBSD требовалось разрешать общий доступ из камер к пространству имён IPC ведущего узла и принимать меры для недопущения конфликтов.) NetBSD

Параметры разделяемой памяти по умолчанию вполне приемлемы, если вы не выберете в shared_memory_type вариант sysv . Обычно имеет смысл увеличить kern.ipc.semmni и kern.ipc.semmns , так как их значения по умолчанию в NetBSD слишком малы.

Параметры IPC можно изменить, воспользовавшись командой sysctl , например:

Чтобы эти параметры сохранялись после перезагрузки, измените /etc/sysctl.conf .

Если вы выбрали в shared_memory_type вариант sysv , возможно, вы захотите настроить ядро так, чтобы разделяемая память System V всегда находилась в ОЗУ и никогда не выгружалась в пространство подкачки. Это можно сделать, установив с помощью sysctl параметр kern.ipc.shm_use_phys . OpenBSD

Параметры разделяемой памяти по умолчанию вполне приемлемы, если вы не выберете в shared_memory_type вариант sysv Обычно имеет смысл увеличить kern.seminfo.semmni и kern.seminfo.semmns , так как их значения по умолчанию в OpenBSD слишком малы.

Параметры IPC можно изменить, воспользовавшись командой sysctl , например:

Чтобы эти параметры сохранялись после перезагрузки, измените /etc/sysctl.conf . HP-UX

Значения по умолчанию как правило вполне удовлетворяют обычным потребностям.

Параметры разделяемой памяти по умолчанию вполне приемлемы, если вы не выберете в shared_memory_type вариант sysv . И даже в этом случае их потребуется увеличить только для старых ядер, в которых эти параметры по умолчанию имеют маленькие значения. Семафоры System V на этой платформе не используются.

Параметры разделяемой памяти можно изменить, воспользовавшись командой sysctl . Например, так можно выделить 16 ГБ:

Чтобы сохранить эти изменения после перезагрузки, воспользуйтесь файлом /etc/sysctl.conf . macOS

Параметры разделяемой памяти и семафоров по умолчанию вполне приемлемы, если вы не выберете в shared_memory_type вариант sysv .

Для настройки разделяемой памяти в macOS рекомендуется создать файл /etc/sysctl.conf и записать в него присваивания переменных следующим образом:

Заметьте, что в некоторых версиях macOS, все пять параметров разделяемой памяти должны быть установлены в /etc/sysctl.conf , иначе их значения будут проигнорированы.

Значение SHMMAX должно быть кратно 4096.

SHMALL на этой платформе измеряется в страницах (по 4 КБ).

Все параметры, кроме SHMMNI можно изменить «на лету», воспользовавшись командой sysctl . Но тем не менее лучше задавать выбранные вами значения в /etc/sysctl.conf , чтобы они сохранялись после перезагрузки. Solaris
illumos

Параметры разделяемой памяти по умолчанию вполне приемлемы для большинства применений PostgreSQL . По умолчанию Solaris устанавливает в SHMMAX четверть объёма ОЗУ . Чтобы выбрать другое значение, задайте соответствующий параметр проекта, связанного с пользователем postgres . Например, выполните от имени root такую команду:

Эта команда создаёт проект user.postgres и устанавливает максимальный объём разделяемой памяти для пользователя postgres равным 8 ГБ. Это изменение вступает в силу при следующем входе этого пользователя или при перезапуске PostgreSQL (не перезагрузке конфигурации). При этом подразумевается, что PostgreSQL выполняется пользователем postgres в группе postgres . Перезагружать систему после этой команды не нужно.

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

Кроме того, если PostgreSQL у вас выполняется внутри зоны, может понадобиться также увеличить лимиты на использование ресурсов зоны. Получить дополнительную информацию о проектах и команде prctl можно в Руководстве системного администратора (System Administrator’s Guide), «Главе 2: Проекты и задачи» (Chapter2: Projects and Tasks).

19.4.2. RemoveIPC в systemd

Если используется systemd , необходимо позаботиться о том, чтобы ресурсы IPC (включая разделяемую память) не освобождались преждевременно операционной системой. Это особенно актуально при сборке и установке PostgreSQL из исходного кода. Пользователей дистрибутивных пакетов PostgreSQL это касается в меньшей степени, так как пользователь postgres обычно создаётся как системный пользователь.

Параметр RemoveIPC в logind.conf определяет, должны ли объекты IPC удаляться при полном выходе пользователя из системы. На системных пользователей это не распространяется. Этот параметр по умолчанию включён в стандартной сборке systemd , но в некоторых дистрибутивах операционных систем он по умолчанию отключён.

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

(ПРЕДУПРЕЖДЕНИЕ: ошибка при удалении сегмента разделяемой памяти «/PostgreSQL.1450751626»: Нет такого файла или каталога) Различные типы объектов IPC (разделяемая память/семафоры, System V/POSIX) обрабатываются в systemd несколько по-разному, поэтому могут наблюдаться ситуации, когда некоторые ресурсы IPC не удаляются так, как другие. Однако полагаться на эти тонкие различия не рекомендуется.

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

Какой пользователь является « системным » , определяется во время компиляции systemd , исходя из значения SYS_UID_MAX в /etc/login.defs .

Скрипт упаковывания и развёртывания сервера должен предусмотрительно создавать пользователя postgres как системного пользователя, используя команды useradd -r , adduser —system или равнозначные.

Если же учётная запись пользователя была создана некорректно и изменить её невозможно, рекомендуется задать

в /etc/systemd/logind.conf или другом подходящем файле конфигурации.

Внимание

Необходимо предпринять минимум одно из этих двух действий, иначе сервер PostgreSQL будет очень нестабильным.

19.4.3. Ограничения ресурсов

В Unix-подобных операционных системах существуют различные типы ограничений ресурсов, которые могут влиять на работу сервера PostgreSQL . Особенно важны ограничения на число процессов для пользователя, число открытых файлов и объём памяти для каждого процесса. Каждое из этих ограничений имеет « жёсткий » и « мягкий » предел. Мягкий предел действительно ограничивает использование ресурса, но пользователь может увеличить его значение до жёсткого предела. Изменить жёсткий предел может только пользователь root. За изменение этих параметров отвечает системный вызов setrlimit . Управлять этими ресурсами в командной строке позволяет встроенная команда ulimit (в оболочках Bourne) и limit ( csh ). В системах семейства BSD различными ограничениями ресурсов, устанавливаемыми при входе пользователя, управляет файл /etc/login.conf . За подробностями обратитесь к документации операционной системы. Для PostgreSQL интерес представляют параметры maxproc , openfiles и datasize . Они могут задаваться, например так:

(Здесь -cur обозначает мягкий предел. Чтобы задать жёсткий предел, нужно заменить это окончание на -max .)

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

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

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

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

Ещё одно ограничение в ядре, с которым можно столкнуться, когда устанавливается большое количество клиентских подключений, — максимальная длина очереди подключений к сокету. Если количество запросов на подключение за короткий промежуток времени превышает этот максимум, некоторые из них будут отклонены до того, как главный процесс сможет их обработать, при этом клиенты получат неинформативное сообщение об ошибке подключения типа « Resource temporarily unavailable » (Ресурс временно недоступен) или « Connection refused » (Не удалось подключиться). Предел длины очереди на многих платформах по умолчанию составляет 128. Чтобы увеличить его, настройте соответствующий параметр ядра через sysctl и перезапустите главный процесс. Этот параметр называется net.core.somaxconn в Linux, kern.ipc.soacceptqueue в последних версиях FreeBSD и kern.ipc.somaxconn в macOS и на других платформах BSD.

19.4.4. Чрезмерное выделение памяти в Linux

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

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

Это сообщение говорит о том, что процесс postgres был уничтожен из-за нехватки памяти. Хотя существующие подключения к базе данных будут работать по-прежнему, новые подключения приниматься не будут. Чтобы восстановить работу сервера, PostgreSQL придётся перезапустить.

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

Если памяти не хватает по вине самого PostgreSQL , эту проблему можно решить, изменив конфигурацию сервера. В некоторых случаях может помочь уменьшение конфигурационных параметров, связанных с памятью, а именно shared_buffers , work_mem и hash_mem_multiplier . В других случаях проблема может возникать, потому что разрешено слишком много подключений к самому серверу баз данных. Чаще всего в такой ситуации стоит уменьшить число подключений max_connections и организовать внешний пул соединений.

« Чрезмерное выделение » памяти можно предотвратить, изменив поведение ядра. Хотя при этом OOM killer (уничтожение процессов при нехватке памяти) всё равно может вызываться, вероятность такого уничтожения значительно уменьшается, а значит поведение системы становится более стабильным. Для этого нужно включить режим строгого выделения памяти, воспользовавшись sysctl :

либо поместив соответствующую запись в /etc/sysctl.conf . Возможно, вы также захотите изменить связанный параметр vm.overcommit_ratio . За подробностями обратитесь к документации ядра https://www.kernel.org/doc/Documentation/vm/overcommit-accounting.

Другой подход, который можно применить (возможно, вместе с изменением vm.overcommit_memory ), заключается в исключении процесса postmaster из числа возможных жертв при нехватке памяти. Для этого нужно задать для свойства поправка очков OOM этого процесса значение -1000 . Проще всего это можно сделать, выполнив

в скрипте запуска управляющего процесса непосредственно перед тем, как запускать postmaster. Заметьте, что делать это надо под именем root, иначе ничего не изменится; поэтому проще всего вставить эту команду в стартовый скрипт, принадлежащий пользователю root. Если вы делаете это, вы также должны установить в данном скрипте эти переменные окружения перед запуском главного процесса:

С такими параметрами дочерние процессы главного будут запускаться с обычной, нулевой поправкой очков OOM, так что при необходимости механизм OOM сможет уничтожать их. Вы можете задать и другое значение для PG_OOM_ADJUST_VALUE , если хотите, чтобы дочерние процессы исполнялись с другой поправкой OOM. ( PG_OOM_ADJUST_VALUE также можно опустить, в этом случае подразумевается нулевое значение.) Если вы не установите PG_OOM_ADJUST_FILE , дочерние процессы будут работать с той же поправкой очков OOM, которая задана для главного процесса, что неразумно, так всё это делается как раз для того, чтобы главный процесс оказался на особом положении.

19.4.5. Огромные страницы в Linux

Использование огромных страниц (huge pages) снижает накладные расходы при работе с большими непрерывными блоками памяти, что характерно для PostgreSQL , особенно при большом объёме shared_buffers. Чтобы такие страницы можно было задействовать в PostgreSQL , ядро должно быть собрано с параметрами CONFIG_HUGETLBFS=y и CONFIG_HUGETLB_PAGE=y . Также вам понадобится настроить ОС, чтобы она могла выделить достаточное количество огромных страниц нужного размера. Чтобы определить требуемое количество огромных страниц, узнайте значение параметра shared_memory_size_in_huge_pages, воспользовавшись командой postgres . Обратите внимание, что узнать значение этого вычисляемого параметра можно только при остановленном сервере. Например, вы можете получить:

В этом примере размер по умолчанию составляет 2 МБ, но задав в параметре huge_page_size 2 МБ или 1 ГБ явным образом, вы получите в shared_memory_size_in_huge_pages пересчитанное количество страниц. В данном примере серверу потребуется 3170 огромных страниц, но можно запросить и больше, если огромные страницы будут использоваться и другими программами в этой системе. Выбранное значение можно задать так:

Не забудьте добавить этот параметр в /etc/sysctl.conf , чтобы он действовал и после перезагрузки. Если же размер огромных страниц отличается от подразумеваемого по умолчанию, их количество можно задать так:

Также можно задать эти параметры во время загрузки ОС, установив соответствующие параметры ядра, например hugepagesz=2M hugepages=3170 .

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

What are the huge pages in Linux?

HugePages in Linux

In this article, we will walk you through details about huge pages so that you will be able to answer: what are huge pages in Linux? How to enable/disable huge pages? How to determine huge page value? in Linux like RHEL6, RHEL7, Ubuntu, etc.

Lets start with Huge pages basics.

What is Huge page in Linux?

Huge pages are helpful in virtual memory management in the Linux system. As the name suggests, they help is managing huge size pages in memory in addition to standard 4KB page size. You can define as huge as 1GB page size using huge pages.

During system boot, you reserve your memory portion with huge pages for your application. This memory portion i.e. these memory occupied by huge pages is never swapped out of memory. It will stick there until you change your configuration. This increases application performance to a great extent like Oracle database with pretty large memory requirements.

Why use huge page?

In virtual memory management, the kernel maintains a table in which it has a mapping of the virtual memory address to a physical address. For every page transaction, the kernel needs to load related mapping. If you have small size pages then you need to load more numbers of pages resulting kernel to load more mapping tables. This decreases performance.

Using huge pages means you will need fewer pages. This decreases the number of mapping tables to load by the kernel to a great extent. This increases your kernel-level performance which ultimately benefits your application.

In short, by enabling huge pages, the system has fewer page tables to deal with and hence less overhead to access/maintain them!

How to configure huge pages?

Run below command to check current huge pages details.

In the above output, you can see the one-page size is 2MB Hugepagesize and a total of 0 pages on the system HugePages_Total . This huge page size can be increased from 2MB to max 1GB.

Run below script to get how much huge pages your system needs currently. The script is from Oracle and can be found.

You can save it in /tmp as hugepages_settings.sh and then run it like below :

Output will be similar to some number as shown in above sample output.

This means your system needs 124 huge pages of 2MB each! If you have set 4MB as page size then the output would have been 62. You got the point, right?

Configure hugepages in kernel

Now last part is to configure the above-stated kernel parameter and reload it. Add below value in /etc/sysctl.conf and reload configuration by issuing sysctl -p command.

Notice that we added 2 extra pages in the kernel since we want to keep a couple of pages spare than the actual required number.

Now, huge pages have been configured in the kernel but to allow your application to use them you need to increase memory limits as well. The new memory limit should be 126 pages x 2 MB each = 252 MB i.e. 258048 KB.

You need to edit below settings in /etc/security/limits.conf

Sometimes these settings are configured in app-specific files like for Oracle DB its in /etc/security/limits.d/99-grid-oracle-limits.conf

That’s it! You might want to restart your application to make use of these new huge pages.

How to disable hugepages?

HugePages are generally enabled by default. Use the below command to check the current state of huge pages.

[always] flag in output shows that hugepages are enabled on system.

For RedHat based systems file path is /sys/kernel/mm/redhat_transparent_hugepage/enabled

Чем Linux HugePages важны для серверов баз данных?

Часто пользователи рассказывают нам о сбое базы данных по вине Out Of Memory Killer. Он завершает процессы PostgreSQL и остается причиной большинства отказов этой БД. Память на хост-компьютере может закончиться по нескольким причинам, наиболее распространенные из них:

Плохо настроена память на хост-компьютере.

Ограничения глобальной переменной work_mem. Например, если у вас 32Гб RAM и work_mem=1Гб, то больше 32 соединений вы никогда не запустите. Каждое соединение PostgreSQL будет выделять этот размер памяти.

Большое количество подключений. Даже неактивное соединение может занимать значительный объем памяти.

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

Меня зовут Jobin Augustine, я работаю в Percona старшим инженером службы поддержки. Более 20-лет был консультантом, архитектором, администратором и инструктором по PostgreSQL, Oracle и другим технологиям баз данных. Поговорим о том, как можно защититься от OOM с помощью HugePages. Разберем насколько они важны и почему нужны.

Когда-то мы помогали настраивать хост-компьютеры и базы данных своим клиентам, то никогда не тратили время на объяснения. Не рассказывали им, что и почему делаем. Мой друг и коллега Fernando Laudares Camargos уже не раз обращал на это мое внимание. Поэтому я решил написать эту статью, чтобы сразу для всех объяснить суть проблемы на проверяемом и повторяемом случае. Это удобно, тем более если вы захотите провести свое собственное тестирование.

Условия теста

40 ядер ЦП (80 vCPU) и 192 ГБ физической памяти.

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

Transparent HugePages (THP) отключены.

За прошедшие годы в Transparent HugePages (THP) было внесено множество улучшений, которые позволяют приложениям использовать HugePages без модификации кода. THP часто рассматривается как замена обычным HugePages для универсальной рабочей нагрузки. Однако использование THP в системах баз данных не рекомендуется, так как оно может привести к фрагментации памяти и увеличенному времени ответа. Это не специфическая проблема PostgreSQL, она затрагивает все системы баз данных. Например:

Я не хочу уходить от темы и ударяться в объяснения, почему THP лучше не использовать для сервера базы данных, кто хочет разобраться, может почитать информацию по ссылкам. Мы же вернемся к условиям теста.

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

Чтобы поддерживать постоянное соединение от хаба к PostgreSQL у параметра server_lifetime указано большое значение.

Вслед за PostgreSQL, вносим изменения в параметры для имитации общих настроек клиентской среды:

Тестовая нагрузка создается с помощью sysbench:

sysbench /usr/share/sysbench/oltp_point_select.lua —db-driver=pgsql —pgsql-host=localhost —pgsql-port=6432 —pgsql-db=sbtest2 —pgsql-user=postgres —pgsql-password=vagrant —threads=80 —report-interval=1 —tables=100 —table-size=37000000 prepare

А затем запускаем:

sysbench /usr/share/sysbench/oltp_point_select.lua —db-driver=pgsql —pgsql-host=localhost —pgsql-port=6432 —pgsql-db=sbtest2 —pgsql-user=postgres —pgsql-password=vagrant —threads=80 —report-interval=1 —time=86400 —tables=80 —table-size=37000000 run

На первом этапе подготовки, на сервер ложится нагрузка write, а на втором — только read. Я не буду объяснять теорию и концепции, лежащие в основе HugePages, а лишь сосредоточусь на анализе последствий его использования. Если хотите разобраться в деталях, можете посмотреть статьи: Five-Level Page Tables и Measuring the Memory Overhead of a Postgres Connection.

Ход тестирования

Мы решили измерять потребление памяти с помощью утилиты Linux free . При использовании обычного пула страниц памяти, потребление началось с очень низкого значения. Но по ходу проведения теста оно начало расти. На скриншоте видно, что со временем «доступная» память расходуется быстрее.

Ближе к концу теста уже видно высокую swap-активность. Она более наглядно фиксируется в выходных данных vmstat :

Информация из /proc/meminfo показывает, что размер таблицы Total Page с первоначальных 45 Мб вырос до более чем 25 Гб:

Это не просто потеря памяти, а огромное потребление ресурсов, влияющее на общее выполнение программ и работоспособность операционной системы. Мы видим, что потери состоят из суммы записей нижнего уровня PageTable с 80 процессами PostgreSQL.

Подтвердить эти данные, можно проверив каждый процесс PostgreSQL:

Если мы возьмем полученное значение 314240 Кб и умножим на 80 соединений, то как раз получим наш примерный общий размер PageTable — 25 Гб. Поскольку наш синтетический тест передает почти одинаковую рабочую нагрузку через все соединения, у всех отдельных процессов почти одинаковые значения, очень близкие к зафиксированному выше.

Поскольку PostgreSQL использует Linux shared memory, нет смысла сосредотачиваться на Rss (Resident set size). Поэтому используем следующую строку для проверки Pss (Proportional set size):

PostgreSQL запускает по процессу на каждое соединение, плюс несколько фоновых. Поэтому без информации Pss сложно понять, как память распределяется по процессам.

В типичной системе баз данных со значительной нагрузкой DML, фоновые процессы PostgreSQL, такие как Checkpointer, Background Writer или рабочие процессы Autovaccum, будут касаться большего количества страниц в shared memory. Соответствующий Pss для этих процессов будет выше, чем для активных процессов.

Так становится понятнее, что Checkpointer, Background worker или даже Postmaster часто становятся целью OOM Killer потому, что используют больше памяти, чем другие процессы.

Так становится понятнее, что Checkpointer, Background worker или даже Postmaster часто становятся целью OOM Killer потому, что используют больше памяти, чем другие процессы.

После нескольких часов активности, отдельный сеанс затронул больше страниц shared memory. Как следствие, значения Pss для каждого процесса были перераспределены. Потребление памяти checpointer-ом снизилось, а другие сеансы поделили память приблизительно поровну.

Тем не менее, Checkpointer сохранил наибольшую долю. Такая схема нагрузки характерна для нагрузочного теста, при котором основная нагрузка приходится на CheckPointer и Background Writer.

Включение HugePages

HugePage (hugetlbfs) изначально появился в Linux Kernel в 2002 году для удовлетворения требований систем баз данных, которым необходимо обращаться к большому объему памяти. Но оказался очень актуален для целей разработки и используется до сих пор. Например, в данном случае помогает сократить расход памяти.

Его можно использовать вместо раздутых таблиц страниц, достаточно выяснить, сколько памяти должно быть выделено для HugePages. Для этого проверим VmPeak процесса postmaster. Например, если PID процесса postmaster — 4537, то:

grep ^VmPeak /proc/4357/status

Получаем необходимый объем памяти в Кб:

VmPeak: 148392404 kB

В HugePages все это должно преобразоваться в страницы размером 2 Мб:

Укажите это значение в /etc/sysctl.conf для vm.nr_hugepages , например: vm.nr_hugepages = 72457

Теперь закройте экземпляр PostgreSQL и выполните sysctl -p

Проверим, создано ли запрошенное количество huge pages:

Запустив PostgreSQL на этом этапе, мы увидим, что HugePages_Rsvd выделен.

Если все идет как надо, на этом этапе я бы убедился, что PostgreSQL всегда использует HugePages. Потому, что уж лучше иметь сбой при запуске PostgreSQL, чем проблемы позже.

postgres=# ALTER SYSTEM SET huge_pages = on;

Обратите внимание, что приведенное выше изменение требует перезапуска экземпляра PostgreSQL.

Тесты с включенными HugePages

HugePages создаются заранее, еще до запуска PostgreSQL. Он просто выделяет и использует их. Так что мы не заметим изменения вывода утилиты Linux free до и после запуска. PostgreSQL загружает shared memory в HugePages, если они уже доступны. Shared_buffers , принадлежащие PostgreSQL, занимают больший объем shared memory.

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

Общий размер таблиц страниц тоже остался прежним:

При этом разница в расходе памяти была колоссальной. Всего 61 Мб при включенном HugePages, вместо 25 Гб без него. Pss за сеанс также значительно сократился:

Самое большое преимущество HugePages заключалось в том, что CheckPointer и Background Writer больше не забирали несколько Гб ОЗУ.

Вместо этого они потребляли лишь несколько Мб. А это значит, что они больше не были целью OOM Killer.

Выводы

Мы убедились, что благодаря HugePages фоновые процессы PostgreSQL не забирали на себя большой объем shared memory. Поэтому они переставали быть первоочередной целью OOM Killers и связанных с этим сбоев. Такие улучшения могут спасти систему, если она находится на грани OOM. Но гарантировать, что это позволит навсегда защитить базу данных от всех условий OOM — все же нельзя.

Нам удалось снизить общее потребление памяти. Если без HugePages доступная память на сервере была полностью исчерпана и началась подкачка, то после его включения в доступном кэше файловой системы Linux остались 38-39 Гб.

Существуют и другие преимущества использования HugePages. Они никогда не выгружаются на диск. Когда общие буферы PostgreSQL находятся в HugePages, они обеспечивают более стабильную и предсказуемую производительность.

Linux использует метод многоуровневой таблицы страниц. HugePages реализованы с использованием прямых указателей на страницы из промежуточного слоя (огромная страница размером 2 Мб находится непосредственно на уровне PMD, без промежуточной страницы PTE). Преобразование адресов значительно упрощается. А это часто встречающаяся операция на сервере базы данных с большим объемом памяти.

28-29 апреля в Москве впервые пройдет TestDriven Conf 2022 — профессиональная конференция для senior тестировщиков и QA-инженеров. Она будет посвящена всем вопросам автоматизации в тестировании и рядом.

Расписание и тезисы докладов уже на сайте. И можно забронировать билеты по выгодной цене — чем ближе к конференции, тем будет дороже.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

https://lechenie.kapelnicza-ot-zapoya-sankt-peterburg.ru/