Ошибки "server has gone away" редко встречаются в обычных php-проектах со стандартными настройками сервера и базы, но зато там, где они могут образоваться, разработчики зачастую не предусматривают большинства подобных ситуаций. В чем же причина таких ошибок?

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

  • Вы используете persistent connection к mysql и одновременно с этим произошло что-то из следующего:

    • Соединение было открыто и длительное время бездействовало. Сервер закрыл соединение по таймауту, а php узнал об этом только когда попытался сделать запрос.

    • Сервер mysql был перезапущен или соединение со стороны сервера было "убито" вручную. В обоих случаях нужно переоткрыть соединение с mysql.

  • Вы выполнили fork php-процесса, но пытаетесь пользоваться "старым соединением", полученным от родительского процесса. Соединение с БД нужно просто переоткрыть.

  • Между сервером с php и сервером mysql упала/"моргнула" сеть или начал работать некорректно настроенный фаервол. Проверить качество соединения можно, если со стороны сервера с php с помощью консольного mysql-клиента соединиться с сервером и сделать любой запрос к базе - если ответ получен, значит, связь между серверами как минимум есть и работает.

  • Так же часто такая ситуация бывает и при использовании балансировщика запросов между несколькими серверами mysql.

Универсального решения этой ошибки не существует, но для ситуаций простых я дам следующий набор решений:

  • Не используйте persistent connection на сайтах, на которых нет нагрузки. Будет печально, если единственный за день пришедший пользователь получит 500-ую ошибку вместо сайта из-за того, что соединение висело 8 часов без дела и "умерло" по таймауту.

  • В ситуациях с fork и при написании демонов на php переоткрывайте соединение при подобной ошибке.

Пример обработки обрыва соединения и переоткрытия для фреймворка Yii 1.1.x

...
function query($query, $noretry = false)
  try {
    return Yii::app()->db->query($query);
  } catch (\Exception $e) {
    if (!$noretry && preg_match('/gone away/',$e->getMessage()) {
       Yii::app()->db->close();
       Yii::app()->db->open();
       return $this->query($query, true);
    } else {
       throw $e;
    }
  }
}
...

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