PHP에서 DB Deadlock 문제와 해결책

프리터코더·2025년 6월 15일
0

php 문제 해결

목록 보기
69/79

데이터베이스 데드락은 두 개 이상의 트랜잭션이 서로의 락을 기다리며 무한 대기 상태에 빠지는 문제입니다. PHP 애플리케이션에서 자주 발생하는 이 문제의 해결책들을 알아보겠습니다.

1. 트랜잭션 순서 통일

테이블 접근 순서를 일관되게 유지하여 데드락을 방지합니다.

// 잘못된 예 - 순서가 다름
function transferMoney($fromId, $toId, $amount) {
    $pdo->beginTransaction();
    
    // 항상 ID 순서대로 락 획득
    $minId = min($fromId, $toId);
    $maxId = max($fromId, $toId);
    
    $stmt1 = $pdo->prepare("SELECT * FROM accounts WHERE id = ? FOR UPDATE");
    $stmt1->execute([$minId]);
    
    $stmt2 = $pdo->prepare("SELECT * FROM accounts WHERE id = ? FOR UPDATE");
    $stmt2->execute([$maxId]);
    
    // 실제 업데이트 로직
    $pdo->commit();
}

2. 트랜잭션 타임아웃 설정

긴 트랜잭션으로 인한 데드락을 방지합니다.

$pdo = new PDO($dsn, $username, $password, [
    PDO::ATTR_TIMEOUT => 30,
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION innodb_lock_wait_timeout = 10"
]);

3. 데드락 재시도 로직

데드락 발생 시 자동으로 재시도하는 메커니즘을 구현합니다.

function executeWithRetry($callback, $maxRetries = 3) {
    $attempt = 0;
    
    while ($attempt < $maxRetries) {
        try {
            return $callback();
        } catch (PDOException $e) {
            if ($e->getCode() == '40001' && $attempt < $maxRetries - 1) {
                $attempt++;
                usleep(rand(100000, 500000)); // 랜덤 대기
                continue;
            }
            throw $e;
        }
    }
}

4. 짧은 트랜잭션 유지

트랜잭션 범위를 최소화하여 락 시간을 줄입니다.

// 좋은 예 - 짧은 트랜잭션
function updateUserProfile($userId, $data) {
    // 트랜잭션 외부에서 검증
    $this->validateUserData($data);
    
    $pdo->beginTransaction();
    try {
        // 최소한의 작업만 트랜잭션 내에서
        $stmt = $pdo->prepare("UPDATE users SET name = ?, email = ? WHERE id = ?");
        $stmt->execute([$data['name'], $data['email'], $userId]);
        $pdo->commit();
    } catch (Exception $e) {
        $pdo->rollback();
        throw $e;
    }
}

5. 적절한 인덱스 사용

인덱스를 통해 락 범위를 최소화합니다.

-- 자주 조회되는 컬럼에 인덱스 추가
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_order_status ON orders(status, created_at);

6. READ COMMITTED 격리 수준 사용

불필요한 락을 줄이기 위해 적절한 격리 수준을 설정합니다.

$pdo->exec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED");

7. 배치 처리 시 청크 단위 처리

대량 데이터 처리 시 작은 단위로 나누어 처리합니다.

function processBulkData($data) {
    $chunks = array_chunk($data, 100);
    
    foreach ($chunks as $chunk) {
        $pdo->beginTransaction();
        try {
            foreach ($chunk as $item) {
                $this->processItem($item);
            }
            $pdo->commit();
        } catch (Exception $e) {
            $pdo->rollback();
            throw $e;
        }
        
        // 청크 간 잠시 대기
        usleep(10000);
    }
}

이러한 해결책들을 적절히 조합하여 사용하면 PHP 애플리케이션에서 데이터베이스 데드락 문제를 효과적으로 예방하고 해결할 수 있습니다.

profile
일용직 개발자. freetercoder@gmail.com

0개의 댓글