ALTER TABLE이 멈췄을 때 — MariaDB Metadata Lock 재현과 안전한 해제 순서

조회수 14

도입

ALTER TABLE이 오래 끝나지 않으면 테이블이 커서 느린 것으로 생각하기 쉽다. 그러나 실행 시간은 0초인데 상태가 Waiting for table metadata lock이라면 작업은 시작조차 못 한 것이다. 다른 세션이 테이블의 메타데이터 잠금을 잡은 채 트랜잭션을 끝내지 않았기 때문이다.

이 글에서는 MariaDB 10.11과 InnoDB를 기준으로 대기 상황을 직접 만들고, blocker를 찾은 뒤 안전하게 해제한다. 운영 데이터베이스가 아닌 별도의 실습 DB에서 실행해야 한다.

1. 실습 테이블 준비

CREATE DATABASE IF NOT EXISTS mdl_lab;
USE mdl_lab;

CREATE TABLE orders (
    order_id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    status VARCHAR(20) NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

INSERT INTO orders(status) VALUES ('paid'), ('ready');

터미널을 세 개 열고 각각 MariaDB에 접속한다.

mariadb --defaults-extra-file="$HOME/.my.cnf" mdl_lab

비밀번호를 -p비밀번호 형태로 명령행에 쓰지 않았다. 프로세스 목록과 셸 기록에 인증 정보가 남을 수 있기 때문이다.

2. 잠금 대기 재현

세션 A에서 트랜잭션을 열고 행을 읽는다.

START TRANSACTION;
SELECT * FROM orders WHERE order_id = 1;

SELECT가 끝나도 트랜잭션은 열려 있다. MariaDB는 트랜잭션이 사용한 테이블의 메타데이터 잠금을 트랜잭션 종료까지 유지한다.

세션 B에서 다음 DDL을 실행한다.

ALTER TABLE orders ADD COLUMN memo VARCHAR(100) NULL;

프롬프트가 돌아오지 않는다. 이 상태에서 같은 명령을 반복 실행하면 해결되지 않고 대기자만 늘어난다.

3. 기다리는 세션과 blocker 찾기

세션 C에서 먼저 프로세스 목록을 확인한다.

SHOW FULL PROCESSLIST;

확인할 열은 Id, User, Host, db, Time, State, Info다. 세션 B의 State가 다음처럼 표시된다.

Waiting for table metadata lock

장시간 Sleep인 연결도 무조건 제거하면 안 된다. 열린 트랜잭션인지 확인한다.

SELECT
    trx_mysql_thread_id,
    trx_started,
    trx_state,
    trx_tables_locked,
    trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started;

trx_mysql_thread_idPROCESSLIST.Id와 연결하면 오래된 트랜잭션의 세션을 찾을 수 있다. Performance Schema가 활성화된 환경에서는 보조적으로 다음 정보를 확인한다.

SELECT *
FROM performance_schema.metadata_locks
WHERE OBJECT_SCHEMA = 'mdl_lab'
  AND OBJECT_NAME = 'orders';

환경에 따라 metadata_locks의 계측 상태나 표시 내용이 다를 수 있으므로, PROCESSLISTinnodb_trx를 함께 보는 편이 안전하다.

4. 안전한 해제 순서

가장 좋은 해결은 세션 A에서 업무 로직에 맞게 트랜잭션을 끝내는 것이다.

ROLLBACK;

변경을 보존해야 한다면 COMMIT한다. 잠금이 풀리면 세션 B의 ALTER TABLE이 진행된다.

SHOW COLUMNS FROM orders LIKE 'memo';

원래 세션에 접근할 수 없고 서비스 영향이 계속되는 경우에만 관리 세션에서 종료를 검토한다.

KILL QUERY 1234;       -- 현재 문장만 중단
KILL CONNECTION 1234;  -- 연결과 트랜잭션 종료

여기서 1234는 반드시 다시 확인한 실제 연결 ID여야 한다. 쓰기 트랜잭션을 KILL CONNECTION으로 종료하면 rollback이 오래 걸릴 수 있다. 담당자와 쿼리, 트랜잭션 시작 시각, 변경량을 확인한 뒤 결정한다.

5. 무한정 기다리지 않게 만들기

MariaDB의 lock_wait_timeout 기본값은 매우 길 수 있다. 유지보수 세션에서만 짧게 제한할 수 있다.

SET SESSION lock_wait_timeout = 10;
ALTER TABLE orders ADD COLUMN reviewed_at DATETIME NULL;

문장 단위로 즉시 실패시키려면 NOWAIT를 사용한다.

ALTER TABLE orders NOWAIT ADD COLUMN reviewed_by BIGINT NULL;

NOWAIT는 잠금을 해결하는 기능이 아니다. 기다리지 않고 실패시켜 배포 자동화가 빠르게 중단되도록 만드는 안전장치다.

운영 점검 체크리스트

  • DDL 전에 오래 열린 트랜잭션을 확인했는가?
  • PROCESSLIST의 대기 세션과 innodb_trx의 blocker를 연결했는가?
  • 세션 종료 전에 정상적인 COMMIT 또는 ROLLBACK을 요청했는가?
  • 연결 ID, 사용자, 호스트와 실행 SQL을 재확인했는가?
  • 배포용 계정의 세션에 적절한 lock_wait_timeout을 설정했는가?
  • 스키마 변경 실패 시 재시도·중단 절차가 있는가?

함께 읽기

참고 자료

확인일: 2026-07-29

한 줄 요약

멈춘 ALTER TABLE은 반복 실행하지 말고, 대기 세션과 열린 트랜잭션을 연결해 blocker를 찾은 뒤 정상 종료부터 시도한다.

댓글 0

  • 첫 번째 댓글을 남겨보세요.