Daeho's Dev Blog

트랜잭션 롤백이 안 먹힐 때 확인할 것 — MyISAM

삽질기#MySQL#MyISAM#InnoDB#transaction#PHP

트랜잭션 걸고 INSERT 하다가 에러 나면 롤백되게 짰는데, 아무리 에러를 일부러 터뜨리고 뭔 짓을 해도 데이터가 들어감. 코드는 이랬음.

public function insertData_trans2(array $data, string $tablename): int
{
    $cnt = 0;
    $this->beginTransaction();
    try {
        foreach ($data as $row) {
            $cnt += $this->insertData($row, $tablename);
        }
        $this->commit();
    } catch (Exception $e) {
        $this->rollback();
        throw $e;
    }
    return $cnt;
}

public function insertData(array $data, string $tablename)
{
    $columns = implode(", ", array_keys($data));
    $placeholders = implode(", ", array_fill(0, count($data), "?"));
    $values = array_values($data);
    $stmt = $this->conn->prepare("INSERT INTO $tablename ($columns) VALUES ($placeholders)");
    $stmt->execute($values);
    return $stmt->rowCount();
}

beginTransaction도 있고 catch에서 rollback도 하고, 코드엔 문제가 없음. 근데 롤백을 해도 이미 들어간 데이터가 그대로 남아 있음. 코드만 계속 의심하면서 삽질하던 찰나.

범인은 테이블 엔진

테이블이 MyISAM으로 되어 있었음… MyISAM은 트랜잭션을 지원하지 않음. beginTransaction()을 하든 말든 쿼리가 실행되는 즉시 반영되고, rollback은 조용히 무시됨. 에러도 안 내주니까 코드 탓인 줄 알고 삽질하게 되는 거.

엔진 확인은 이렇게.

SHOW TABLE STATUS WHERE Name = 'tablename';
-- Engine 컬럼 확인

InnoDB로 변경.

ALTER TABLE tablename ENGINE = InnoDB;

변경하니까 롤백 잘됨.

정리

  • 트랜잭션이 안 먹으면 코드보다 테이블 엔진 먼저 확인. MyISAM은 트랜잭션 미지원.
  • MyISAM에서 rollback은 에러 없이 조용히 무시됨. 이게 제일 악질임.
  • ALTER TABLE ... ENGINE = InnoDB로 변경하면 해결.