본문 바로가기
카테고리 없음

운영 중 MySQL 테이블의 컬럼을 제거했을 때 생길 수 있는 에러

by 노력남자 2025. 7. 11.
반응형

1.1. Table definition has changed, please retry transaction: MVCC와 트랜잭션 스냅샷의 충돌

이 에러는 데이터베이스의 동시성 제어 메커니즘과 스키마 변경 작업 간의 근본적인 충돌로 인해 발생합니다. 특히 MySQL의 InnoDB 스토리지 엔진과 REPEATABLE-READ 격리 수준에서 두드러지게 나타납니다.

핵심 개념: MySQL(InnoDB)의 REPEATABLE-READ 격리 수준과 MVCC

MySQL의 기본 트랜잭션 격리 수준인 REPEATABLE-READ는 트랜잭션의 일관성을 보장하기 위해 MVCC(Multi-Version Concurrency Control)라는 기술을 사용합니다. 트랜잭션이  

 

BEGIN 또는 START TRANSACTION으로 시작될 때, InnoDB는 그 시점의 데이터베이스에 대한 '스냅샷(snapshot)'을 생성합니다. 이 스냅샷 덕분에 트랜잭션이 진행되는 동안 다른 트랜잭션에 의해 데이터가 변경되더라도, 해당 트랜잭션은 자신이 시작된 시점의 일관된 데이터만을 보게 됩니다. 이 스냅샷에는 테이블의 데이터뿐만 아니라 테이블의 구조, 즉 메타데이터 정보도 포함됩니다.

그러나 ALTER TABLE, DROP COLUMN과 같은 DDL(Data Definition Language) 문은 일반적인 DML(INSERT, UPDATE, DELETE)과 근본적으로 다르게 처리됩니다. DDL은 MVCC의 대상이 아니며, 테이블의 구조 자체를 영구적으로 변경하는 작업입니다. 이러한 작업은 원자적으로 실행되어 성공 시 즉시 커밋됩니다. 바로 이 지점에서 DDL 실행 전 시작된 트랜잭션의 '과거의 스냅샷'과 DDL 실행 후의 '현재의 테이블 정의' 사이에 불일치가 발생할 수 있는 조건이 형성됩니다.  

 

에러 발생 메커니즘

이 에러가 발생하는 과정은 다음과 같은 타임라인으로 재구성할 수 있습니다.

 

  1. 트랜잭션 A 시작: 애플리케이션이 특정 작업을 위해 트랜잭션을 시작합니다 (BEGIN). InnoDB는 이 시점의 데이터베이스 스냅샷을 생성합니다. 이 스냅샷의 메모리에는 아직 삭제되지 않은 컬럼을 포함한 테이블의 정의가 기록되어 있습니다.
  2. DDL 실행 및 커밋: 다른 세션에서 관리자가 ALTER TABLE... DROP COLUMN 명령을 실행합니다. 이 DDL 명령은 필요한 잠금을 획득한 후 성공적으로 완료되고, 데이터베이스 시스템에 즉시 커밋됩니다. 이로 인해 데이터 딕셔너리에 저장된 공식적인 테이블 정의에서 해당 컬럼이 영구적으로 제거됩니다.  
  3. 트랜잭션 A의 후속 쿼리: 아직 커밋되지 않은 트랜잭션 A가 동일한 테이블에 대해 추가적인 SELECT나 UPDATE 같은 쿼리를 실행하려고 시도합니다.
  4. 불일치 감지 및 에러 발생: InnoDB 엔진은 트랜잭션 A의 쿼리를 처리하기에 앞서, 트랜잭션 A가 보유한 스냅샷의 테이블 정의와 현재 데이터베이스의 실제 테이블 정의를 비교합니다. 이 과정에서 컬럼이 사라진 것을 감지하게 됩니다. 오래된 스키마 정보를 기반으로 존재하지 않는 컬럼에 접근하거나 데이터를 조작하려는 시도는 데이터 정합성을 심각하게 훼손할 수 있으므로, InnoDB는 이를 방지하기 위한 안전장치를 발동합니다. 즉, 트랜잭션 A를 강제로 롤백시키고 ERROR 1412 (HY000): Table definition has changed, please retry transaction 에러를 클라이언트에게 반환합니다.  

 

결론적으로 이 에러는 버그가 아니라, 데이터베이스가 스스로의 데이터 무결성을 보호하기 위한 의도된 동작입니다. 오래된 스냅샷을 기반으로 한 위험한 작업을 원천적으로 차단하는 것입니다.

ER_TABLE_DEF_CHANGED의 더 깊은 의미

이 에러 메시지는 단순히 '테이블 구조가 바뀌었다'는 표면적인 사실을 넘어, 데이터베이스 아키텍처의 두 가지 핵심 원칙 사이의 근본적인 충돌을 드러냅니다. 바로 '트랜잭션의 시간적 일관성(Snapshot Isolation)과 스키마 변경의 즉시성(DDL Atomicity)' 사이의 충돌입니다.

MVCC의 목표는 각 트랜잭션에게 "세상은 내가 시작했을 때의 모습 그대로다"라는 환상을 제공하는 것입니다. 이를 통해 동시성을 높이면서도 일관된 결과를 보장합니다. 반면, DDL은 관리자급의 강력한 명령으로, 데이터베이스의 물리적, 논리적 구조를 직접 수정하여 이 환상을 깨뜨립니다. MySQL의 InnoDB 엔진은 이 두 상충하는 개념이 만났을 때, 데이터 무결성을 최우선으로 선택합니다. 즉, 오래된 환상(스냅샷)을 기반으로 새로운 현실(변경된 스키마)에 접근하려는 위험한 시도를 거부하고 해당 트랜잭션을 실패시키는 것입니다. 따라서 이 에러는 개발자와 운영자가 DDL과 장기 실행 트랜잭션(long-running transaction)을 함께 운영할 때의 내재된 위험성을 명확히 인지해야 함을 시사하는 중요한 신호입니다.  

 

1.2. The active SQL connection has changed...: 잠금 대기(Lock Wait)와 커넥션 타임아웃의 연쇄 작용

두 번째 에러 메시지는 첫 번째와는 다른 메커니즘, 즉 잠금(Locking)으로 인해 발생합니다. 이는 DDL 작업이 데이터베이스 리소스에 접근하는 방식과 애플리케이션의 연결 관리 방식 사이의 상호작용 결과입니다.

핵심 개념: DDL과 메타데이터 잠금(Metadata Lock)

ALTER TABLE과 같은 DDL 문은 테이블의 데이터가 아닌 정의, 즉 메타데이터를 변경합니다. 이 작업의 원자성과 일관성을 보장하기 위해, 데이터베이스는 DDL 실행 동안 해당 테이블에 대해 매우 강력한 배타적 잠금(Exclusive Lock)을 획득해야 합니다. PostgreSQL에서는 이를  

 

ACCESS EXCLUSIVE 잠금이라고 부르며 , MySQL InnoDB에서는 스키마 수정 잠금(Schema Modification Lock, Sch-M)이라고 합니다.  

 

이 잠금은 가장 강력한 수준의 잠금으로, 일단 획득되면 다른 모든 트랜잭션(읽기 포함)의 테이블 접근을 차단합니다. 더 중요한 점은, 이 잠금을 획득하려면 해당 테이블을 사용 중인 모든 기존 트랜잭션이 완료(커밋 또는 롤백)될 때까지 기다려야 한다는 것입니다. 운영 중인 서비스에서는 항상 테이블을 읽고 쓰는 트랜잭션이 존재하므로, DDL은 거의 항상 대기 상태에 놓이게 됩니다.  

 

에러 발생 메커니즘: 잠금 큐(Lock Queue)와 타임아웃

이 에러가 발생하는 연쇄 반응은 다음과 같습니다.

  1. 기존 트랜잭션 존재: 운영 중인 서비스에서는 항상 다수의 SELECT, INSERT, UPDATE 트랜잭션이 대상 테이블을 사용하고 있습니다. 이들은 각자 ROW SHARE 또는 ROW EXCLUSIVE 같은 비교적 약한 수준의 잠금을 획득한 상태입니다.  
  2. DDL 잠금 대기 시작: 관리자가 DROP COLUMN DDL을 실행합니다. DDL 프로세스는 ACCESS EXCLUSIVE 잠금을 요청하지만, 테이블을 사용 중인 기존 트랜잭션들이 끝나지 않았기 때문에 즉시 획득할 수 없고 '대기(WAIT)' 상태에 들어갑니다.  
  3. 잠금 큐 형성: DDL이 대기하는 동안, 애플리케이션에서 오는 새로운 모든 요청(간단한 SELECT 포함)들 역시 해당 테이블에 접근해야 합니다. 데이터베이스의 잠금 관리자는 "배타적 잠금 요청이 대기 중이므로, 후속 요청들은 모두 그 뒤에 줄을 서야 한다"는 규칙에 따라 이들을 모두 대기시킵니다. 이것이 바로 '잠금 큐(Lock Queue)' 또는 '잠금 경합(Lock Contention)' 상황입니다.  
  4. 커넥션 고갈 및 타임아웃: 애플리케이션 서버의 커넥션 풀은 새로운 요청을 처리하기 위해 DB 커넥션을 계속해서 할당합니다. 하지만 할당된 커넥션에서 실행된 쿼리들은 DB 서버에서 모두 '잠금 대기' 상태에 빠져 응답을 반환하지 못합니다. 결국 커넥션 풀의 모든 가용 커넥션이 소진되고, 새로운 요청은 커넥션을 할당받지 못해 대기하게 됩니다. 이미 할당된 커넥션들은 애플리케이션에 설정된 타임아웃(connect_timeout, socket_timeout 등)에 도달하여 강제로 연결이 끊어집니다.  
  5. 에러 발생: 이 때 클라이언트(애플리케이션) 측에서는 The active SQL connection has changed due to a connection failure 또는 A network-related or instance-specific error occurred 와 같은 일반적인 네트워크 단절 혹은 연결 실패 오류를 받게 됩니다.  

에러 메시지의 기만성(Deceptiveness)

connection failure 라는 에러 메시지는 네트워크 문제나 DB 서버 다운을 암시하지만, 실제 원인은 매우 다릅니다. 이 현상의 본질은 애플리케이션의 타임아웃 설정과 데이터베이스의 잠금 정책 사이의 상호작용입니다. 네트워크는 정상이고 DB 서버도 완벽하게 작동하고 있지만, DB가 의도적으로 응답을 지연시키는(잠금 대기) 상황을 애플리케이션이 "연결 실패"로 해석하는 것입니다.

사고 발생 시, 애플리케이션 팀은 "DB 연결이 계속 끊어진다"고 보고하고, DB 팀은 "DB는 정상 작동 중이며 부하도 높지 않다"고 말하는 전형적인 교착 상태가 발생할 수 있습니다. 진짜 원인은 그 둘의 경계에 있습니다. 즉, DB의 잠금 대기 시간(lock_wait_timeout과는 별개로, DDL은 기본적으로 무한정 기다릴 수 있음)이 애플리케이션의 인내심(커넥션 타임아웃)을 초과한 것입니다. 따라서 이 에러는 단순히 DB나 네트워크를 탓할 문제가 아니라,  

 

DDL 작업의 잠금 특성을 고려한 애플리케이션 타임아웃 전략의 부재가 근본 원인 중 하나임을 명확히 보여줍니다. 이는 시스템 전체를 아우르는 통합적인 관점의 필요성을 강조합니다.

1.3. 재구성: 장애 발생 시나리오의 타임라인

지금까지의 분석을 바탕으로, 장애 발생 순간의 상세한 이벤트 시퀀스를 시간 순서대로 재구성하면 다음과 같습니다.

  • (T-10초) 정상 상태: 다수의 애플리케이션 트랜잭션(읽기/쓰기)이 대상 테이블에 접근하며 정상적으로 처리되고 있습니다. 이 중 일부는 장기 실행 트랜잭션(예: REPEATABLE-READ 스냅샷을 가진 트랜잭션 A)일 수 있습니다.
  • (T=0초) DDL 실행: 관리자가 ALTER TABLE my_table DROP COLUMN col_x; 명령을 실행합니다.
  • (T=0.01초) 잠금 대기 시작: my_table에 활성 트랜잭션이 존재하므로, DDL 세션은 ACCESS EXCLUSIVE 메타데이터 잠금을 즉시 획득하지 못하고 대기 상태에 돌입합니다.  
  • (T=0.02초 ~ T+5초) 잠금 큐 형성 및 커넥션 장애:
    • 새로운 웹 요청들이 유입되고, 애플리케이션은 커넥션 풀에서 커넥션을 꺼내 my_table에 대한 쿼리를 보냅니다.
    • 이 쿼리들은 모두 DDL 잠금 요청 뒤에 큐잉되어 응답 없이 대기합니다.  
    • 애플리케이션 커넥션 풀이 빠르게 고갈되기 시작합니다. 일부 커넥션은 설정된 타임아웃(예: 3초)을 초과하여 클라이언트 측에서 연결을 강제로 끊어버립니다. 사용자에게 The active SQL connection has changed... 에러가 발생하기 시작합니다.
  • (T+5.1초) DDL 잠금 획득 및 실행: DDL을 막고 있던 기존 트랜잭션들이 모두 완료(커밋/롤백)됩니다. DDL 세션이 마침내 ACCESS EXCLUSIVE 잠금을 획득합니다.
  • (T+5.2초) DDL 커밋: DROP COLUMN 작업 자체는 메타데이터만 변경하므로 매우 빠르게 완료되고 즉시 커밋됩니다.   my_table의 잠금은 해제됩니다.
  • (T+5.3초) 후폭풍 - 트랜잭션 스냅샷 충돌:
    • DDL 실행 이전에 시작되었던 장기 실행 트랜잭션 A가, 이제 변경된 my_table에 대한 후속 쿼리를 실행합니다.
    • InnoDB 엔진이 트랜잭션 A의 오래된 스냅샷과 변경된 실제 테이블 정의의 불일치를 감지합니다.
    • 트랜잭션 A는 강제 롤백되고, 해당 트랜잭션을 실행한 애플리케이션 스레드는 Table definition has changed... 에러를 수신합니다.  
  • (T+6초 ~) 시스템 정상화: DDL이 완료되고 잠금이 해제되었으므로, 새롭게 시작되는 트랜잭션들은 정상적으로 처리됩니다. 장애는 외부에서 보기에 "아주 잠깐" 발생했던 것으로 관측됩니다.
반응형

댓글