PHP MySQL 연결 끊김 문제 해결

프리터코더·2025년 5월 25일
0

php 문제 해결

목록 보기
22/79

장시간 실행되는 PHP 스크립트에서 MySQL 연결이 끊어지는 문제와 해결 방법을 알아보겠습니다.

주요 오류 메시지

SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server

원인 분석

  1. wait_timeout 초과: MySQL 서버의 비활성 연결 타임아웃
  2. max_allowed_packet 초과: 전송 데이터 크기 제한
  3. 장시간 실행: 긴 작업으로 인한 연결 유지 실패

해결책

1. 연결 상태 확인 및 재연결

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();
    }
}

2. 연결 유지 (Keep Alive)

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);
}

3. MySQL 설정 최적화

-- 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

4. 연결 풀링 구현

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']);
    }
}

5. 배치 작업 최적화

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());
    }
}

예방법

  • MySQL 타임아웃 설정 조정
  • 연결 상태 주기적 확인
  • 적절한 배치 크기 사용
  • 연결 풀링 도입 검토
  • 장시간 작업 시 중간 체크포인트 설정
profile
일용직 개발자. freetercoder@gmail.com

0개의 댓글