Статьи

Траблшутинг без шуток: как мы лечили нестабильную репликацию MariaDB

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

Ниже — краткая выжимка того, как мы шли по проблеме, какие гипотезы проверяли и к какому итоговому сетапу в итоге пришли.

С какими симптомами столкнулись

В «рандомные» моменты времени на реплике наблюдались:
• отставание или полная остановка репликации;
• периодические зависания и перезапуски;
• нарушения порядка транзакций, приводящие к deadlock;
• в целом низкая и нестабильная производительность.

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

Гипотеза №1. Не хватает ресурсов

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

Реплике добавили ресурсы до уровня мастера: 16 vCPU / 48 GB RAM. По рекомендациям выставили: innodb_buffer_pool_size = 38G (около 80% от RAM).

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

Гипотеза №2. Слишком маленькие таймауты

В логах регулярно появлялись ошибки: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction. Это натолкнуло на мысль, что тяжёлые запросы просто не успевают отрабатывать и откатываются по таймауту.

Пробуем:
innodb_lock_wait_timeout = 120
innodb_rollback_on_timeout = 1

То есть увеличиваем время ожидания блокировок и включаем откат всей транзакции, а не только последнего запроса.

Ситуация становится чуть стабильнее, но полностью проблему это не решает.

Гипотеза №3. Параллельная репликация

Включаем и наращиваем количество потоков: slave_parallel_threads = 4, потом = 8. Без эффекта.

Напомним, как вообще устроена репликация в MariaDB:

1. IO thread читает события с мастера и пишет их в relay log.

2. SQL thread (или несколько потоков) читает события из relay log.

3. События применяются на реплике. Начиная с MariaDB 10 — параллельно в несколько потоков, что увеличивает производительность.

Гипотеза №4. Режим параллельной репликации

Пробуем разные режимы:

• optimistic (по умолчанию)
• conservative

Оба варианта проблему не решают. В итоге полностью отключаем параллельное применение: slave_parallel_threads = 0.

Гипотеза №5. Неоптимальные запросы пользователей

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

Вводим жёсткие, но спасительные ограничения:

idle_transaction_timeout = 600 (рвём соединение, если в транзакции 10 минут ничего не происходит);
max_statement_time = 900 (обрываем запросы, которые выполняются дольше 15 минут).

После этого количество «вечных» блокировок резко сократилось.

Гипотеза №6. Перебор с памятью

Под нагрузкой стало понятно, что innodb_buffer_pool_size в 80% RAM — это перебор именно для этой машины и этого профиля нагрузки. В пиках либо начинала зависать сама база, либо OOM-killer убивал другие процессы хоста (вплоть до sshd).

В итоге пришли к формуле: «80% минус примерно 2 ГБ».

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

Итоговая конфигурация

В финале сетап выглядел так:

innodb_buffer_pool_size = 36G
innodb_rollback_on_timeout = 1
idle_transaction_timeout = 600
max_statement_time = 900
innodb_lock_wait_timeout = 120
slave_parallel_threads = 0

И главный результат: за последний месяц — ни одного разрыва репликации.

Выводы

Главный вывод из этого кейса — универсального рецепта настройки репликации не существует.