🔤🔤🔤 OOMKiller - страшный сон Администратора баз данных.
Попробуем выяснить основные причины, неочевидные особенности работы Лучшей в мире платформы 1С и возможно ли с этим что-то сделать…
OOMKiller –система защиты Linux от перерасхода памяти.
Срабатывает, убивая самый «толстый» по памяти процесс в Операционной системе при приближении расхода памяти к 95%
Итак, в чём же вообще проблема?
В том, что неожиданно падает сервер PostgreSQL и по системным логам мы понимаем что он пал жертвой убийства со стороны OOMKiller.
Основной причиной такого поведения PostgreSQL является ошибка планировщика в том, где делать сортировку результата запроса.
Ошибка планировщика чаще всего возникает из-за отсутствия актуальной статистики по базе – это корневая причина
В итоге, при планировании запроса, планировщик уверен что сортировку можно произвести в памяти, так как по его прикидкам она влезет в параметр work_mem (например 256MB), так как получит на вход небольшое количество строк для сортировки и их размер тоже будет небольшим (например 100 строк, размером по 30КБ каждая). Эти данные планировщик получает из статистики.
Начав выполнение запроса СУБД уже не может ослушаться этой декларации планировщика и выполняет сортировку в памяти.
НО вместо предполагаемых 100 строк по 30КБ, что спокойно вмещается в 256МБ нам для сортировки прилетело 1,5млн строк по 30КБ, что уже занимает 42ГБ!
В итоге в процессе сортировки такого объёма в памяти, эта самая память или совсем закончится или её расход приблизится к уровню сработки OOMKiller-а.
Ситуацию усугубляется следующим:
❗️Убивать процессы нельзя! Это приведет к перезапуску всего PostgreSQL!
▫️work_mem устанавливается на каждое соединение. Т.е. у нас может сложится ситуация когда сразу несколько пользователей 1С сделают запрос к СУБД на такого рода сортировку…
▫️ Каждое соединение в PostgreSQL работает в отдельном процессе и при этом процесс не умеет худеть по памяти, он просто убивается как становится никому не нужным.
▫️ Платформа 1С удерживает idle соединения при необходимости (например в нём создавалась временная таблица и её можно будет переиспользовать) . Т.е. у нас может сложится ситуация, когда запрос выполнился, заняв 20ГБ RAM, при этом соединение удерживается и используется для других запросов, которым столько RAM не нужно.
Методы борьбы с этими негативными моментами:
❗️Убивать процессы нельзя! Это приведет к перезапуску всего PostgreSQL! А значит увидев что процесс работает (не idle) наш единственный шанс корректно завершить этот запрос, это отменить его выполнение на сервере 1С вычислив его по номеру соединения с СУБД.
▫️Необходимо содержать статистику в максимально актуальном состоянии. Настраивая агрессивный autovacuum, устанавливая нестандартные значения сработки autovacuum на больших таблицах, проводить vacuum analyze как регламентную операцию и т.д.
▫️Уменьшить work_mem, отслеживая как это повлияет и на план и на работу дисков
▫️Настроить OOMKiller. Чтобы он не убивал, а замораживал процесс OOMPolicy=continue, но это опасно, так как в итоге вся память может быть поглощена и работы вообще остановится.
▫️Отдельный пункт для борьбы с толстыми idle.
Мы можем вычислить ТОП-3 процессов, потребляющих память
ps -eo pmem,vsize,pid,cmd | sort -k 1 -nr | head -3
и увидеть такую картину:
6.6 12095572 2380533 postgres: postgres erp 10.27.24.1(64722) idle
6.6 12015844 2380844 postgres: postgres erp 10.27.24.1(64837) idle
6.4 12091534 2380682 postgres: postgres erp 10.27.24.1(64735) SELECT
Видим что в топе процессы с idle и только 1 с select.
В PostgreSQL есть параметр idle_session_timeout, по умолчанию 0.
Но мы можем выставить его в 3 сек, подождать 7 сек и обратно убрать в 0.
ALTER SYSTEM SET idle_session_timeout = 3000;
'select pg_reload_conf();
'ALTER SYSTEM RESET idle_session_timeout;
Таким образом мы корректно прервём соединения с 1С и СУБд сама корректно завершит наши объевшиеся процессы.
Негативный побочный эффект – временные таблицы платформе 1С придётся создать заново, но по сравнению с перезапуском СУБД это допустимо