Многопроцессорная обработка Python 3.5.x: "Ошибка OSE: [Errno 12] Невозможно выделить память" в 74% свободной системе ОЗУ
Я написал приложение на Python 3.5, которое порождает несколько процессов, используя multiprocessing
библиотека. Он работает на Ubuntu 16.04.3 LTS на выделенном сервере, который поставляется с 2-мя процессорами Intel Xeon E2690 v1 (по 8 ядер в каждой) и 96 ГБ оперативной памяти.
Эта система запускает экземпляр PostgreSQL, который настроен на использование максимум 32 ГБ ОЗУ (effective_cache_size
установлен в 32GB
), но это только напоминание для целей моего вопроса (я пробовал несколько комбинаций effective_cache_size
, work_mem
, shared_buffers
, так далее.).
Каждый процесс открывает соединение с базой данных и многократно использует его.
Вот упрощенная часть кода, которая показывает, как я порождаю новый процесс:
from multiprocessing import Process
import time
process = Process(target=start_algorithm, args=(arg1, arg2))
process.start()
def start_algorithm():
while True :
time.sleep(1)
return True
После порождения более 200 процессов (точное число не всегда совпадает) приложение выдает исключение при попытке создать новый процесс:
OSError: [Errno 12] Cannot allocate memory
ulimit -a
вывод:
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 384500
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 524288
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 384500
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
В /etc/sysctl.conf
Я установил следующие параметры, которые хорошо работают для mdadm
и PostgreSQL:
# Allows for 84GB shared_buffers in PostgreSQL
kernel.shmmax = 90914313216
kernel.shmall = 22020096
# Various PostgreSQL optimizations
vm.overcommit_memory = 2
vm.overcommit_ratio = 90
vm.swappiness = 4
vm.zone_reclaim_mode = 0
vm.dirty_ratio = 15
vm.dirty_background_ratio = 3
# mdadm optimizations
vm.min_free_kbytes=262144
kernel.sched_migration_cost_ns = 5000000
kernel.sched_autogroup_enabled = 0
dev.raid.speed_limit_max=1000000
dev.raid.speed_limit_min=1000000
Я пытался также установить и сбросить vm.nr_hugepages
но это не избавило от проблемы.
Перед запуском моего приложения на Python использование оперативной памяти составляет около 500 MB
над 96 GB
, так что я вижу, что общая оперативная память довольно пуста. После порождения этих 200+ процессов ОЗУ начинает заполняться и достигает максимума примерно при 20 GB
(оставшийся 74 GB
все еще свободны), а затем бросает Cannot allocate memory
исключение.
Вопрос в том, почему?
Я попытался измерить след общих процессов и нашел memory_profiler
библиотека / инструмент для Python. Я смог получить этот график:
Если я не ошибаюсь, это о 47500 MiB
памяти, так о 50 GB
из "занятой" оперативной памяти. Каждый след процесса должен быть около 170 MB
, Проблема в том, что я нигде не вижу такого количества занятой оперативной памяти. Вот некоторые выводы:
$ free -h
total used free shared buff/cache available
Mem: 94G 18G 74G 570M 1,6G 73G
Swap: 15G 0B 15G
$ vmstat -S M
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 0 75879 81 1518 0 0 3 40 229 157 12 1 86 0 0
$ top
Tasks: 1457 total, 2 running, 1455 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3,1 us, 0,9 sy, 0,0 ni, 95,8 id, 0,0 wa, 0,0 hi, 0,2 si, 0,0 st
KiB Mem : 98847552 total, 77698752 free, 19509612 used, 1639188 buff/cache
KiB Swap: 15825916 total, 15825916 free, 0 used. 77580104 avail Mem
Я смог запустить 287 процессов, уменьшив объем памяти, требуемый PostgreSQL, но это всегда приводит к МНОГО свободного ОЗУ (74GB
). Вот мой файл конфигурации для PostgreSQL 9.6 (postgresql.conf
):
max_connections=2000
listen_addresses = '127.0.0.1,192.168.2.90'
shared_buffers = 1GB
work_mem = 42MB
port=5433
maintenance_work_mem = 256MB
checkpoint_completion_target = 0.9
effective_cache_size = 32GB
default_statistics_target = 1000
random_page_cost=1.2
seq_page_cost=1.0
max_files_per_process = 500 # default 1000
huge_pages = off
РЕДАКТИРОВАТЬ
Я нашел этот ответ на SO и нашел способ измерить общее использование памяти.
Python (порождено 288 процессов):
$ ps aux | grep python3 | awk '{sum=sum+$6}; END {print sum/1024 " MB"}'
53488.1 MB
PostgreSQL:
$ ps aux | grep postgres | awk '{sum=sum+$6}; END {print sum/1024 " MB"}'
20653.4 MB
Я до сих пор не понимаю, почему обычные инструменты (vmstat
, free
, top
, glances
) показывает другой объем используемой оперативной памяти.