장시간 실행되는 PHP 스크립트에서 MySQL 연결이 끊어지는 문제와 해결 방법을 알아보겠습니다.
SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server
class DatabaseManager {
private $pdo;
private $config;
public function __construct($config) {
$this->config = $config;
$this->connect();
}
private function connect() {
$dsn = "mysql:host={$this->config['host']};dbname={$this->config['dbname']}";
$this->pdo = new PDO($dsn, $this->config['username'], $this->config['password']);
}
public function query($sql, $params = []) {
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt;
} catch (PDOException $e) {
if ($this->isConnectionError($e)) {
$this->reconnect();
return $this->query($sql, $params); // 재시도
}
throw $e;
}
}
private function isConnectionError($e) {
return in_array($e->getCode(), ['2006', '2013']);
}
private function reconnect() {
$this->pdo = null;
$this->connect();
}
}
function keepConnectionAlive($pdo) {
try {
$pdo->query('SELECT 1');
return true;
} catch (PDOException $e) {
return false;
}
}
// 장시간 작업 시 주기적 호출
for ($i = 0; $i < 10000; $i++) {
// 매 100번째마다 연결 확인
if ($i % 100 === 0) {
if (!keepConnectionAlive($pdo)) {
// 재연결 로직
$pdo = createNewConnection();
}
}
// 실제 작업 수행
processData($i);
}
-- MySQL 설정 확인
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';
SHOW VARIABLES LIKE 'max_allowed_packet';
-- 설정 변경 (my.cnf)
-- wait_timeout = 28800
-- interactive_timeout = 28800
-- max_allowed_packet = 64M
class ConnectionPool {
private $connections = [];
private $config;
private $maxConnections = 10;
public function getConnection() {
// 사용 가능한 연결 찾기
foreach ($this->connections as $key => $conn) {
if ($this->isConnectionValid($conn)) {
return $conn;
} else {
unset($this->connections[$key]);
}
}
// 새 연결 생성
if (count($this->connections) < $this->maxConnections) {
$conn = $this->createConnection();
$this->connections[] = $conn;
return $conn;
}
throw new Exception('연결 풀 한계 초과');
}
private function isConnectionValid($pdo) {
try {
$pdo->query('SELECT 1');
return true;
} catch (PDOException $e) {
return false;
}
}
private function createConnection() {
$dsn = "mysql:host={$this->config['host']};dbname={$this->config['dbname']}";
return new PDO($dsn, $this->config['username'], $this->config['password']);
}
}
function processBatchData($data, $batchSize = 1000) {
$chunks = array_chunk($data, $batchSize);
foreach ($chunks as $chunk) {
try {
$pdo->beginTransaction();
foreach ($chunk as $item) {
// 데이터 처리
processItem($item);
}
$pdo->commit();
} catch (Exception $e) {
$pdo->rollback();
// 연결 오류 시 재연결
if (isConnectionError($e)) {
$pdo = reconnectDatabase();
}
throw $e;
}
// 메모리 정리
unset($chunk);
gc_collect_cycles();
}
}
function logConnectionStatus($pdo) {
try {
$stmt = $pdo->query('SHOW STATUS LIKE "Threads_connected"');
$result = $stmt->fetch();
error_log("MySQL 연결 수: " . $result['Value']);
} catch (PDOException $e) {
error_log("연결 상태 확인 실패: " . $e->getMessage());
}
}