Отличный вопрос! Ошибка 2006 в MySQL — это одна из самых распространенных и раздражающих проблем, особенно когда, казалось бы, настроены большие таймауты. Давайте разберем это подробно.
Что такое ошибка MySQL 2006?
MySQL error 2006 (CRSERVERGONE_ERROR) — "MySQL server has gone away" — означает, что соединение с сервером базы данных было разорвано. Клиент (ваш PHP-скрипт) больше не может использовать это соединение для выполнения запросов.
Основные причины при timeout 28800
Даже при wait_timeout = 28800
(8 часов) ошибка может возникать по следующим причинам:
1. Таймаут неактивности (Idle Timeout)
// Пример: скрипт выполняется долго между запросами $pdo = new PDO($dsn, $user, $password); // Первый запрос $stmt = $pdo->query("SELECT * FROM large_table"); // Долгая обработка данных (например, 30+ минут) process_large_dataset($data); // Второй запрос - ВОЗМОЖНА ОШИБКА 2006! $stmt = $pdo->query("UPDATE another_table SET status = 1");
Решение:
// Проверка соединения перед использованием function checkConnection($pdo) { try { $pdo->query("SELECT 1"); return true; } catch (PDOException $e) { return false; } } // Или переподключение при необходимости if (!checkConnection($pdo)) { $pdo = new PDO($dsn, $user, $password); }
2. Размер пакета (maxallowedpacket)
// Большие вставки данных $largeData = generate_large_blob(); // > max_allowed_packet $stmt = $pdo->prepare("INSERT INTO files (data) VALUES (?)"); $stmt->bindParam(1, $largeData, PDO::PARAM_LOB); $stmt->execute(); // ОШИБКА 2006 если данные слишком большие
Проверка и решение:
-- Проверить текущее значение SHOW VARIABLES LIKE 'max_allowed_packet'; -- Временное увеличение (в байтах) SET GLOBAL max_allowed_packet = 64*1024*1024; -- 64MB
3. Проблемы с сетью
// Нестабильное соединение между веб-сервером и БД $pdo = new PDO($dsn, $user, $password); // Сеть прерывается на несколько секунд... $stmt = $pdo->query("SELECT * FROM table"); // ОШИБКА 2006
Решение:
// Использование механизма повторных попыток function executeWithRetry($pdo, $sql, $maxRetries = 3) { for ($attempt = 1; $attempt <= $maxRetries; $attempt++) { try { return $pdo->query($sql); } catch (PDOException $e) { if ($e->getCode() == 2006 && $attempt < $maxRetries) { // Переподключение и повторная попытка $pdo = reconnectDatabase(); sleep(1); continue; } throw $e; } } }
4. Долгие выполняющиеся запросы
// Запрос выполняется дольше, чем interactive_timeout $stmt = $pdo->query(" SELECT * FROM huge_table WHERE complex_condition ORDER BY multiple_columns "); // Выполняется 10 минут // Следующий запрос может вызвать ошибку 2006
Решение:
-- Проверить разные таймауты SHOW VARIABLES LIKE '%timeout%'; -- Увеличить при необходимости SET GLOBAL interactive_timeout = 28800; SET GLOBAL wait_timeout = 28800;
5. Проблемы на стороне сервера БД
- Перезагрузка MySQL сервера
- Завершение процесса mysqld
- Исчерпание памяти (OOM killer)
- Проблемы с диском
Комплексное решение для PHP
1. Настройка соединения с проверками
class RobustDBConnection { private $pdo; private $dsn, $user, $password; public function __construct($dsn, $user, $password) { $this->dsn = $dsn; $this->user = $user; $this->password = $password; $this->connect(); } private function connect() { $this->pdo = new PDO($this->dsn, $this->user, $this->password, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 30, PDO::ATTR_PERSISTENT => false // Лучше избегать persistent connections ]); } public function query($sql, $params = []) { try { if (!empty($params)) { $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt; } else { return $this->pdo->query($sql); } } catch (PDOException $e) { if ($e->getCode() == 2006) { // Переподключение и повтор $this->connect(); return $this->query($sql, $params); } throw $e; } } }
2. Настройки MySQL для предотвращения
-- В my.cnf или через SET GLOBAL [mysqld] wait_timeout = 28800 interactive_timeout = 28800 max_allowed_packet = 64M net_read_timeout = 120 net_write_timeout = 120
3. Мониторинг и диагностика
// Функция диагностики function diagnoseConnection($pdo) { try { // Проверка базового соединения $pdo->query("SELECT 1"); echo "✓ Базовое соединение работаетn"; // Проверка таймаутов $stmt = $pdo->query("SHOW VARIABLES WHERE Variable_name IN ('wait_timeout', 'interactive_timeout', 'max_allowed_packet')"); $vars = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); echo "✓ wait_timeout: " . $vars['wait_timeout'] . "n"; echo "✓ interactive_timeout: " . $vars['interactive_timeout'] . "n"; echo "✓ max_allowed_packet: " . $vars['max_allowed_packet'] . "n"; } catch (PDOException $e) { echo "✗ Ошибка: " . $e->getMessage() . " (код: " . $e->getCode() . ")n"; } }
Практические рекомендации
- Используйте пулы соединений через PDO или mysqli
- Реализуйте механизм повторных попыток для критически важных операций
- Логируйте ошибки соединения для последующего анализа
- Мониторьте нагрузку на БД и сетевую инфраструктуру
- Используйте транзакции аккуратно - не держите их открытыми долго
Пример полного решения
class Database { private static $instance = null; private $pdo; public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } private function __construct() { $this->connect(); } private function connect() { $this->pdo = new PDO( 'mysql:host=localhost;dbname=test;charset=utf8mb4', 'username', 'password', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 30, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ); } public function execute($sql, $params = []) { try { $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt; } catch (PDOException $e) { if (in_array($e->getCode(), [2006, 2013])) { // 2006 или 2013 (server gone/closed) error_log("Переподключение к БД из-за ошибки: " . $e->getMessage()); $this->connect(); return $this->execute($sql, $params); // Повторная попытка } throw $e; } } } // Использование $db = Database::getInstance(); $result = $db->execute("SELECT * FROM users WHERE active = ?", [1]);
Ошибка 2006 при больших таймаутах обычно указывает на проблемы с сетью, размером данных или конфигурацией сервера. Систематический подход к диагностике и правильная обработка ошибок в коде помогут решить эту проблему.