2012-05-02 4 views
15

У меня есть некоторые команды управления, основанные на gevent. Поскольку моя команда управления делает тысячи запросов, я могу превратить все вызовы сокетов в неблокирующие вызовы с помощью Gevent. Это действительно ускоряет мое приложение, так как я могу делать запросы одновременно.Как pgBouncer помогает ускорить Django

В настоящее время узким местом в моем приложении, по-видимому, является Postgres. Похоже, что это потому, что библиотека Psycopg, которая используется для подключения к Django, написана на C и не поддерживает асинхронные соединения.

Я также читал, что использование pgBouncer может ускорить Postgres на 2X. Это звучит здорово, но было бы здорово, если бы кто-нибудь мог объяснить, как pgBouncer работает и помогает?

Благодаря

+0

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

ответ

65

Помимо сохранения накладных расходов на подключение & отключите, если это делается по каждому запросу, пул соединений может направлять большое количество клиентских подключений вплоть до небольшого числа реальных подключений к базе данных. В PostgreSQL оптимальное количество активных соединений с базой данных обычно находится где-то вокруг ((2 * core_count) + effective_spindle_count). Выше этого числа ухудшаются как пропускная способность, так и латентность.

Иногда люди говорят: «Я хочу поддерживать 2000 пользователей с быстрым временем отклика». В значительной степени гарантировано, что если вы попытаетесь сделать это с 2000 действительными подключениями к базе данных, производительность будет ужасной. Если у вас есть машина с четырьмя четырехъядерными процессорами, и активный набор данных полностью кэширован, вы увидите гораздо лучшую производительность для этих пользователей 2000 путем перенаправления запросов через 35 подключений к базе данных.

Чтобы понять, почему это так, этот мысленный эксперимент должен помочь. Рассмотрим гипотетический сервер баз данных базы данных с одним ресурсом для совместного использования - одним ядром. Это ядро ​​будет равномерно распределено между всеми параллельными запросами без накладных расходов. Предположим, что в тот же момент приходит все 100 запросов, каждый из которых нуждается в одной секунде процессорного времени. Ядро работает над всеми из них, сокращая время между ними, пока они не закончили через 100 секунд. Теперь рассмотрим, что произойдет, если вы поместите пул подключений на передней панели, который будет принимать 100 клиентских подключений, но одновременно будет делать только один запрос на сервер базы данных, помещая любые запросы, которые поступают, когда соединение занято в очередь. Теперь, когда 100 запросов поступают в одно и то же время, один клиент получает ответ за 1 секунду; другой получает ответ через 2 секунды, а последний клиент получает ответ через 100 секунд. Никто не должен был ждать дольше, чтобы получить ответ, пропускная способность такая же, но средняя задержка составляет 50,5 секунды, а не 100 секунд.

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

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

+1

Отличный ответ. Не могу не согласиться. – wildplasser

+0

Эти хиппи-интерфейсы всего хотят сделать и сломать соединения как можно быстрее, и поставить пулеметы в передней части, если они не могут достичь своего естественного состояния. Мне нравится формула 2 * ncore + nspindle. Каждый процесс считается заблокированным на диске. – wildplasser

+0

@kgrittn Я предполагаю, что в вашем опыте мысли выше, каждый запрос занимает одну секунду, чтобы работать при отсутствии других запросов? –

9

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

+0

Если Iäve понял это правильно - Django все еще создает соединения снова и снова, но pgBouncer сокращает время, необходимое для создания этого соединения. Я слышал, что Django создает новое соединение для каждого запроса. По запросу люди означают веб-запрос для извлечения страницы (что означает, что каждая отдельная команда, выполняемая в цикле представления, проходит через одно соединение с базой данных), или запрос означает, что каждая отдельная база данных попадает (SELECT, INSERT, UPDATE и DELETE), и в этом случае каждая отдельная команда будет выполняться в новом соединении, даже если они будут в одном цикле просмотра. –

+2

Да, Django создаст новое соединение, но соединение будет установлено быстрее, так как оно будет связано с локальным экземпляром PgBouncer. Django будет использовать новое соединение для каждого веб-запроса, а не запроса к базе данных. –

+1

Возможно, у вас [этот вопрос] (http://stackoverflow.com/questions/1125504/django-persistent-database-connection) есть еще более интересная информация. Но обратите внимание, что есть причина, по которой открываются новые подключения по каждому запросу. Если запрос встречает ошибку, возможно, транзакция может быть не закрыта (между прочим), что приведет к неожиданным результатам. –

 Смежные вопросы

  • Нет связанных вопросов^_^