docs: update sysadmin/Linux/PostgreSQL/PGbouncer
parent
d0d79bd132
commit
eb7cbd1f67
|
|
@ -2,95 +2,478 @@
|
|||
title: PGbouncer
|
||||
description:
|
||||
published: true
|
||||
date: 2023-11-15T17:46:54.594Z
|
||||
date: 2023-11-15T17:47:12.972Z
|
||||
tags:
|
||||
editor: ckeditor
|
||||
dateCreated: 2023-11-15T17:46:34.218Z
|
||||
-->
|
||||
|
||||
<p>Типичные веб-проекты, разрабатываемые на чем-то вроде Python или PHP, характерны тем, что создают большое количество соединений к СУБД — по одному, а иногда и по несколько, на каждый HTTP-запрос. Имея классическую архитектуру «один процесс на соединение», PostgreSQL не очень хорошо справляется с большим (условно, больше 100) количеством соединений. Решить проблему позволяет пулер соединений под названием PgBouncer. Благодаря использованию библиотеки libevent, PgBouncer может поддерживать большое количество (тысячи) соединений, которые проксируются на несколько (пара десятков) соединений непосредственно к PostgreSQL.</p>
|
||||
<p>Типичные веб-проекты, разрабатываемые на чем-то вроде <a href="https://eax.me/python/"><u>Python</u></a> или PHP, характерны тем, что создают большое количество соединений к СУБД — по одному, а иногда и по несколько, <i>на каждый HTTP-запрос</i>. Имея классическую архитектуру «один процесс на соединение», PostgreSQL не очень хорошо справляется с большим (условно, больше 100) количеством соединений. Решить проблему позволяет пулер соединений под названием <a href="https://pgbouncer.github.io/"><u>PgBouncer</u></a>. Благодаря использованию библиотеки <a href="https://eax.me/libevent/"><u>libevent</u></a>, PgBouncer может поддерживать большое количество (тысячи) соединений, которые проксируются на несколько (пара десятков) соединений непосредственно к PostgreSQL.</p>
|
||||
<p>Данная заметка предполагает, что на сервере у вас используется Ubuntu Linux, так как сегодня это, по всей видимости, наиболее популярный серверный дистрибутив Linux. Отличия описанный далее шагов для других дистрибутивов будут минимальными. Также предполагается, что на сервере уже установлен PostgreSQL.</p>
|
||||
<p> </p>
|
||||
<p>Устанавливается PgBouncer очень просто:</p>
|
||||
<p>sudo apt-get install pgbouncer<br>
|
||||
По умолчанию прокси слушает порт 6432. Логи можно почитать так:</p>
|
||||
<p>less /var/log/postgresql/pgbouncer.log<br>
|
||||
Конфигурационный файл называется /etc/pgbouncer/pgbouncer.ini. Рассмотрим основные параметры.</p>
|
||||
<p>;; database name = connect string<br>
|
||||
;;<br>
|
||||
;; connect string params:<br>
|
||||
;; dbname= host= port= user= password=<br>
|
||||
;; client_encoding= datestyle= timezone=<br>
|
||||
;; pool_size= connect_query=<br>
|
||||
;; auth_user=<br>
|
||||
[databases]</p>
|
||||
<ul>
|
||||
<li>= host=localhost port=5432<br>
|
||||
Здесь мы можем указать, на каких серверах какие базы нужно искать. Звездочкой обозначается дэфолтный сервер. Кстати, это хоть еще и примитивный, но все-таки уже шардинг.</li>
|
||||
</ul>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>sudo</code> <code>apt-get install</code> <code>pgbouncer</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>По умолчанию прокси слушает порт 6432. Логи можно почитать так:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>less</code> <code>/var/log/postgresql/pgbouncer.log</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Конфигурационный файл называется /etc/pgbouncer/pgbouncer.ini. Рассмотрим основные параметры.</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>;; database</code> <code>name</code> <code>= connect</code> <code>string</code></p>
|
||||
<p><code>;;</code></p>
|
||||
<p><code>;; connect</code> <code>string params:</code></p>
|
||||
<p><code>;; dbname= host= port= user= password=</code></p>
|
||||
<p><code>;; client_encoding= datestyle= timezone=</code></p>
|
||||
<p><code>;; pool_size= connect_query=</code></p>
|
||||
<p><code>;; auth_user=</code></p>
|
||||
<p><code>[databases]</code></p>
|
||||
<p><code>* = host=localhost port=5432</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Здесь мы можем указать, на каких серверах какие базы нужно искать. Звездочкой обозначается дэфолтный сервер. Кстати, это хоть еще и примитивный, но все-таки уже шардинг.</p>
|
||||
<p>Не менее важная настройка:</p>
|
||||
<p>; When server connection is released back to pool:<br>
|
||||
; session - after client disconnects<br>
|
||||
; transaction - after transaction finishes<br>
|
||||
; statement - after statement finishes<br>
|
||||
pool_mode = transaction<br>
|
||||
о умолчанию стоит в session, то есть, сессия будет удерживаться клиентом до тех пор, пока он не закроет соединение. Чаще всего значение имеет смысл заменить на transaction. В этом случае соединение будет возвращаться в общий пул после завершения транзакции. Значение statement означает, что соединение будет освобождаться после выполнения каждого отдельного выражения, чего вы почти наверняка не должны хотеть.</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>; When</code> <code>server connection</code> <code>is</code> <code>released back to</code> <code>pool:</code></p>
|
||||
<p><code>; session - after</code> <code>client disconnects</code></p>
|
||||
<p><code>; transaction</code> <code>- after</code> <code>transaction</code> <code>finishes</code></p>
|
||||
<p><code>; statement - after</code> <code>statement finishes</code></p>
|
||||
<p><code>pool_mode = transaction</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>о умолчанию стоит в session, то есть, сессия будет удерживаться клиентом до тех пор, пока он не закроет соединение. Чаще всего значение имеет смысл заменить на transaction. В этом случае соединение будет возвращаться в общий пул после завершения транзакции. Значение statement означает, что соединение будет освобождаться после выполнения каждого отдельного выражения, чего вы почти наверняка не должны хотеть.</p>
|
||||
<p>Настройки размера пула:</p>
|
||||
<p>; total number of clients that can connect<br>
|
||||
max_client_conn = 1000</p>
|
||||
<p>; default pool size. 20 is good number when transaction pooling<br>
|
||||
; is in use, in session pooling it needs to be the number of<br>
|
||||
; max clients you want to handle at any moment<br>
|
||||
default_pool_size = 20</p>
|
||||
<p>;; Minimum number of server connections to keep in pool.<br>
|
||||
;min_pool_size = 0<br>
|
||||
Здесь я увеличил максимальное количество клиентских соединений до 1000. Значение, используемое по умолчанию, равно 100.</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>; total number of</code> <code>clients that can connect</code></p>
|
||||
<p><code>max_client_conn = 1000</code></p>
|
||||
<p><code>; default</code> <code>pool size. 20 is</code> <code>good number when</code> <code>transaction</code> <code>pooling</code></p>
|
||||
<p><code>; is</code> <code>in</code> <code>use, in</code> <code>session pooling it needs to</code> <code>be the number of</code></p>
|
||||
<p><code>; max</code> <code>clients you want to</code> <code>handle at</code> <code>any</code> <code>moment</code></p>
|
||||
<p><code>default_pool_size = 20</code></p>
|
||||
<p><code>;; Minimum number of</code> <code>server connections to</code> <code>keep in</code> <code>pool.</code></p>
|
||||
<p><code>;min_pool_size = 0</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Здесь я увеличил максимальное количество клиентских соединений до 1000. Значение, используемое по умолчанию, равно 100.</p>
|
||||
<p>Настройки аутентификации:</p>
|
||||
<p>; any, trust, plain, crypt, md5, cert, hba, pam<br>
|
||||
auth_type = md5<br>
|
||||
auth_file = /etc/pgbouncer/userlist.txt<br>
|
||||
… и доступа к админке pgbouncer:</p>
|
||||
<p>;;;<br>
|
||||
;;; Users allowed into database 'pgbouncer'<br>
|
||||
;;;</p>
|
||||
<p>; comma-separated list of users, who are allowed to change settings<br>
|
||||
admin_users = eax</p>
|
||||
<p>; comma-separated list of users who are just allowed to use<br>
|
||||
; SHOW command<br>
|
||||
;stats_users = stats, root<br>
|
||||
Важно! Параметр auth_type по умолчанию имеет значение trust. То есть, PgBouncer будет пускать всех в базу данных без запроса пароля. Вы почти наверняка этого не хотите.</p>
|
||||
<p>Файл /etc/pgbouncer/userlist.txt содержит имена пользователей и пароли, с которыми PgBouncer подключается к базе. Например:</p>
|
||||
<p>"eax" "qwerty"<br>
|
||||
Пароли не обязательно хранить открытым текстом:</p>
|
||||
<p>"eax" "md5175c641eaed0b6c05ae8444b73d789f0"<br>
|
||||
Здесь хэш 175c641e... был посчитан как MD5 от пароля, следом за которым записано имя пользователя:</p>
|
||||
<p>echo -n 'qwertyeax' | md5sum<br>
|
||||
Теперь, когда PgBouncer настроен, можно перезапустить его:</p>
|
||||
<p>sudo service pgbouncer restart<br>
|
||||
… и ломиться в базу:</p>
|
||||
<p>psql -p 6432 -U eax<br>
|
||||
Если все было сделано правильно, PgBouncer запросит пароль. В приведенном примере пароль, с которым пользователь ходит в PostgreSQL напрямую, и пароль, запрашиваемый PgBouncer — это один и тот же пароль. Если вдруг что-то не работает, рекомендую начать с проверки настройки аутентификации самого PostgreSQL (файл pg_hba.conf).</p>
|
||||
<p>Выше пользователь eax был добавлен в admin_users, что дает ему доступ в админку PgBouncer. Вход в админку осуществляется так:</p>
|
||||
<p>psql -p 6432 -U eax pgbouncer<br>
|
||||
Наиболее интересная из доступных команд — это перечитывание конфигурации без перезапуска сервера:</p>
|
||||
<p>RELOAD;<br>
|
||||
Информацию о других доступных командах можно посмотреть так:</p>
|
||||
<p>SHOW HELP;<br>
|
||||
Пример вывода:</p>
|
||||
<p>DETAIL:<br>
|
||||
SHOW HELP|CONFIG|DATABASES|POOLS|CLIENTS|SERVERS|VERSION<br>
|
||||
SHOW FDS|SOCKETS|ACTIVE_SOCKETS|LISTS|MEM<br>
|
||||
SHOW DNS_HOSTS|DNS_ZONES<br>
|
||||
SHOW STATS|STATS_TOTALS|STATS_AVERAGES<br>
|
||||
SET key = arg<br>
|
||||
RELOAD<br>
|
||||
PAUSE []<br>
|
||||
RESUME []<br>
|
||||
DISABLE <br>
|
||||
ENABLE <br>
|
||||
KILL <br>
|
||||
SUSPEND<br>
|
||||
SHUTDOWN<br>
|
||||
Проверяем, что все работает, запустив pgbench с 900 соединенинями:</p>
|
||||
<p>pgbench -i<br>
|
||||
pgbench -p 6432 -c 900 -C -T 60 -P 1<br>
|
||||
Если все было сделано правильно, pgbench будет превосходно работать, а команда SHOW CLIENTS;, выполненная в админке PgBouncer, покажет 900 соединений. При этом вывод ps wuax | grep postgres покажет, что реально запущено лишь 20 бэкендов PostgreSQL.</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>; any, trust, plain, crypt, md5, cert, hba, pam</code></p>
|
||||
<p><code>auth_type = md5</code></p>
|
||||
<p><code>auth_file = /etc/pgbouncer/userlist.txt</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>… и доступа к админке pgbouncer:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>;;;</code></p>
|
||||
<p><code>;;; Users allowed into</code> <code>database</code> <code>'pgbouncer'</code></p>
|
||||
<p><code>;;;</code></p>
|
||||
<p><code>; comma-separated list of</code> <code>users, who are allowed to</code> <code>change settings</code></p>
|
||||
<p><code>admin_users = eax</code></p>
|
||||
<p><code>; comma-separated list of</code> <code>users who are just allowed to</code> <code>use</code></p>
|
||||
<p><code>; SHOW command</code></p>
|
||||
<p><code>;stats_users = stats, root</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p><strong>Важно!</strong> Параметр auth_type по умолчанию имеет значение trust. То есть, PgBouncer будет пускать всех в базу данных без запроса пароля. Вы почти наверняка этого не хотите.</p>
|
||||
<p>Файл /etc/pgbouncer/userlist.txt содержит имена пользователей и пароли, с которыми PgBouncer подключается к базе. Например:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>"eax"</code> <code>"qwerty"</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Пароли не обязательно хранить открытым текстом:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>"eax"</code> <code>"md5175c641eaed0b6c05ae8444b73d789f0"</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Здесь хэш <code>175c641e...</code> был посчитан как MD5 от пароля, следом за которым записано имя пользователя:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>echo</code> <code>-n 'qwertyeax'</code> <code>| md5sum</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Теперь, когда PgBouncer настроен, можно перезапустить его:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>sudo</code> <code>service pgbouncer restart</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>… и ломиться в базу:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>psql -p 6432 -U eax</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Если все было сделано правильно, PgBouncer запросит пароль. В приведенном примере пароль, с которым пользователь ходит в PostgreSQL напрямую, и пароль, запрашиваемый PgBouncer — это один и тот же пароль. Если вдруг что-то не работает, рекомендую начать с проверки настройки аутентификации самого PostgreSQL (файл pg_hba.conf).</p>
|
||||
<p>Выше пользователь eax был добавлен в admin_users, что дает ему доступ в админку PgBouncer. Вход в админку осуществляется так:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>psql -p 6432 -U eax pgbouncer</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Наиболее интересная из доступных команд — это перечитывание конфигурации без перезапуска сервера:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>RELOAD;</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Информацию о других доступных командах можно посмотреть так:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>SHOW HELP;</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Пример вывода:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>DETAIL:</code></p>
|
||||
<p><code> SHOW HELP|CONFIG|DATABASES|POOLS|CLIENTS|SERVERS|VERSION</code></p>
|
||||
<p><code> SHOW FDS|SOCKETS|ACTIVE_SOCKETS|LISTS|MEM</code></p>
|
||||
<p><code> SHOW DNS_HOSTS|DNS_ZONES</code></p>
|
||||
<p><code> SHOW STATS|STATS_TOTALS|STATS_AVERAGES</code></p>
|
||||
<p><code> SET key = arg</code></p>
|
||||
<p><code> RELOAD</code></p>
|
||||
<p><code> PAUSE [<db>]</code></p>
|
||||
<p><code> RESUME [<db>]</code></p>
|
||||
<p><code> DISABLE <db></code></p>
|
||||
<p><code> ENABLE <db></code></p>
|
||||
<p><code> KILL <db></code></p>
|
||||
<p><code> SUSPEND</code></p>
|
||||
<p><code> SHUTDOWN</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Проверяем, что все работает, запустив pgbench с 900 соединенинями:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>pgbench -i</code></p>
|
||||
<p><code>pgbench -p 6432 -c 900 -C -T 60 -P 1</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Если все было сделано правильно, pgbench будет превосходно работать, а команда <code>SHOW CLIENTS;</code>, выполненная в админке PgBouncer, покажет 900 соединений. При этом вывод <code>ps wuax | grep postgres</code> покажет, что реально запущено лишь 20 бэкендов PostgreSQL.Типичные веб-проекты, разрабатываемые на чем-то вроде <a href="https://eax.me/python/"><u>Python</u></a> или PHP, характерны тем, что создают большое количество соединений к СУБД — по одному, а иногда и по несколько, <i>на каждый HTTP-запрос</i>. Имея классическую архитектуру «один процесс на соединение», PostgreSQL не очень хорошо справляется с большим (условно, больше 100) количеством соединений. Решить проблему позволяет пулер соединений под названием <a href="https://pgbouncer.github.io/"><u>PgBouncer</u></a>. Благодаря использованию библиотеки <a href="https://eax.me/libevent/"><u>libevent</u></a>, PgBouncer может поддерживать большое количество (тысячи) соединений, которые проксируются на несколько (пара десятков) соединений непосредственно к PostgreSQL.</p>
|
||||
<p>Данная заметка предполагает, что на сервере у вас используется Ubuntu Linux, так как сегодня это, по всей видимости, наиболее популярный серверный дистрибутив Linux. Отличия описанный далее шагов для других дистрибутивов будут минимальными. Также предполагается, что на сервере уже установлен PostgreSQL.</p>
|
||||
<p> </p>
|
||||
<p>Устанавливается PgBouncer очень просто:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>sudo</code> <code>apt-get install</code> <code>pgbouncer</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>По умолчанию прокси слушает порт 6432. Логи можно почитать так:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>less</code> <code>/var/log/postgresql/pgbouncer.log</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Конфигурационный файл называется /etc/pgbouncer/pgbouncer.ini. Рассмотрим основные параметры.</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>;; database</code> <code>name</code> <code>= connect</code> <code>string</code></p>
|
||||
<p><code>;;</code></p>
|
||||
<p><code>;; connect</code> <code>string params:</code></p>
|
||||
<p><code>;; dbname= host= port= user= password=</code></p>
|
||||
<p><code>;; client_encoding= datestyle= timezone=</code></p>
|
||||
<p><code>;; pool_size= connect_query=</code></p>
|
||||
<p><code>;; auth_user=</code></p>
|
||||
<p><code>[databases]</code></p>
|
||||
<p><code>* = host=localhost port=5432</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Здесь мы можем указать, на каких серверах какие базы нужно искать. Звездочкой обозначается дэфолтный сервер. Кстати, это хоть еще и примитивный, но все-таки уже шардинг.</p>
|
||||
<p>Не менее важная настройка:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>; When</code> <code>server connection</code> <code>is</code> <code>released back to</code> <code>pool:</code></p>
|
||||
<p><code>; session - after</code> <code>client disconnects</code></p>
|
||||
<p><code>; transaction</code> <code>- after</code> <code>transaction</code> <code>finishes</code></p>
|
||||
<p><code>; statement - after</code> <code>statement finishes</code></p>
|
||||
<p><code>pool_mode = transaction</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>о умолчанию стоит в session, то есть, сессия будет удерживаться клиентом до тех пор, пока он не закроет соединение. Чаще всего значение имеет смысл заменить на transaction. В этом случае соединение будет возвращаться в общий пул после завершения транзакции. Значение statement означает, что соединение будет освобождаться после выполнения каждого отдельного выражения, чего вы почти наверняка не должны хотеть.</p>
|
||||
<p>Настройки размера пула:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>; total number of</code> <code>clients that can connect</code></p>
|
||||
<p><code>max_client_conn = 1000</code></p>
|
||||
<p><code>; default</code> <code>pool size. 20 is</code> <code>good number when</code> <code>transaction</code> <code>pooling</code></p>
|
||||
<p><code>; is</code> <code>in</code> <code>use, in</code> <code>session pooling it needs to</code> <code>be the number of</code></p>
|
||||
<p><code>; max</code> <code>clients you want to</code> <code>handle at</code> <code>any</code> <code>moment</code></p>
|
||||
<p><code>default_pool_size = 20</code></p>
|
||||
<p><code>;; Minimum number of</code> <code>server connections to</code> <code>keep in</code> <code>pool.</code></p>
|
||||
<p><code>;min_pool_size = 0</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Здесь я увеличил максимальное количество клиентских соединений до 1000. Значение, используемое по умолчанию, равно 100.</p>
|
||||
<p>Настройки аутентификации:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>; any, trust, plain, crypt, md5, cert, hba, pam</code></p>
|
||||
<p><code>auth_type = md5</code></p>
|
||||
<p><code>auth_file = /etc/pgbouncer/userlist.txt</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>… и доступа к админке pgbouncer:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>;;;</code></p>
|
||||
<p><code>;;; Users allowed into</code> <code>database</code> <code>'pgbouncer'</code></p>
|
||||
<p><code>;;;</code></p>
|
||||
<p><code>; comma-separated list of</code> <code>users, who are allowed to</code> <code>change settings</code></p>
|
||||
<p><code>admin_users = eax</code></p>
|
||||
<p><code>; comma-separated list of</code> <code>users who are just allowed to</code> <code>use</code></p>
|
||||
<p><code>; SHOW command</code></p>
|
||||
<p><code>;stats_users = stats, root</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p><strong>Важно!</strong> Параметр auth_type по умолчанию имеет значение trust. То есть, PgBouncer будет пускать всех в базу данных без запроса пароля. Вы почти наверняка этого не хотите.</p>
|
||||
<p>Файл /etc/pgbouncer/userlist.txt содержит имена пользователей и пароли, с которыми PgBouncer подключается к базе. Например:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>"eax"</code> <code>"qwerty"</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Пароли не обязательно хранить открытым текстом:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>"eax"</code> <code>"md5175c641eaed0b6c05ae8444b73d789f0"</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Здесь хэш <code>175c641e...</code> был посчитан как MD5 от пароля, следом за которым записано имя пользователя:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>echo</code> <code>-n 'qwertyeax'</code> <code>| md5sum</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Теперь, когда PgBouncer настроен, можно перезапустить его:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>sudo</code> <code>service pgbouncer restart</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>… и ломиться в базу:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>psql -p 6432 -U eax</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Если все было сделано правильно, PgBouncer запросит пароль. В приведенном примере пароль, с которым пользователь ходит в PostgreSQL напрямую, и пароль, запрашиваемый PgBouncer — это один и тот же пароль. Если вдруг что-то не работает, рекомендую начать с проверки настройки аутентификации самого PostgreSQL (файл pg_hba.conf).</p>
|
||||
<p>Выше пользователь eax был добавлен в admin_users, что дает ему доступ в админку PgBouncer. Вход в админку осуществляется так:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>psql -p 6432 -U eax pgbouncer</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Наиболее интересная из доступных команд — это перечитывание конфигурации без перезапуска сервера:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>RELOAD;</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Информацию о других доступных командах можно посмотреть так:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;"><code>SHOW HELP;</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Пример вывода:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>DETAIL:</code></p>
|
||||
<p><code> SHOW HELP|CONFIG|DATABASES|POOLS|CLIENTS|SERVERS|VERSION</code></p>
|
||||
<p><code> SHOW FDS|SOCKETS|ACTIVE_SOCKETS|LISTS|MEM</code></p>
|
||||
<p><code> SHOW DNS_HOSTS|DNS_ZONES</code></p>
|
||||
<p><code> SHOW STATS|STATS_TOTALS|STATS_AVERAGES</code></p>
|
||||
<p><code> SET key = arg</code></p>
|
||||
<p><code> RELOAD</code></p>
|
||||
<p><code> PAUSE [<db>]</code></p>
|
||||
<p><code> RESUME [<db>]</code></p>
|
||||
<p><code> DISABLE <db></code></p>
|
||||
<p><code> ENABLE <db></code></p>
|
||||
<p><code> KILL <db></code></p>
|
||||
<p><code> SUSPEND</code></p>
|
||||
<p><code> SHUTDOWN</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Проверяем, что все работает, запустив pgbench с 900 соединенинями:</p>
|
||||
<figure class="table" style="height:auto;width:1468px;">
|
||||
<table style="background-color:rgb(255, 255, 255);border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="border-bottom:0px;border-left:0px;border-right:0px;border-top:0px;height:auto;padding:0px 0px 0px 15px;width:1453px;">
|
||||
<p><code>pgbench -i</code></p>
|
||||
<p><code>pgbench -p 6432 -c 900 -C -T 60 -P 1</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</figure>
|
||||
<p>Если все было сделано правильно, pgbench будет превосходно работать, а команда <code>SHOW CLIENTS;</code>, выполненная в админке PgBouncer, покажет 900 соединений. При этом вывод <code>ps wuax | grep postgres</code> покажет, что реально запущено лишь 20 бэкендов PostgreSQL.</p>
|
||||
|
|
|
|||
Loading…
Reference in New Issue