Выбор БД pool_size для приложения Flask -SQLAlchemy, работающего на Gunicorn - PullRequest
8 голосов
/ 15 февраля 2020

У меня есть приложение Flask -SQLAlchmey, работающее в Gunicorn, подключенное к базе данных PostgreSQL, и у меня возникают проблемы с определением, каким должно быть значение pool_size и сколько соединений с базой данных мне следует ожидать.

Это мое понимание того, как все работает:

  • Процессы в Python 3.7. НЕ делятся памятью
  • Каждый работник Gunicorn - это собственный процесс
  • Следовательно, каждый работник Gunicorn получит свою собственную копию пула соединений с базой данных, и он не будет передан другим работникам
  • Потоки в Python DO совместно используют память
  • Следовательно, любые потоки внутри работника Gunicorn будут совместно использовать пул соединений с базой данных

Это пока правильно? Если это правильно, то для синхронного Flask приложения, работающего в Gunicorn:

  • Максимальное количество соединений с базой данных = (количество рабочих) * (количество потоков на одного рабочего)?
  • И будет ли внутри работника когда-либо использовать больше соединений из пула, чем рабочих?

Есть ли причина, по которой pool_size должно быть больше, чем число потоков? Итак, для приложения gunicorn, запущенного с gunicorn --workers=5 --threads=2 main:app, должно быть pool_size 2? И если я использую только рабочие, а не потоки, есть ли причина иметь pool_size больше 1?

Ответы [ 3 ]

3 голосов
/ 24 февраля 2020

Добавление моих 2 центов. Ваше понимание верно, но некоторые соображения следует учитывать:

  • , если ваше приложение связано с вводом-выводом (например, общение с базой данных), вы действительно хотите иметь более 1 потока. В противном случае ваш процессор никогда не достигнет 100% загрузки. Вам нужно поэкспериментировать с количеством потоков, чтобы получить нужную сумму, обычно с помощью инструмента нагрузочного тестирования и сравнения запросов в секунду и загрузки ЦП.

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

  • postgresql создает процесс в соответствии с соединение и может не очень хорошо масштабироваться, когда у вас будет много процессов военного оружия. Я бы сделал go с некоторым пулом соединений, который находится между вашим приложением и базой данных (я думаю, pgbouncer - самый популярный).

3 голосов
/ 24 февраля 2020

Просто добавляю свой собственный недавний опыт в @ matino's answer . Приложения WSGI также могут получить пользу от asyn c работников. Я добавлю несколько пунктов о async workers и connection pools здесь.

Недавно мы столкнулись с некоторыми похожими проблемами в нашей продукции. Наш трафик c прыгнул с парашютом через 1-2 дня, и все запросы по какой-то причине были забиты. Мы использовали gunicorn с gevent asyn c работниками для нашего django приложения. Оказалось, что psql соединения были причиной того, что многие запросы были остановлены (и в конечном итоге истекли).

Рекомендуемое число одновременных запросов - (2*CPU)+1. Таким образом, в сценарии syn c ваши вычисления будут выглядеть так: (workers_num * threads_num) <= (2 * cores_num) + 1

И вы получите (workers_num * threads_num) макс. Подключений к вашей базе данных. (скажем, все запросы имеют db-запросы). Поэтому вам нужно будет установить для вашего параметра psql pool_size значение, превышающее это число. Но когда вы используете asyn c работники, расчеты будут немного другими. Посмотрите на эту команду gunicorn:

gunicorn --worker-class=gevent --worker-connections=1000 --workers=3 django:app

В этом случае максимальное количество одновременных запросов может составить до 3000 запросов. Поэтому вам нужно установить pool_size на значение, большее 3000. Если ваше приложение связано с вводом-выводом, вы получите лучшую производительность с работниками asyn c. Таким образом, вы сможете более эффективно использовать свой ЦП.

А что касается пула соединений, когда вы используете решение, подобное PgBouncer, вы все время избавляетесь от накладных расходов на открытие и закрытие соединений. Так что это не повлияет на ваше решение о настройке pool_size. Эффекты могут быть не заметны при низких трафиках, но это будет необходимо для обработки более высоких скоростей трафика c.

2 голосов
/ 24 февраля 2020

Я бы сказал, что ваше понимание довольно хорошее. Потоки в пределах одного рабочего WSGI действительно будут совместно использовать пул соединений; поэтому теоретически максимальное количество соединений с базой данных составляет (number of workers) * N, где N = pool_size + max_overflow. (Я не уверен, что Flask -SQLAlchemy устанавливает max_overflow, но это важная часть уравнения здесь - см. документацию QueuePool , что это значит.)

На практике , если вы когда-либо используете только сеанс с областью потока, предоставленный вам Flask -SQLAlchemy, у вас будет максимум одно соединение на поток; так что если количество потоков меньше N, тогда верхняя граница будет действительно (number of workers) * (number of threads per worker).

...