데이터베이스 데드락은 두 개 이상의 트랜잭션이 서로의 락을 기다리며 무한 대기 상태에 빠지는 문제입니다. PHP 애플리케이션에서 자주 발생하는 이 문제의 해결책들을 알아보겠습니다.
테이블 접근 순서를 일관되게 유지하여 데드락을 방지합니다.
// 잘못된 예 - 순서가 다름
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();
}
긴 트랜잭션으로 인한 데드락을 방지합니다.
$pdo = new PDO($dsn, $username, $password, [
PDO::ATTR_TIMEOUT => 30,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION innodb_lock_wait_timeout = 10"
]);
데드락 발생 시 자동으로 재시도하는 메커니즘을 구현합니다.
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;
}
}
}
트랜잭션 범위를 최소화하여 락 시간을 줄입니다.
// 좋은 예 - 짧은 트랜잭션
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;
}
}
인덱스를 통해 락 범위를 최소화합니다.
-- 자주 조회되는 컬럼에 인덱스 추가
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_order_status ON orders(status, created_at);
불필요한 락을 줄이기 위해 적절한 격리 수준을 설정합니다.
$pdo->exec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED");
대량 데이터 처리 시 작은 단위로 나누어 처리합니다.
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 애플리케이션에서 데이터베이스 데드락 문제를 효과적으로 예방하고 해결할 수 있습니다.