Pull to refresh

Comments 26

До версии 9.3 SHMMAX был наиболее важным параметром ядра.

Вот эту мысль надо выделить ещё раз и отдельно. Потому что на 9.3+ этот параметр из важного перекочевал в бесполезный. Не влияет касательно базы ни на что. Как и shmall

А если у вас стоит что-то настолько древнее — то вам необходимо не ядро крутить, а обновлять базу. Уже 9.4.х ветка скоро EOL будет. То есть не будет ни багфиксов, ни исправлений безопасности.

PostgreSQL поддерживает большие страницы только в Linux.

Начиная с 11 — и windows. Кому-то понадобилось и сделали.

Размер большой страницы может быть установлен во время загрузки.

2мб страницы vm.nr_hugepages под linux спокойно меняются на лету. Может понадобиться сбросить файловый кэш, изменение числа страниц не вытесняет память кэша.
гигабайтные страницы — надо проверять поддержку со стороны CPU, и эти да, могут выделяться только при старте ОС.
Мы на рабочем проекте замеряли — разницу между 2мб и 1гб страницами не нашли.
А вот между 2мб и 4кб страницами при объёмах shared_buffers от 32гб — must have.

Сразу момент: shared memory в postgresql это не только shared_buffers. shared_buffers — это только буфер блоков, даже без учёта управляющих этих блоком структур. Суммарно объём shared memory выделяется где-то на 3% больше чем заявлен shared_buffers.

Теперь задайте параметр huge_pages «on»

По-умолчанию он try. То есть для использования huge pages достаточно только рестартовать базу.
try означает, что база сначала попробует запросить huge pages, если не удастся — попросит сегмент обычной памяти.
на on — при неудаче с huge pages выключится с ошибкой.

Может быть плохой идеей выдавать huge pages ровно под обрез объёма выделяемой базой памяти. Так поднимете тот же max_connections — и база при рестарте не запустится.

vm.dirty_*ratio опасны тем, что вот у вас средняя железка с 256гб памяти. И всего-то 5% от них — это уже до 12 гигабайт данных, которые вдруг начинают лететь на диск одним махом потому что база запросила fsync. И всё, диски перегружены и тормозят. Даже на хороших SSD записать 12 гигабайт random write — это не микросекунды вопрос. Они только по интерфейсу даже pci-e nvme дисков несколько секунд пропихиваться будут.
А если у вас не хорошие SSD ориентированные на пишущую нагрузку (а таких подавляющее большинство) — то сразу начинаются фокусы вроде ой, простой insert одной строки работает больше секунды. Потому что диск стал резко перегружен и не отвечает на fsync записи в WAL.
Поэтому только *_bytes и при том весьма агрессивные. Что-то вроде сотни мегабайт dirty_bytes и гигабайт-два dirty_background_bytes.

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

простой insert одной строки работает больше секунды. Потому что диск стал резко перегружен и не отвечает на fsync записи в WAL

А разве оно не будет ждать только, если включен synchronous_commit?
Мы на многих базах его выключаем по этой причине, так как во многих процессах не так страшно, что транзакция как бы прошла, а на самом деле нет (fsync понятно, что самоубийство отключать).
Дефолтно-то синхронный коммит включен. И крутят его только если знают о такой настройке. А так — да, ждать запись не будет (хотя может и будет в граничном случае когда пытаемся писать больше чем успеваем делать асинхронный fsync WAL, гарантируется же потеря только до 3*wal_writer_delay. Надо читать исходник). И я полностью согласен что для многих баз synchronous_commit=on — не очень нужное насилие над железом. Тем более эту настройку можно хоть на отдельную транзакцию переключать, это простой PGC_USERSET. При капитальном крахе ОС потерять до 3*wal_writer_delay или wal_writer_flush_after объём записи критично не так уж повсеместно.

Просто пример показательный из практики.
Дефолтно-то синхронный коммит включен

Да, поэтому мы во всех настройках, как правило, его выключаем. При этом почему-то во многих примерах в интернете, его не отключают.
Кстати, я там в конце статьи написал комментарий. Вы, как опытный специалист, может сталкивались с этим? Если да, то какие варианты решения проблема могут быть.
на on — при неудаче с huge pages выключится с ошибкой.

Так и есть. Postgre просто не рестартанет

Вообще, если сравнивать производительность shared_buffers vs huge_pages, то с большим размером shared_buffers слоник работает быстрее, в некоторых местах прямо ощутимее. Так что на мой взгляд лучше выделить большой объем памяти shared_buffers, чем активировать huge_pages в ОС, установив маленький размер shared_buffers
эээ… Вы о чём это? Как и зачем сравнивать несравнимое?

shared_buffers крутить необходимо само собой разумеется. Дефолтная настройка не для работы, а чтобы запуститься на любой кофеварке.
А если вы выделите много huge pages, которые не будете использовать — да лучше выньте из сервера планку памяти и продайте её, раз уж всё равно не используете. Выделенные huge pages не используются под page cache и в нормальной работе базы. Только под shared memory. Если вы на 128гб всего памяти выделите 32гб в huge pages, а shared_buffers поставите в 8 гб — то остальные 24гб вы тупо выбросите. Они не будут использоваться ни для чего.

Если накатываете реплику, делая потоковую репликацию, сделайте шаред на реплики очень мелким вначале, это ускорит поднятие в разы…

Так же справедливо для pitr. Есть места, где делается перебор всего shared_buffers.
Ну вот есть дилемма — использовать память для shared buffer или отдать ее часть под huge pages и где получить максимальную произоводительность. Проэкспериментировал у себя со стандартными настройками huge pages = 2 МБ, и пришел к выводу, что у меня без huge pages работает слоник быстрее
где дилемма? Нет никакой дилеммы.
Выделяете huge pages, затем запускаете базу — база размещает shared_buffers используя huge pages. Здесь нет выбора или huge pages или shared_buffers. Вы выделяете huge pages и там размещается shared memory. Объём занятой памяти под буфер от этого не меняется. Но зато радикально сокращается оверхед на управление этой памятью ядром ОС.
Может я что-то не понимаю из того, что вы написали. Но. В настройках postgresql.conf если я оставляю значение, которое было до включения huge_pages, например 4GB, то постгре не стартует, а сообщает, что не хватает памяти. Пока не уменьшу, к примеру, значение shared_buffers до 128MB, постгре не стартует
вы huge pages выделили достаточно чтобы туда shared memory влез?
shared memory в postgresql это не только shared_buffers. shared_buffers — это только буфер блоков, даже без учёта управляющих этих блоком структур. Суммарно объём shared memory выделяется где-то на 3% больше чем заявлен shared_buffers.


вы проверили, что huge pages действительно выделились после sysctl -p?
2мб страницы vm.nr_hugepages под linux спокойно меняются на лету. Может понадобиться сбросить файловый кэш, изменение числа страниц не вытесняет память кэша.


Ну и на скромных 4гб shared_buffers эффекта заметного не будет.
вы huge pages выделили достаточно чтобы туда shared memory влез?

Скорее всего нет. Нужно будет с этим поэксперементировать. Спасибо
вы проверили, что huge pages действительно выделились после sysctl -p?

Точно, проверил через значение HugePages_Rsvd
Есть еще один относительно важный параметр, с которым по крайней мере мы сталкивались регулярно: min_free_kbytes. Он проявлялся только на больших объемах оперативной памяти.
По умолчанию, там стоит совсем маленькое значение (что-то около 128МБ). В таком случае ОС использует всю свободную память под кэши диска. И такое ощущение, что у ОС в определенные моменты начинается дикая фрагментация памяти, и она начинает очень долго выделять относительно крупные блоки памяти. В dstat это выглядит как внезапно возникший дикий sys. Если профайлить PostgreSQL, то он в этот момент висит в каком-то системном вызове (не помню каком), связанным именно с выделением памяти. Мы несколько раз на это нарывались и поднятие min_free_kbytes хотя бы до 1-2ГБ решало проблему.
Кроме того, vm.swappiness выставленный 1 по непонятной причине очень часто не помогает. И Linux вместо того, чтобы освобождать кэши диска, начинает зачем-то загонять память в своп. Причем часто под нее почему-то попадают какие-то важные участки памяти postmaster'а, что весь PostgreSQL начинает подвисать. Решалось это уменьшением shared_buffers, work_mem и temp_buffers (просто за счет того, что потреблялось меньше памяти).
Кто-нибудь еще с этим сталкивался с этими 2мя проблемами?

Я больше скажу, мне min_free_kbytes даже на raspberrypi с 512мб памяти крутить пришлось =) иначе под нагрузкой сеть временно отваливалась с руганью в dmesg что нет памяти под сетевой буфер. На больших машинках да, где-то гигабайт выделяем всегда под эту настройку.


А вот по свапу не знаю, свап обязательно делаем, swapinnes ставим в 1, проблем отсюда не припоминаю.

Про min_free_kbytes неплохо написано вот в этой статье. Собственно там, я и нашел решение. А вот со swappiness их способом, я не смог, к сожалению, побороться.
По первой проблеме у вас может быть включен Transparet Huge Page?
Пробовал в обоих положениях — не помогало никак. А вообще обычно выключен.
Я сталкивался с PostgreSQL с большими БД, фрагментацию памяти можно увидеть несколькими способами:
1. Самый простой это htop с показом тредов ядра, всегда в топе будет висеть kswapd{0,1} и большой systime
2. perf top, isolate_freepages_block, будет в топе висеть.
Вот тут хорошо эта ситуация описана: habr.com/ru/company/odnoklassniki/blog/266005
vm.swappiness это софт порог, если памяти не хватает, например в следствии фрагментации, то все равно будет swap. Вариант крутить min_free_kbytes и отключить swap вообще, как бы если БД туда пошла это все равно уже могила, вывод не правильно отсайзили сервер.
Для PostgreSQL лучше использовать Huge Pages, так как большие страницы не подвержены фрагментации, поэтому под нужды PostgreSQL (при правильном сайзинге) всегда будет память.
И да тут в статье не написано, но желательно HugePages запереть под процесс через vm.hugetlb_shm_group = GID (PostgreSQL) и будет счастье. И отрубить Transparent Huge Pages это зло
(https://habr.com/ru/company/tinkoff/blog/446342/). Для БД например IBM, RedHat рекомендует отключать.
А вот что может еще немного ускорить БД: увеличение kernel.sched_migration_cost_ns = 500000 -> kernel.sched_migration_cost_ns = 5000000 (на порядок)
Хорошо про это рассказывал Илья Космодемьянский из Data Egret на HiLoad. Если вкратце, то время работы процесса на 1 ядре будет увеличено до миграции на другое или сна, тем самым вероятность выполнения 1 транзакции за проход увеличивается.
И следите за NUMA зонами, пример, 1 физ сервер с 2 сокетами. Память делиться на 2 NUMA зоны. Одна ближе к 1 сокету, 2-ая зона ко второму. Если вы аллоцируете память под shared_buffers больше чем 1 зона NUMA, то при заполнении 1 зоны память из второй не будет использоваться, а процессы пойдут в swap. Поэтому если ожидается такой кейс, необходимо использовать NUMA interleaving (править стартовые скрипты, добавляя numactl --interleave=all перед стартом процесса PostgreSQL)
По поводу NUMA совершенно верно сказано. Но отдельно хочу заострить внимание — речь всё-таки не о сокетах CPU, а о NUMA нодах. Например, они в листинге lscpu видны. Фокус в том, что они не всегда очевидно сопоставляются с числом сокетов. Так, к примеру, AMD EPYC 7551P — это один CPU, один сокет, но распознаётся как 4 раздельные NUMA.
И особое веселье тут начинается, когда приличный объём данных скапливается в одной NUMA ноде, а процессы из других нод начинают активно ходить в чужую относительно них память. Начинают нагружать этот один контроллер памяти, шины между ядрами, а у postgresql многие локи и межпроцессное взаимодействие именно через shared_memory работает и потому чувствительно к latency памяти. В общем, вдруг получается большая разница производительности смотря на ядро CPU какой группы NUMA операционная система отправила процесс базы.
PostgreSQL сам к сожалению не умеет работать с NUMA от слова вообще. И самое лучше что тут можно сделать — перевести систему в NUMA interleave режим. Тогда это будет да, неэффективно с точки зрения сухой теории, мы не используем преимущества локальных NUMA нод, а размазываем память ровным слоем по всем. Но у нас равномерно утилизированы контроллеры памяти и в результате работа запросов более равномерная независимо от того, на каком ядре CPU (и какого CPU в многосокетных конфигах) выполняется.
Удобнее всего — выключить вообще глобально в bios или параметром numa=off в cmdline ядра. Патчить инитскрипты всё-таки порядком неудобно, эти правки перетираются с обновлениями и про них легко забыть.

Ещё рядом с kernel.sched_migration_cost_ns Илья по идее должен был говорить про kernel.sched_autogroup_enabled=0
Чего ещё не упоминали в комментариях здесь?
vm.zone_reclaim_mode = 0
kernel.numa_balancing=0
vm.hugepages_treat_as_movable = 0

Ну и рядом с настройками ядра для комплекта — указание на powersave режимы CPU. CPU нагрузку из коротких быстрых запросов может не считать поводом выходить из powersave режима и может не поднимать частоту даже до своей номинальной. Переключение в performance может сильно улучшить жизнь (см /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor или какие гайки поверх этого приняты в вашем дистрибьютиве, обычно какой-то сервис при загрузке выставляет режим)
Ну плюс выбрать io scheduler под свою систему хранения.

PS: в общем, если кто надумает собрать из комментариев здесь ещё одну статью по настройкам ядра под postgresql — упомяните Data Egret в источниках, я ведь тоже оттуда ;-)
Спасибо. Очень полезно.
Удобно, наверное сделать какой-то образ системы под базу с уже установленными всеми настройками как операционки, так и PostgreSQL. Понятно, что они могут отличаться от железа, но все равно — дефолтные настройки в ОС и PostgreSQL явно не адаптированы ни под что и их придется менять в любом случае.
Вообще на системе только под базу не так уж и много мест надо крутить для среднего по больнице случая. Немного sysctl, десятка два настроек postgresql, pgbouncer, cpu powersave, чуток кроновых скриптов. Образ системы городить тут как-то избыточно. Удобнее ansible и подобные или даже просто задокументировать во внутренней вики.
Для быстрых хранилок, неплохо включить: blk-mq, плюс если 4-е ядро и доступно mq-deadline, например
Угу, я поленился про шедулеры расписывать, ограничившись
Ну плюс выбрать io scheduler под свою систему хранения.


С 4.19 ядер blk_mq семейство активно дефолтно, а с 5.0 уже старые планировщики удалили.
Значение варьируется от 0 до 100. Он определяет, сколько памяти будет выгружено или выгружено. Ноль означает отключение обмена, а 100 означает агрессивный обмен.

В интернете миф про процентное соотношение вытеснения в подкачку (vm.swappiness) ходит полезный, но не верный.
Увы в исходниках ядра указано, что параметр имеет значения от 0 до 100, но по факту он от 0 до 200, где 200 это 100%, а 0 это >0%.
Так же в исходном коде ядра, я не обнаружил проверку для диапазона этого параметра.

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

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

Ссылки по теме:

habr.com/ru/company/flant/blog/348324
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/vmscan.c#n2357
Sign up to leave a comment.

Articles