MySQL 에서 DDL 과 Auto-commit

이명규·2025년 5월 24일

MySQL 에서 DDL을 실행할 때의 트랜잭션 처리 방식과 암묵적 커밋(Implicit Commit) 에 대한 이슈를 통해 정리한 글 입니다


1. DDL 은 트랜잭션에서 암묵적으로 커밋된다

MySQL 에서는 DDL이 실행되면 자동으로 커밋이 발생합니다.
이를 "암묵적 커밋 (Implicit Commit)" 이라고 부릅니다

예시

START TRANSACTION;
INSERT INTO user VALUES(1, '철수');
ALTER TABLE user ADD COLUMN email VARCHAR(255);
ROLLBACK;

결과

ALTER TABLE 이 실행되는 순간, 앞서 실행된 INSERT 도 함께 커밋됨 이후 ROLLBACK 은 무의미하게 됩니다

왜 이렇게 동작할까 ?

  • DDL 은 테이블의 메타데이터를 변경합니다
  • 메타데이터는 여러 세션과 공유되며, 롤백되면 위험합니다
  • 구조 변경은 즉시 반영되어야 하므로 트랜잭션과 무관하게 COMMIT이 강제됩니다

관련 문서 : MySQL 공식문서 - Implicit Commit



2. DDL 중 Lock Timeout 이 발생한 원인 → Metadata Lock (MDL)

실무에서 DDL 을 실행하는 도중에 lock wait timeout exceeded 오류가 발생한 경우가 있습니다.

이는 해당 테이블의 메타데이터 락 (MDL)을 다른 트랜잭션이 쥐고 있어서 발생하는 문제 입니다.

실제로 일어난 흐름

  1. ALTER TABLE 쿼리를 실행 중
  2. lock wait timeout exceeded 에러 발생
  3. SHOW FULL PROCESSLIST 로 확인해보니 Sleep 상태의 커넥션이 존재
  4. 해당 커넥션을 KILL 하니 이후 DDL 실행이 정상적으로 처리됨

원인 분석

  • MySQL 은 DDL 시 MDL_EXCLUSIVE 락을 요구함
  • 동시에 다른 커넥션이 SELECT 쿼리를 통해 MDL_SHARED 락을 잡고 있었다면 충돌 발생
  • 그 세션이 Sleep 상태였어도 MDL은 유지됨
  • KILL 명령으로 세션 종료 → MDL 해제 → 이후 쿼리 정상 실행

이후 알게된 것

  • 트랜잭션 커밋 또는 세션 종료 전에 구조 변경 시도하지 말것.
  • SHOW PROCESSLIST + performance_schema.metadata_locks 로 누가 락을 가지고 있는지 확인
    (+ 실행 시점을 알기위해 performance_schema.threads 테이블 join)

참고

IntelliJ 에서 한 콘솔에 여러 DDL 을 실행하면 일반적으로 하나의 커넥션에서 처리됩니다

Auto-commit 이 꺼져 있어도 DDL 은 내부적으로 자동 커밋되고, 커넥션이 새로 생성되지는 않습니다

SHOW FULL PROCESSLIST 결과, 여러 Sleep 커넥션이 남지 않음이 확인되었습니다

과거에 Sleep 커넥션이 다수 쌓였던 현상은 백그라운드 구조 동기화(Auto Sync), 여러 세션 열림, 실패한 트랜잭션이 커밋되지 않은 채 유지된 경우일 수 있습니다.


3. Metadata Lock (MDL) 이란?

MDL 은 테이블, 컬럼, 인덱스 등 메타데이터에 대한 동시 접근을 제어하기 위한 락입니다.
메타데이터를 수정하거나 읽는 모든 쿼리는 이 락을 획득해야 합니다

종류

  • MDL_SHARED
    • Shared Lock 이며 여러 트랜잭션이 동시에 획득할 수 있습니다
    • 세부적으로는 MDL_SHARED_READ , MDL_SHARED_WRITE 등으로 나뉘어 있습니다

  • MDL_EXCLUSIVE
    • Exclusive Lock 은 하나의 트랜잭션만 획득 가능한 Lock
    • Exclusive Lock 은 테이블에 대해 어떠한 Lock 도 걸려있지 않아야 획득 가능합니다
    • ALTER, DROP, RENAME 등 구조 변경 시 사용합니다.

  • MDL_INTENTION
    • Intention Lock 은 잠금을 걸기 전에 먼저 의도(intent)를 표시하며 다른 트랜잭션 과의 충돌을 방지하기 위해 InnoDB 에서 사용하는 잠금 메커니즘 입니다.
    • InnoDB 엔진이 자동으로 관리하므로 사용자가 직접 제어할 일은 거의 없습니다
    • 락 요청 의도가 있는 작업 시 미리 획득하게 됩니다

특징: 트랜잭션 종료와 무관하게 커넥션이 열려 있으면 락이 유지됨

언제 걸리는 것 인가?

  • SELECT * FROM table → MDL_SHARED
  • ALTER TABLE table ... → MDL_EXCLUSIVE
  • SHOW COLUMNS FROM table → MDL_SHARED

MySQL 의 InnoDB 엔진은 DML 시 내부적으로 다양한 레코드 수준의 락을 사용합니다
이들과 MDL 은 전혀 다른 레이어 입니다

종류

  • X (Exclusive Lock)
    • UPDATE , DELETE 등에서 해당 ROW 를 다른 트랜잭션이 접근하지 못하게 막음
  • S (Shared Lock)
    • SELECT ... LOCK IN SHARE MODE 에서 사용
  • IX / IS (Intention Lock
    • 테이블에 대해 부분적으로 row-level 락을 걸겠다는 의도를 나타낸 락
  • Gap Lock
    • 인덱스 상 존재하지 않는 값 범위에 락을 걸어 Insert 방지
  • Next-Key Lock
    • 레코드 락 + 갭 락의 조합

격리 수준과 락의 결정 방식

  • REPEATABLE READ 격리 수준에서는 기본적으로 Next-Key Lock 을 사용하여 팬텀 리드를 방지합니다
  • 하지만 정확히 유니크한 인덱스 값을 WHERE 조건으로 조회하는 경우 해당 ROW 에 대해서만 Record Lock 만 걸리고 Gap Lock 은 생략됩니다

즉, 쿼리 결과가 정확히 유니크한 하나의 레코드일 경우에는 Gap Lock 대신 Record Lock만 획득합니다

MDL 과의 차이점

  • Metadata Lock (MDL)
    • 테이블/컬럼 등의 구조 범위에서 사용
    • 쿼리 해석 시점에 발생
    • 커넥션 종료 또는 명시적 해제를 통해 락 해제 처리
  • Record Lock (InnoDB)
    • 행(ROW) 수준 데이터 범위에서 사용
    • 실행 중 트랜잭션 안에서 발생
    • 트랜잭션 커밋 또는 롤백 시점에 락 해제 처리

정리

  • MySQL 에서 DDL 은 항상 암묵적으로 커밋되며 트랜잭션의 영향을 받지 않는다
  • DDL 중간에 lock wait timeout 이 발생했다면 다른 커넥션이 해당 테이블에 MDL 락을 쥐고 있어서 생긴 현상일 가능성이 높다
  • InnoDB 의 레코드 락(Gap, Next-key, Intention Lock 등) 은 데이터 조작시 작동하며, MDL 과는 계층과 해제 시점이 다르다
  • REPEATABLE READ 격리 수준에서 범위 조회는 Gap + Record (Next-Key Lock) 를, 유니크 단건 조회는 Record Lock 만 획득한다
  • SHOW PROCESSLIST, performance_schema.metadata_locks , performance_schema.threads 를 통해 커넥션 및 실행 시점, 락 상태를 확인하면 원인 추적이 수월하다

참고

profile
개발자

0개의 댓글