Как оптимизировать пул соединений Postgresql max_connections и node-postgres?

Короче говоря, у меня возникают проблемы с поддержкой более 5000 запросов на чтение в минуту из API данных, использующих Postgresql, Node.js и node-postgres. Похоже, что узкое место находится между API и БД. Вот подробности внедрения.

Я использую экземпляр базы данных AWS Postgresql RDS (m4.4xlarge - 64 ГБ памяти, 16 виртуальных ЦП, 350 ГБ SSD, без выделенных операций ввода-вывода в секунду) для API данных на базе Node.js. По умолчанию max_connections RDS = 5000. Нагрузка API узла сбалансирована по двум кластерам по 4 процесса в каждом (2 Ec2 с 4 виртуальными ЦП, на которых выполняется API с PM2 в кластерном режиме). Я использую node-postgres для привязки API к Postgresql RDS, и я пытаюсь использовать его соединение функция объединения. Ниже приведен образец кода моего пула соединений:

var pool = new Pool({
    user: settings.database.username,
    password: settings.database.password,
    host: settings.database.readServer,
    database: settings.database.database,
    max: 25, 
    idleTimeoutMillis: 1000
});

/* Example of pool usage */
pool.query('SELECT my_column FROM my_table', function(err, result){
    
    /* Callback code here */
});

Используя эту реализацию и тестирование с помощью тестера нагрузки, я могу поддерживать около 5000 запросов в течение одной минуты со средним временем отклика около 190 мс (что я и ожидал). Как только я отправляю более 5000 запросов в минуту, мое время ответа увеличивается до 1200 мс в лучшем случае, а в худшем случае API начинает часто отключаться по таймауту. Мониторинг показывает, что для EC2, на которых запущен API Node.js, загрузка ЦП остается ниже 10%. Таким образом, я сосредоточен на БД и привязке API к БД.

Я попытался увеличить (и уменьшить, если на то пошло) настройку максимального количества подключений node-postgres, но не было никаких изменений в поведении ответа / тайм-аута API. Я также пробовал подготовить IOPS на RDS, но без улучшений. Кроме того, что интересно, я масштабировал RDS до m4.10xlarge (160 ГБ памяти, 40 виртуальных ЦП), и хотя загрузка ЦП RDS сильно упала, общая производительность API значительно ухудшилась (не мог даже поддерживать 5000 запросов в минуту. что я смог сделать с меньшим RDS).

Я нахожусь на другой территории во многих отношениях, и я не уверен, как лучше всего определить, какая из этих движущихся частей является узким местом производительности API при более 5000 запросов в минуту. Как уже отмечалось, я попытался внести различные изменения на основе обзора документации по конфигурации Postgresql и документации node-postgres, но безрезультатно.

Если у кого-то есть совет по диагностике или оптимизации, я был бы очень признателен.

ОБНОВИТЬ

После масштабирования до m4.10xlarge я провел серию нагрузочных тестов, варьируя количество запросов в минуту и ​​максимальное количество подключений в каждом пуле. Вот несколько снимков экрана с показателями мониторинга:

мониторинг показателей

подключения к базе данных


person rgwozdz    schedule 31.10.2016    source источник
comment
максимальное соединение по умолчанию - 100. Вы пробовали его увеличить?   -  person Andrew Scott Evans    schedule 04.08.2017


Ответы (3)


Чтобы поддерживать более 5 тыс. Запросов при сохранении той же скорости отклика, вам понадобится лучшее оборудование ...

Простая математика утверждает, что: 5000 requests*190ms avg = 950k ms divided into 16 cores ~ 60k ms per core что в основном означает, что ваша система была сильно загружена.
(я предполагаю, что у вас был запасной процессор, так как некоторое время было потеряно на сети)

Теперь действительно интересная часть вашего вопроса связана с попыткой масштабирования: m4.10xlarge (160 ГБ памяти, 40 виртуальных ЦП).
Снижение загрузки ЦП указывает на то, что масштабирование освободило временные ресурсы БД - поэтому вам необходимо нажмите больше запросов!
2 предложения:

  • Попробуйте увеличить пул соединений до max: 70 и посмотрите на сетевой трафик (в зависимости от объема данных, которые вы можете загружать в сеть)
  • Кроме того, синхронизируются ли ваши запросы к БД со стороны приложения? убедитесь, что ваше приложение действительно может отправлять больше запросов.
person cohenjo    schedule 02.11.2016
comment
Спасибо большое. Я попробую установить максимальное значение 70 (при 8-кратной репликации API Node.js в кластере, это означает, что эффективный максимум составляет 70 * 8 = 560). Что касается асинхронных запросов к БД из приложения - я предположил, что все асинхронно, учитывая асинхронные функции Node.js и node-postgres Клиент Postgres описан разработчиком как неблокирующий. Но я должен признать, что я не изучал внутреннее устройство библиотек, чтобы подтвердить это. - person rgwozdz; 03.11.2016
comment
У вас есть мониторинг на db сервере? Io / network - что-то пошло? И просто интересно, уменьшилось ли увеличение до 2k - улучшает ли это уменьшение до 10? - person cohenjo; 08.11.2016
comment
Вот подробности из теста 10xlarge с max: 20: нагрузочным тестом 3000 запросов чтения / мин. CPU около 7%. Без таймаутов; Нагрузочный тест с 4000 запросов чтения в минуту. CPU около 64%. Более половины приложений запрашивают тайм-аут. Попробую сделать скриншоты мониторинга и обновить пост. - person rgwozdz; 08.11.2016
comment
Мы знаем, что центральный процессор не является узким местом - взгляните на io и сеть. Отсутствие тайм-аутов означает, что вы можете нажимать больше :) - person cohenjo; 08.11.2016
comment
По вашему совету я провел серию нагрузочных тестов на m4.10xlarge. Я обновил исходный вопрос, добавив, как представляется, наиболее отзывчивые метрики БД. - person rgwozdz; 08.11.2016
comment
Привет, что-то в вашей конфигурации приложения не работает - как для max = 10, так и для max = 70 у вас только 80 подключений db. Кстати, как у вас был ЦП приложения во время тестов - все ли запросы обрабатывает 8 ВЦП? - person cohenjo; 08.11.2016

Лучше всего использовать отдельный Pool для каждого вызова API в зависимости от приоритета вызова:

const highPriority = new Pool({max: 20}); // for high-priority API calls
const lowPriority = new Pool({max: 5}); // for low-priority API calls

Затем вы просто используете правильный пул для каждого вызова API для оптимальной доступности сервиса / соединения.

person vitaly-t    schedule 23.06.2017

Поскольку вас интересует производительность чтения, вы можете настроить репликацию между двумя (или более) экземплярами PostgreSQL, а затем использовать pgpool II для балансировки нагрузки между экземплярами.

Горизонтальное масштабирование означает, что вы не начнете достигать максимальных размеров инстансов в AWS, если на следующей неделе решите выполнить 10 000 одновременных чтений.

Вы также начинаете получать HA в своей архитектуре.

--

Часто люди будут использовать pgbouncer в качестве пула соединений, даже если он уже встроен в их код приложения. pgbouncer работает очень хорошо и, как правило, проще настраивать pgpool и управлять им, но он не выполняет балансировку нагрузки. Я не уверен, что это поможет вам в этом сценарии.

person rotten    schedule 07.11.2016
comment
Я определенно рассматривал реплики для чтения, но мне интересно узнать больше о задержке репликации. В нашем приложении есть пользователи, которые очень часто записывают данные, которые затем используются таким образом, чтобы влиять на содержимое их полезных данных запроса чтения. Таким образом, задержка репликации может привести к другим задержкам в нашем решении. Есть ли способ оценить задержку репликации? - person rgwozdz; 08.11.2016
comment
Здесь обсуждается измерение задержки репликации в одном из списков postgresql: postgresql.org/message-id/ - person rotten; 08.11.2016
comment
По моему опыту, обычно это не так уж плохо. К тому времени, когда страница отрисовывается и конечный пользователь делает запрос, репликация завершается. Проблемы возникают тогда, когда вы отправляете запись, сразу за которой следует чтение. Я думаю, что в pgpool есть некоторые настройки, которые помогают в этих сценариях, хотя прошло несколько лет с тех пор, как я испортил настройки pgpool. - person rotten; 08.11.2016