Настройка MaxScale: где рвётся пул соединений к MySQL
Типовая картина выглядит так: приложение открывает соединение под каждый запрос и закрывает сразу после ответа — на пике это сотни коротких сессий в секунду. Бэкенд упирается в max_connections, заполняется очередь ожидания на входе, часть запросов возвращается отказом, а обработчики приложения держат потоки в ожидании освободившегося слота. Внешне это выглядит как «база тормозит», хотя узкое место — не запросы, а их доставка до сервера.
Поднять max_connections — путь в тупик. Каждое соединение резервирует память под стек потока и буферы сортировки и соединений, поэтому лимит упирается в память раньше, чем в процессор, а переключение контекста между сотнями активных потоков съедает выигрыш. Проблема ещё и в том, что большинство этих соединений в любой момент простаивают: приложение их уже открыло, но запрос через них пока не пошло.
MaxScale встаёт между приложением и MySQL как прокси-слой и переиспользует бэкенд-соединения: в простое клиентские сессии разделяют одни и те же подключения к серверу, а не занимают своё. Параметр max_connections в секции service задаёт, сколько соединений к бэкенду прокси держит одновременно, multiplex_timeout — сколько он ждёт перед возвратом освободившегося подключения в общий набор. Настройка MaxScale сводится к тому, чтобы очереди соединений формировались на входе в прокси, где их можно измерить, а не в виде отказов внутри базы.
Отдельно синхронизируем таймауты: wait_timeout и back_log на сервере должны быть не короче connection_timeout и multiplex_timeout в прокси, иначе MaxScale отдаёт соединение, которое бэкенд уже закрыл, и клиент получает ошибку на ровном месте. Разбираем, как это настраивается.
Что даёт управляемый пул соединений на практике
Пул соединений работает как буфер между приложением и базой: сервисы держат небольшой набор каналов к прокси, а сам прокси — уже установленные соединения к узлам. Вместо того чтобы каждый процесс открывал собственный канал, трафик переиспользует готовые. На типовом сервисе число одновременных соединений к базе падает в разы при том же объёме запросов.
На пике эффект заметнее всего. Новый запрос получает соединение моментально, без повторного согласования и аутентификации, поэтому всплеск трафика не превращается в очередь на подключение. База видит ровный поток и в спокойные часы, и в пик — ступенька из сотен одновременных подключений исчезает.
Отдельный пункт — переключение узлов базы. При отказе реплики или плановом обслуживании прокси сам уводит новые транзакции на живой узел, а приложение продолжает работать с тем же адресом. Правки в конфигурациях сервисов и рестарт не нужны.
Отсюда уходит целый класс отказов: ошибки превышения лимита соединений, всплески задержек при массовом переподключении, обрывы во время ручного переключения узлов. Проверять результат стоит по трём числам — сколько соединений база видит в пике, сколько длится переключение, сколько ошибок подключения осталось за сутки. MaxScale закрывает все эти пункты на уровне прокси, без изменений в коде приложений.
Варианты балансировки и режимы маршрутизации запросов
Маршрутизация и балансировка решают разные задачи: первая определяет, куда уйдёт конкретный запрос, вторая распределяет нагрузку между узлами. В MaxScale это делают модули: readwritesplit разбирает тип запроса и уводит чтение с реплик, readconnroute раздаёт соединения целиком, hintrouter позволяет приложению задать цель прямо в комментарии к запросу.
Выбор режима определяет поведение транзакций, временных таблиц и чтения собственных записей после записи. Отдельно решается, где держать пул соединений — в прокси, в драйвере или вовсе без него.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Внешний пул соединений в прокси | Разнородные приложения, много коротких подключений | Дополнительный сетевой переход; проверять совместимость с подготовленными выражениями |
| Пул в драйвере приложения | Одно приложение, стабильная нагрузка, важна минимальная задержка | Лимиты считаются на процесс, нет общего перераспределения |
| Прокси на уровне приложения | Нужны собственные правила маршрутизации | Своя поддержка и тесты, отставание от изменений в СУБД |
| Прямое подключение к репликам | Мало узлов, нет жёстких требований к свежести данных | Отставание реплик и переключение при отказе — на стороне кода |
На практике режимы комбинируют: запись идёт на мастер, отчётные выборки — на реплики, а запросы, чувствительные к свежести данных, адресуют мастеру явным указанием. Перед включением прогоняем сценарии со сменой схемы в сессии, сессионными переменными и откатом транзакций — на них прозрачная балансировка ломается чаще всего.
Этапы настройки MaxScale: от аудита нагрузки до передачи
Работы идут рядом с боевым контуром, поэтому порядок здесь важнее скорости: каждый шаг оставляет систему в предсказуемом состоянии. Ниже — маршрут от первого замера до передачи дежурной смене.
- Аудит нагрузки базы. Снимаем реальный профиль соединений: пики за сутки и неделю, распределение по приложениям, доля коротких и долгих сессий. На выходе — обоснованный размер пула вместо значения «на глаз».
- Схема маршрутизации. В MaxScale разделяем потоки на чтение и запись, определяем состав реплик и признак, по которому узел выводится из ротации при отставании. Схема фиксируется до правок конфигурации.
- Настройка лимитов. Задаём предел одновременных соединений на узел, время удержания простаивающей сессии и поведение при переполнении — очередь с ограничением либо быстрый отказ.
- Копия боевого контура. Стенд повторяет топологию и роли узлов, поэтому результаты проверок переносятся на боевую систему без поправок.
- Проверка под нагрузкой. Прогоняем снятый профиль и отдельно ломаем систему: гасим реплику, перезапускаем узел, рвём соединение на середине запроса. Смотрим длительность отказа и скорость возврата трафика.
- Передача в эксплуатацию. Отдаём метрики пула и отказов, пороги оповещений, регламент плановых переключений и порядок отката на прежнюю схему.
Этап закрыт не после запуска, а после того, как дежурная смена провела по нашей схеме плановое переключение.
Что передаём на руки: конфигурации, схемы, регламенты
Проект закрывается не отчётом о выполненной работе, а набором артефактов, по которым ваша команда эксплуатирует пул соединений без нашего участия. Ниже — что остаётся на руках после настройки MaxScale: рабочие файлы, схемы, доступы и регламенты, каждый с пояснением, зачем он нужен и что в нём менять при росте нагрузки.
- Рабочие конфигурации пула соединений — файлы сервисов и слушателей с лимитами соединений, таймаутами ожидания и поведением при исчерпании пула; параметры прокомментированы, чтобы значение менялось осознанно.
- Схемы маршрутизации — какие узлы принимают запись, какие чтение, как распределяются запросы между репликами и что происходит, когда реплика отстаёт или выпадает.
- Правила доступа к базе — отдельные учётные записи под каждый сервис, разделение прав на чтение и запись, ротация паролей и порядок выдачи доступа новому сервису.
- Каталог типовых сбоев — «пул исчерпан», «узел выпал из кластера», «реплика отстала»: симптом, вероятная причина, действие дежурного, признак восстановления.
- Инструкции для дежурной смены — пошаговые сценарии проверки здоровья узлов, ручного переключения на резерв и порядок эскалации, если сценарий не сработал.
- Регламент эксплуатации — как менять лимиты пула, когда расширять число соединений, какие метрики смотреть ежедневно и какие пороги считать тревожными.
- Панель метрик и оповещения — занятость пула, число отказов подключения, задержка репликации; оповещения настроены на пороги, а не на факт падения.
Конфигурации хранятся в репозитории с историей изменений — откат на предыдущее состояние занимает минуты и не требует разбора вслепую.
Разбор внедрения: пик нагрузки и очереди соединений
Клиент — платёжный сервис с неравномерным трафиком: к вечеру и в дни акций одновременных пользователей становилось втрое больше. Приложение держало соединения с базой напрямую, у каждого экземпляра был свой пул, и в пик к серверу открывалось до девятисот соединений при потолке в пятьсот. Ожидание слота доходило до четырёх секунд, часть запросов обрывалась по таймауту, а о проблеме дежурный узнавал от пользователей.
Мы вынесли управление соединениями на отдельный слой — MariaDB MaxScale в режиме чтения-записи: приложения ходят к прокси, а он держит общий пул к базе, разделяя входящие сессии и реальные соединения. Соединения переиспользуются между запросами, лишние закрываются по простою, а не висят до перезапуска приложения.
Метрики до и после
| Показатель | До | После |
|---|---|---|
| Соединений к базе в пик | ~900 | 120 |
| Ожидание слота подключения | до 4 секунд | десятые доли секунды |
| Запросы, оборванные по таймауту | 6-7% | меньше 0,1% |
| Отказ узла базы | ручное переключение | автоматически, за секунды |
Отказ узла проверяли отдельно: гасили реплику под нагрузкой. Прокси снял её с балансировки, живые сессии перераспределились — приложение не увидело разрыва, ручных переключений и повторов запросов не потребовалось.
Чем мультиплексирование отличается от пула соединений в коде?
Пул соединений в приложении решает одну задачу — не открывать соединение на каждый запрос. Подключение живёт внутри процесса, возвращается в пул и переиспользуется следующим обработчиком. Верхняя граница задана заранее: столько соединений держит одна копия сервиса, и она же их расходует.
Мультиплексирование соединений работает на другом уровне — между приложением и базой. Прокси, в нашем случае MaxScale, держит собственный набор сессий к серверу и распределяет их между клиентскими подключениями. Пока запрос ждёт ответа или приложение занято вычислениями, его серверная сессия отдаётся другому клиенту — простой перестаёт занимать ресурс базы.
Разница становится заметной при многих копиях сервиса. Каждая копия со своим пулом держит отдельный набор соединений: масштабирование вверх увеличивает их число линейно, база упирается в предельное количество подключений и расходует память на каждое. Мультиплексирование разрывает эту зависимость — число сессий к базе определяется одновременной нагрузкой, а не количеством копий сервиса.
Сравнение подходов не сводится к «одно вместо другого». Мультиплексирование несовместимо с состоянием на уровне сессии: временные таблицы, пользовательские переменные, блокировки и транзакции требуют привязки соединения к клиенту на время работы. Прокси выделяет такому клиенту отдельную сессию, а после завершения возвращает её в общий оборот. Пул в приложении снимает накладные расходы на открытие соединения, мультиплексирование — на его простой, поэтому в рабочей схеме они стоят вместе.
Частые технические вопросы по пулу соединений
Пул соединений кажется простым, пока узел не отваливается в середине транзакции. Сомнения обычно не про сам пул, а про поведение при отказе и при переключении записи на реплику.
Часть вопросов закрывается конфигурацией прокси и параметрами драйвера, остаток требует правок в коде. Ниже — то, что чаще всего всплывает до начала работ.
Что происходит с открытой транзакцией, если узел перестал отвечать?
Транзакция не переносится на другой узел: прокси разрывает сессию, приложение получает ошибку и повторяет операцию целиком. Поэтому задаём время ожидания и следим, чтобы операции были идемпотентны.
Нужно ли менять код приложения?
Обычно нет: приложение подключается к адресу прокси, драйвер и запросы остаются прежними. Правки нужны, если сервис сам выбирает узел для чтения или держит одно соединение на весь жизненный цикл.
Как чтение уходит на реплики?
Правилом маршрутизации: запись идёт на мастер, чтение — на реплики. Отдельно настраиваем поведение при отставании реплики, чтобы после записи приложение не получило устаревшие данные.
Как диагностировать остаточные соединения и очереди?
На прокси видно число сессий к каждому узлу, длину очереди и время ожидания в ней. Очередь, которая не рассасывается после переключения, — признак, что размер пула не совпадает с нагрузкой.
Можно ли откатить изменения?
Конфигурация лежит в системе контроля версий: возврат к предыдущему набору правил — одна команда, без перезапуска приложения.
Обсудим задачу: подберём схему пула под вашу нагрузку
Пул соединений — это не один параметр, а выбор схемы: как трафик распределяется между мастером и репликами, что делать с транзакциями и длинными запросами, как ведёт себя приложение при отказе узла. Чтобы обсудить задачу предметно, достаточно прислать вводные — остальное уточним сами и вернёмся с оценкой.
Что прислать для первичного разбора:
- приложения-клиенты: сколько их, как подключаются, есть ли внешний балансировщик или соединения идут напрямую в базу;
- профиль нагрузки: доля чтения и записи, самые частые и самые долгие запросы, пиковые окна;
- топология: число реплик, отставание репликации, требование читать свои данные сразу после записи;
- симптомы: обрывы соединений, рост спящих сессий, ошибки при переключении мастера;
- ограничения: окно обслуживания, требования к доступности, правила доступа и аудита.
Мы уточняем подбор схемы маршрутизации: разделение чтения и записи, балансировку соединений, закрепление сессии за узлом, таймауты и поведение при отказе мастера.
В ответ вы получаете оценку и план работ: целевую топологию, конфигурацию пула и маршрутизации, этапы внедрения без простоя и критерии приёмки — долю запросов, ушедших на реплики, время переключения при отказе, число разорванных соединений на пике. Дальше — сопровождение базы: мониторинг метрик пула и плановые изменения под рост нагрузки. Опишите задачу — вернёмся с оценкой и планом.







