Обычно сайт работает быстро, но в моменты наплыва посетителей вместо страниц начинает появляться ошибка базы данных, а в логе — запись Too many connections. Это значит, что сервер MySQL не может принять новое подключение: все «места» заняты, и следующий запрос отклоняется прямо на входе. Разберём, откуда берётся эта ошибка, как её диагностировать и что сделать, чтобы сайт оставался стабильным под нагрузкой.
Как устроена ошибка 1040
Параметр max_connections на сервере MySQL задаёт общее число подключений, которые могут быть открыты одновременно. Каждый PHP-процесс подключается к базе, чтобы сформировать страницу, и удерживает соединение до конца работы. Если число одновременно открытых подключений достигает лимита, новая попытка завершается ошибкой:
ERROR 1040 (HY000): Too many connections
Важно помнить, что эта ошибка относится ко всему серверу, а не к одному пользователю. При превышении лимита на уровне пользователя появляется другая ошибка — 1203 max_user_connections, а при исчерпании почасовой квоты — 1226. Поэтому точный код в логе — основа правильной диагностики.
Почему подключения заканчиваются
Многие понимают эту ошибку как «слишком много посетителей», но на практике причина чаще в том, что подключения подолгу остаются занятыми. Если страница формируется за 50 миллисекунд, сотня подключений способна обслужить тысячи запросов в секунду. Но если один медленный запрос блокирует таблицу, зависящие от неё запросы ждут в очереди, и каждый занимает своё подключение. В результате за несколько секунд заполняются все соединения.
Среди других частых причин — боты, без остановки запрашивающие одни и те же страницы, тяжёлые страницы категорий и фильтров без кеша, долгие скрипты экспорта или отчётов. Кроме того, если число PHP-воркеров больше лимита подключений базы, при росте нагрузки веб-сервер «заваливает» базу параллельными запросами, которые она не в состоянии обработать.
Команды для диагностики
Если у вас собственный VPS или административный доступ к базе, сначала посмотрите лимит и текущее состояние. Threads_connected показывает число открытых сейчас подключений, а Max_used_connections — максимум с момента запуска сервера. Если второе значение равно лимиту, сервер действительно упирался в потолок:
SHOW VARIABLES LIKE 'max_connections';
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
Следующий шаг — понять, чем заняты подключения. SHOW FULL PROCESSLIST выводит для каждого соединения пользователя, время выполнения и текущий запрос. Если много запросов с большим значением Time или строк в состоянии Waiting for table metadata lock, проблема не в нагрузке, а в конкретном запросе:
SHOW FULL PROCESSLIST;
-- остановить безнадёжно зависший запрос:
KILL 48213;
Сверх лимита MySQL резервирует одно дополнительное подключение для администратора с расширенными правами. Поэтому даже когда все соединения заняты, пользователь root обычно может войти и оценить ситуацию.
Увеличение лимита и очистка простаивающих подключений
Лимит можно временно поднять на работающем сервере, но после перезапуска значение сбросится. Для постоянного изменения его прописывают в конфигурационном файле MySQL. Полезно также уменьшить wait_timeout: по умолчанию простаивающее соединение может оставаться открытым часами и впустую занимать место:
# временно, до перезапуска
SET GLOBAL max_connections = 300;
# постоянно: /etc/mysql/my.cnf или /etc/my.cnf, секция [mysqld]
[mysqld]
max_connections = 300
wait_timeout = 120
# затем перезапуск (имя службы зависит от системы)
sudo systemctl restart mysql
Бездумно увеличивать лимит тоже опасно. Каждое подключение занимает память под свои буферы, поэтому на сервере с небольшим объёмом памяти и разрешённой тысячей подключений при пике нагрузки MySQL может упасть из-за нехватки памяти. Лимит нужно оценивать вместе с объёмом оперативной памяти и innodb_buffer_pool_size. На виртуальном хостинге эти настройки пользователь не меняет — ими управляет провайдер.
Что можно сделать на стороне приложения
Самое эффективное решение часто состоит в том, чтобы сократить сами обращения к базе. Следующие меры дают заметный результат даже без изменения настроек сервера:
- Включите кеш страниц: страница из кеша вообще не обращается к базе.
- Проанализируйте лог медленных запросов и добавьте индексы для самых тяжёлых — подключения будут освобождаться быстрее.
- Ограничьте лишних ботов на уровне сайта, особенно тех, что обходят страницы фильтров и поиска.
- Перенесите тяжёлые выгрузки и отчёты на ночные часы с минимальным трафиком.
- Согласуйте число PHP-воркеров с лимитом подключений базы, иначе при пике они её перегрузят.
Пример: день объявления результатов экзаменов
Сайт частной школы в Ташкенте misol.uz весь год работал без проблем. Но в день объявления результатов вступительных экзаменов тысячи родителей одновременно открыли страницу результатов, и через несколько минут сайт начал выдавать Too many connections. Проверка показала, что при каждом открытии страница перебирала всю таблицу по столбцу без индекса, и каждый запрос длился несколько секунд.
После добавления индекса время запроса упало до миллисекунд, а страницу результатов поставили в короткий кеш. Лимит подключений не меняли, но ошибка больше не возвращалась: каждое соединение теперь освобождалось мгновенно. Этот пример хорошо показывает, почему прежде чем повышать лимит, нужно выяснить, чем заняты подключения. Если сайт размещён на хостинге sayt.uz и ошибка повторяется, откройте тикет в техподдержку, указав время и адрес страницы.
Подготовка заранее
Подключения чаще всего заканчиваются во время заранее известных событий: акций, объявления результатов, рекламных кампаний. Перед такими днями стоит прогнать самые посещаемые страницы простым нагрузочным тестом, проверить работу кеша и исправить медленные запросы — тогда сайт не откажет именно в тот момент, когда он нужнее всего.