Настройка MySQL Connection Pooling через ProxySQL

База захлёбывается соединениями, а приложение ждёт ответа дольше обычного? Мы настраиваем ProxySQL под ключ: разбираем нагрузку, проектируем маршрутизацию и регламенты, чтобы пропускная способность росла без расширения серверов. Обсудим задачу и вернёмся с оценкой и планом.
Часто задаваемые вопросы

Перегрузка соединениями: когда ProxySQL обязателен

Приложение редко экономит на соединениях: типовой сценарий — открыть подключение на один-два запроса и закрыть. Пока обращений десятки, это незаметно; на пике их сотни, и база упирается в лимит соединений. Дальше запускается цепочка: часть попыток отбивается отказом, часть встаёт в очередь на стороне сервера, приложение повторяет неудачные обращения и добивает лимит окончательно.

Время ответа в такой ситуации растёт не линейно, а скачками. Пока есть свободный обработчик, отклик ровный; за порогом очередь нарастает быстрее, чем приходит трафик, и 95-й процентиль уходит вверх в разы при почти неизменной медиане — по графику это выглядит как «база внезапно стала медленной». Узкое место при этом не в процессоре и не в диске, а в постоянном пересоздании подключений: на каждое тратится согласование, проверка прав и выделение сессии.

Локальный пул внутри приложения проблему не снимает: при горизонтальном масштабировании копии сервисов умножают собственный пул, и предел снова рядом. Здесь и нужен ProxySQL — слой между приложением и базой. Он держит долгоживущие соединения к серверу, клиентам отдаёт уже готовые, разводит чтение и запись, ограничивает повторы, а вместо отказа ставит запрос в очередь с предсказуемым лимитом ожидания.

Сама настройка к установке не сводится: нужны правила маршрутизации, таймауты, размеры пулов под профиль нагрузки и метрики, по которым видно, что предел отодвинулся. Разбираем, как это закрывается.

Что даёт управляемый пул соединений приложению

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

Управляемый пул соединений снимает эту связку. Промежуточный слой принимает тысячи клиентских подключений и мультиплексирует их в небольшое число постоянных соединений к серверам базы: 2000 входящих ложатся на 40 исходящих. Приложение перестаёт зависеть от лимита на число процессов — под пиком растёт пропускная способность, а стабильное время ответа остаётся в тех же границах, что и на спокойной нагрузке.

Разворачиваем ProxySQL и настраиваем правила маршрутизации: чтения уходят на реплики, запись — на мастер, соединения переиспользуются между запросами. Доступность узлов проверяется постоянно, поэтому переключение при отказе занимает секунды и не требует вмешательства в код приложения.

Клиентские соединения при кратком сбое узла не рвутся — переподключение делает пул, а не приложение, и временные ошибки не долетают до пользователя. Эффект подтверждаем метриками: доля запросов, обслуженных без ожидания соединения, время ответа по перцентилям до и после пика, глубина очереди и число переключений при отказе.

Сравнение режимов маршрутизации запросов

Маршрутизация чтения и записи решается на одном из четырёх уровней — от промежуточного слоя перед базой до явного выбора соединения в коде. Уровень определяет, где живёт логика разведения трафика и сколько кода придётся менять при изменении топологии. Ниже — условия применимости и ограничения каждого варианта.

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

Вариант Когда подходит Ограничения
Промежуточный слой перед базой Несколько приложений и языков, нужен общий пул и разведение чтения и записи без изменений в коде Ещё одна точка отказа и лишний участок сети, нужны свой мониторинг, обновления и запас по соединениям
Встроенный пул в приложении Один сервис с предсказуемой нагрузкой, пул живёт в рамках процесса При масштабировании число соединений растёт кратно, лимиты базы считаются вручную, отказ узла обрабатывает код
Пул на стороне драйвера Приложение уже работает с базой напрямую, нужно переиспользование соединений и проверка доступности Маршрутизацию чтения и записи драйвер не делает, переключение при отказе узла остаётся на приложении
Ручное распределение Разовые задачи: миграции, отчёты, выгрузки на конкретный узел Не масштабируется, логика расползается по коду, ошибка в выборе узла видна только на реальной нагрузке

На практике варианты комбинируют: промежуточный слой держит общий пул и разведение трафика, встроенный пул сглаживает всплески внутри процесса. Ручное распределение оставляют для разовых работ — как исключение, а не как постоянную схему.

Как мы внедряем пул соединений: этапы работ

ProxySQL перед базой — не «поставили и забыли». Порядок работ определяет, переживёт ли база пик без роста ошибок и без правок в коде приложения. Ошибка в последовательности почти всегда вылезает на проде: пул есть, а соединения всё равно упираются в лимит ведущего узла. Разбираем, как строим работу.

  1. Снятие метрик и профиля нагрузки. С боевых серверов собираем статистику: сколько соединений открывается и сколько живёт, какие запросы доминируют, где растёт время ожидания. Это база для сравнения «до и после» — без неё настройка превращается в угадывание.

  2. Проектирование схемы маршрутизации. Определяем, что уходит на ведущий узел, что на реплики и как ведёт себя приложение, если реплика отстаёт. Точки отказа и порядок переключения фиксируем на схеме заранее.

  3. Настройка пула и правил перезаписи запросов. Задаём границы пула по пользователю и базе, время жизни соединений, число серверных подключений на одно клиентское. Правила перезаписи приводят различающиеся запросы к одному виду — растёт доля попаданий в кэш, нагрузка на серверы падает.

  4. Проверка под нагрузкой. Повторяем на стенде профиль с первого этапа и отдельно ломаем узел: смотрим, как быстро перестраивается маршрутизация и растут ли ошибки у приложения. Правила правим по цифрам, а не по ощущениям.

  5. Ввод в работу. Включаем постепенно — сначала доля трафика, затем весь, с наблюдением за задержками и числом соединений. Путь отключения остаётся: трафик можно вернуть напрямую, если метрики выйдут за границы.

  6. Передача и обучение. Отдаём конфигурации, схему, правила и разбор статистики: как читать состояние пула и что менять при росте нагрузки. Команда должна сама добавить правило или расширить пул.

Политики доступа, конфигурации и регламенты

Перед вводом пула соединений в эксплуатацию заказчик получает пакет артефактов, по которому систему можно обслуживать, передавать между сменами и восстанавливать после сбоя. Ниже — обязательный минимум: без него прокси ProxySQL живёт на ручных правках в момент инцидента.

  • Файлы конфигурации. Основной конфиг и переопределения по бэкендам, разложенные по контурам и под версионным контролем, с описанием: какой параметр за что отвечает и что изменится при его правке.
  • Схемы потоков. Путь запроса от приложения через прокси до реплик: правила разделения чтения и записи, разбор запросов, поведение при отказе узла.
  • Учётные записи и права. Раздельные пользователи для приложения, мониторинга и администрирования, минимально необходимые привилегии, привязка к правилам маршрутизации; секреты — в хранилище, не в файле на диске.
  • Лимиты соединений. Границы на пул, пользователя, хост и бэкенд с обоснованием каждой: при каком сценарии она достигается и как система деградирует дальше.
  • Политики доступа. Кто и из каких сетей попадает в административный интерфейс, что применяется к клиентским запросам, чем закрыт внешний периметр.
  • Регламент обслуживания и дежурства. Порядок правки правил и лимитов, перезагрузка конфигурации без обрыва живых соединений, окна работ, точки отката и инструкция дежурному: что смотреть и что делать первым.

Документы актуализируем вместе с конфигурацией — при следующем изменении топологии логику не восстанавливают по памяти.

Кейс: как выросла пропускная способность базы

Служба с неравномерным потоком: в утренние и вечерние часы пик число одновременных обращений к базе вырастало кратно, между ними нагрузка падала почти до нуля. Каждый рабочий процесс держал своё соединение с MySQL, и приложение упиралось в потолок по их числу: добавить процессы было нельзя — база начинала деградировать по всем запросам сразу.

До: время ответа росло вместе с нагрузкой

В часы пик время ответа на 95-м процентиле вырастало втрое против спокойных часов, часть запросов уходила в таймаут. Главный класс отказов — соединения заканчивались, новые запросы вставали в очередь и падали с ошибкой установки связи. Пропускная способность росла вместе с числом процессов лишь до определённой точки, дальше росло только время ответа.

После: ровная линия в пик

Между приложением и базой встал ProxySQL — проксирующий слой с собственным пулом соединений, который переиспользует их между рабочими процессами. Правила маршрутизации разделили чтение и запись, аналитические запросы ушли на реплику, а health-check выводил проблемный узел из ротации до того, как на нём копились ошибки. Время ответа в часы пик перестало разъезжаться с нагрузкой: линия 95-го процентиля стала ровной и близкой к спокойным часам.

Пропускная способность выросла кратно на том же оборудовании, потолок по числу процессов снят, отказы класса «соединения закончились» в наблюдениях больше не повторялись.

Чем ProxySQL отличается от пула внутри приложения

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

Отдельный слой перед базой меняет схему. Приложение больше не держит долгоживущие соединения — оно отдаёт короткие сессии, а общие соединения к серверу баз данных поддерживает промежуточный узел с фиксированным размером набора. Десять копий сервиса и один узел дают предсказуемое число подключений, которое не зависит от того, сколько экземпляров вы подняли в этот момент. Заодно снимается боль с перезапусками: при обновлении приложения соединения к базе не рвутся и не пересоздаются лавиной.

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

Третье — единая точка правил. Маршрутизация чтения на реплики, разрыв долгих запросов, приоритет фоновых задач, ограничение частоты обращений — всё описывается в одном конфигурационном файле, а не дублируется в коде каждого сервиса. Это заметно упрощает разбор инцидентов: видно, чей запрос и куда он ушёл.

Практический вывод: встроенный пул разумен как локальный кэш соединений внутри экземпляра, а отдельный слой нужен, когда сервисов больше одного и правила работы с базой должны быть общими. ProxySQL в этой роли закрывает оба сценария — и переиспользование соединений, и централизацию правил.

Технические сомнения: вопросы по настройке пула

Сомнения перед включением ProxySQL обычно одни и те же: что будет при отказе узла, нужны ли правки в коде, выдержит ли пул всплеск. Ниже — короткие ответы; на разборе задачи мы проверяем каждый пункт на вашей схеме репликации.

Что происходит, когда узел базы перестаёт отвечать?

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

Нужно ли править код приложений?

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

Выдержит ли пул всплеск нагрузки?

Соединения переиспользуются, поэтому рост числа клиентов не даёт лавину подключений к базе. На всплеске включается очередь на входе и лимит одновременных соединений на узел: избыточные запросы ждут, а не добивают сервер.

Как делится чтение и запись?

Выборки уходят на реплики, записи и всё внутри транзакций — на ведущий узел. Выборки с блокировкой распознаются как изменяющие и тоже идут на ведущий узел, поэтому приложение не получает устаревших данных.

Можно ли ограничить доступ?

Учётные записи и права выдаются на уровне пула, а прямой доступ к портам базы закрывается на межсетевом экране. В журнале видно, кто и какой запрос отправил — разбор инцидентов заметно упрощается.

Обсудим задачу и вернёмся с оценкой и планом

Для предметного разбора достаточно того, что уже есть под рукой: схема контура, конфиги и статистика за показательный период. Чем ближе присланные данные к реальному трафику, тем точнее оценка и план — без догадок и «на глазок».

С вашей стороны полезно прислать:

  • схему текущего контура: приложения, бэкенды, точки входа в базу и пути трафика;
  • текущие конфиги прокси и правила маршрутизации, включая раздачу прав и разделение читающих и пишущих узлов;
  • выгрузку статистики за период с заметной нагрузкой: пики соединений, доля отказов, время ожидания в очереди;
  • перечень известных точек роста — что уже ломалось, где лимиты поднимали вручную;
  • ограничения: окна обслуживания, требования к откату, порядок согласования изменений.

По итогам разбора вы получаете оценку и план: целевую схему пула, порядок работ по этапам, критерии приёмки и набор проверок, которые показывают результат до вывода в боевой контур. Переключение планируем без остановки сервиса — сначала настраиваем ProxySQL на копии контура, прогоняем нагрузку, затем переносим на рабочий.

Опишите задачу и пришлите то, что есть, — вернёмся с оценкой и планом.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139