카테고리 : MySQL/기술노트

InnoDB data dictionary와 metadata lock의 기본 개념

MySQL 8.0 InnoDB data dictionary와 metadata lock이 DDL, DML, 장애 대응, 온라인 변경 작업에 미치는 영향을 운영 관점에서 정리한다.

저자: MySQL 기술 노트 작성: 2026.05.30 약 11분 6,473자
다운로드

1. 왜 data dictionary와 metadata lock을 함께 보아야 하는가

MySQL 운영에서 잠금이라고 하면 보통 row lock, gap lock, table lock을 먼저 떠올린다. 그러나 실제 장애 대응 현장에서는 데이터 행 자체가 아니라 테이블의 정의와 객체 메타데이터 때문에 작업이 멈추는 경우가 적지 않다. ALTER TABLE이 끝나지 않거나, 배포 스크립트의 CREATE INDEX가 대기하거나, 짧은 조회라고 생각한 세션이 스키마 변경을 오래 막는 상황이 여기에 해당한다.

이 문제를 이해하려면 두 가지 축을 함께 봐야 한다.

  • InnoDB data dictionary: 테이블, 인덱스, tablespace, 컬럼, 제약조건 같은 객체 정의를 저장하고 관리하는 내부 메타데이터 계층
  • metadata lock(MDL): SQL 문장이 참조하는 객체 정의가 실행 중에 바뀌지 않도록 보호하는 서버 계층의 잠금

Data dictionary는 “MySQL이 객체 정의를 어디에, 어떤 일관성 모델로 보관하는가”의 문제이고, metadata lock은 “동시에 실행되는 SQL과 DDL이 그 정의를 안전하게 공유하도록 어떻게 직렬화하는가”의 문제다. 둘은 서로 다른 계층의 기능이지만 운영자는 둘을 분리해서 볼 수 없다. MySQL 8.0 이후 data dictionary가 트랜잭션 기반으로 통합되면서 DDL의 원자성과 복구 가능성은 크게 좋아졌지만, DDL이 metadata lock을 필요로 한다는 사실은 그대로 남아 있다.

2. MySQL 8.0의 InnoDB data dictionary 개념

MySQL 5.7까지는 .frm 파일, InnoDB 내부 dictionary, 일부 system table, storage engine별 메타데이터가 함께 사용되었다. 이 구조에서는 서버 계층의 테이블 정의 파일과 InnoDB 내부 메타데이터가 서로 맞지 않는 문제, 장애 복구 시 dictionary 불일치 문제, DDL 원자성 제한이 운영 리스크가 될 수 있었다.

MySQL 8.0부터는 data dictionary가 InnoDB 기반의 트랜잭션 저장소로 통합되었다. 사용자가 직접 수정하는 일반 테이블은 아니지만, 서버는 테이블 정의, 인덱스 정의, tablespace 정보, column metadata 등을 dictionary table에 저장하고 InnoDB 트랜잭션 메커니즘으로 보호한다. 그 결과 다음 특성이 중요해졌다.

  1. Dictionary 변경은 InnoDB 트랜잭션과 더 강하게 결합된다. DDL이 성공하거나 실패할 때 dictionary와 물리 객체 상태를 더 일관되게 유지할 수 있다.
  2. Atomic DDL의 기반이 된다. CREATE TABLE, ALTER TABLE, DROP TABLE 같은 작업이 장애 중간에 끊겨도 crash recovery 과정에서 더 예측 가능한 상태로 정리된다.
  3. 객체 정의 조회 경로가 달라졌다. INFORMATION_SCHEMA는 더 이상 예전 방식의 파일 스캔 중심 모델로만 이해하면 안 된다. 내부 dictionary와 data dictionary cache를 통해 객체 정의를 읽는다.
  4. 운영자가 dictionary 내부 테이블을 직접 조작하지 않는다. 장애 대응 시에도 dictionary를 수동으로 고치는 접근은 매우 위험하다. 정상 SQL, 백업·복구 절차, MySQL이 제공하는 진단 뷰를 통해 접근해야 한다.

개념적으로는 다음 흐름으로 볼 수 있다.

flowchart TB
  SQL[SQL 계층\nCREATE/ALTER/DROP/SELECT] --> MDL[metadata lock 관리자]
  SQL --> DDAPI[data dictionary API]
  DDAPI --> DDCACHE[data dictionary cache]
  DDCACHE --> DDTABLE[InnoDB dictionary tables]
  DDTABLE --> REDO[redo log / undo / crash recovery]
  SQL --> PFS[performance_schema.metadata_locks]
  MDL --> PFS

중요한 점은 data dictionary가 InnoDB에 저장된다고 해서 일반 업무 데이터처럼 SELECT * FROM mysql.some_dictionary_table 방식으로 직접 다루는 대상이 아니라는 것이다. 운영자는 INFORMATION_SCHEMA, performance_schema, SHOW CREATE TABLE, SHOW TABLE STATUS, error log를 통해 간접적으로 상태를 해석한다.

3. Metadata lock은 무엇을 보호하는가

Metadata lock은 테이블의 행 데이터가 아니라 객체 정의의 안정성을 보호한다. 예를 들어 어떤 세션이 SELECT * FROM orders를 실행하는 동안 다른 세션이 동시에 DROP TABLE orders를 완료해 버리면 실행 중인 문장은 참조하던 객체의 구조를 잃게 된다. MySQL은 이런 상황을 막기 위해 SQL 문장이 객체를 열고 사용하는 동안 metadata lock을 잡는다.

대표적인 잠금 관계는 다음과 같다.

작업 일반적으로 필요한 MDL 성격 운영상 의미
SELECT, INSERT, UPDATE, DELETE 공유 성격의 metadata lock 실행 중 객체 정의가 DDL로 바뀌지 않도록 보호한다.
ALTER TABLE, DROP TABLE, TRUNCATE TABLE 배타 성격의 metadata lock 기존 문장이 끝날 때까지 기다리고, 대기 중에는 뒤따르는 DML도 줄줄이 막을 수 있다.
CREATE INDEX 또는 index 관련 ALTER TABLE DDL 단계에 따라 강한 metadata lock 필요 온라인 DDL이라도 시작·종료 단계에서 MDL 대기가 발생할 수 있다.
Foreign key 관련 DDL 관련 부모·자식 테이블의 metadata lock 한 테이블만 바꾸는 작업처럼 보여도 연관 테이블 때문에 대기할 수 있다.

Metadata lock은 트랜잭션 경계와도 연결된다. autocommit 모드의 단일 SELECT는 보통 문장 종료 시 metadata lock이 해제된다. 그러나 명시적 트랜잭션 안에서 테이블을 참조하면 트랜잭션이 끝날 때까지 metadata lock이 유지될 수 있다. 이 때문에 “조회만 하는 세션”이 ALTER TABLE을 오래 막는 상황이 발생한다.

4. DDL 대기가 장애로 커지는 전형적인 경로

Metadata lock 문제가 위험한 이유는 단순히 DDL 하나가 기다리는 데서 끝나지 않기 때문이다. 다음 순서를 보면 장애가 어떻게 커지는지 이해할 수 있다.

sequenceDiagram
  participant S1 as 세션 1: 긴 트랜잭션 SELECT
  participant S2 as 세션 2: ALTER TABLE
  participant S3 as 세션 3: 신규 INSERT/UPDATE
  participant T as 대상 테이블

  S1->>T: SELECT 실행 후 트랜잭션 미종료
  Note over S1,T: 공유 metadata lock 유지
  S2->>T: ALTER TABLE 요청
  Note over S2,T: 배타 metadata lock 대기
  S3->>T: INSERT/UPDATE 요청
  Note over S3,T: 대기 중인 DDL 뒤에서 추가 대기 가능
  S1->>T: COMMIT 또는 ROLLBACK
  S2->>T: DDL 진행
  S3->>T: DDL 이후 재개

운영자가 보는 증상은 다음처럼 나타난다.

  • ALTER TABLE 세션이 Waiting for table metadata lock 상태로 보인다.
  • 평소 빠르던 DML이 갑자기 누적된다.
  • connection 수, thread 수, application timeout이 증가한다.
  • 원인은 DDL 세션이 아니라 그보다 먼저 테이블을 참조한 장기 트랜잭션일 수 있다.
  • DDL을 강제로 종료해도 이미 쌓인 트래픽과 application retry 때문에 회복에 시간이 걸릴 수 있다.

따라서 metadata lock 장애에서는 “지금 기다리는 DDL”만 볼 것이 아니라 누가 먼저 객체를 잡았는지, DDL 뒤에 어떤 세션이 줄을 섰는지, 트랜잭션이 왜 끝나지 않았는지를 함께 확인해야 한다.

5. MySQL 8.0에서 dictionary와 MDL 상태 확인하기

다음 예제는 MySQL 8.0 기준으로 data dictionary 관련 조회 경로와 metadata lock 진단 객체의 존재를 확인한다. 실제 운영 장애에서 대기 관계를 보려면 동시에 여러 세션을 만들어야 하지만, 먼저 어떤 시스템 뷰를 사용할 수 있는지 확인하는 절차가 필요하다.

SELECT VERSION() AS mysql_version;

DROP TABLE IF EXISTS mdl_dictionary_demo;

CREATE TABLE mdl_dictionary_demo (
  id BIGINT NOT NULL,
  status VARCHAR(20) NOT NULL,
  created_at DATETIME NOT NULL,
  PRIMARY KEY (id),
  KEY ix_status_created (status, created_at)
) ENGINE=InnoDB;

SELECT table_schema, table_name, engine, table_type
FROM information_schema.tables
WHERE table_schema = DATABASE()
  AND table_name = 'mdl_dictionary_demo';

SELECT table_name, index_name, seq_in_index, column_name, non_unique
FROM information_schema.statistics
WHERE table_schema = DATABASE()
  AND table_name = 'mdl_dictionary_demo'
ORDER BY index_name, seq_in_index;

SHOW TABLES FROM performance_schema LIKE 'metadata_locks';

SELECT name, enabled, timed
FROM performance_schema.setup_instruments
WHERE name = 'wait/lock/metadata/sql/mdl';

DROP TABLE mdl_dictionary_demo;

실행 결과(MySQL 8.0.46):

mysql> SELECT VERSION() AS mysql_version;

+---------------+
| mysql_version |
+---------------+
| 8.0.46        |
+---------------+
1 row in set (0.00 sec)

mysql> CREATE TABLE mdl_dictionary_demo (... ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT table_schema, table_name, engine, table_type
    -> FROM information_schema.tables
    -> WHERE table_schema = DATABASE()
    ->   AND table_name = 'mdl_dictionary_demo';

+-----------------+---------------------+--------+------------+
| TABLE_SCHEMA    | TABLE_NAME          | ENGINE | TABLE_TYPE |
+-----------------+---------------------+--------+------------+
| mysql_tech_note | mdl_dictionary_demo | InnoDB | BASE TABLE |
+-----------------+---------------------+--------+------------+
1 row in set (0.01 sec)

mysql> SELECT table_name, index_name, seq_in_index, column_name, non_unique
    -> FROM information_schema.statistics
    -> WHERE table_schema = DATABASE()
    ->   AND table_name = 'mdl_dictionary_demo'
    -> ORDER BY index_name, seq_in_index;

+---------------------+-------------------+--------------+-------------+------------+
| TABLE_NAME          | INDEX_NAME        | SEQ_IN_INDEX | COLUMN_NAME | NON_UNIQUE |
+---------------------+-------------------+--------------+-------------+------------+
| mdl_dictionary_demo | ix_status_created |            1 | status      |          1 |
| mdl_dictionary_demo | ix_status_created |            2 | created_at  |          1 |
| mdl_dictionary_demo | PRIMARY           |            1 | id          |          0 |
+---------------------+-------------------+--------------+-------------+------------+
3 rows in set (0.00 sec)

mysql> SHOW TABLES FROM performance_schema LIKE 'metadata_locks';

+-----------------------------------------------+
| Tables_in_performance_schema (metadata_locks) |
+-----------------------------------------------+
| metadata_locks                                |
+-----------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT name, enabled, timed
    -> FROM performance_schema.setup_instruments
    -> WHERE name = 'wait/lock/metadata/sql/mdl';

+----------------------------+---------+-------+
| name                       | enabled | timed |
+----------------------------+---------+-------+
| wait/lock/metadata/sql/mdl | YES     | YES   |
+----------------------------+---------+-------+
1 row in set (0.00 sec)

mysql> DROP TABLE mdl_dictionary_demo;

Query OK, 0 rows affected (0.00 sec)

위 예제에서 information_schema.tablesinformation_schema.statistics는 사용자가 직접 dictionary table을 읽는 방식이 아니라 MySQL이 제공하는 메타데이터 조회 인터페이스다. performance_schema.metadata_locks는 현재 획득되었거나 대기 중인 metadata lock을 확인할 때 사용하는 대표적인 진단 뷰다.

운영 환경에서 실제 대기 세션을 볼 때는 다음처럼 컬럼을 제한해 조회하는 방식이 유용하다. 이 쿼리는 특정 테이블에 대한 metadata lock 상태를 보여 주는 진단 쿼리이며, 실제 결과는 그 시점의 workload와 Performance Schema 설정에 따라 달라진다.

DROP TABLE IF EXISTS mdl_lock_view_demo;

CREATE TABLE mdl_lock_view_demo (
  id BIGINT NOT NULL PRIMARY KEY,
  note VARCHAR(100) NOT NULL
) ENGINE=InnoDB;

SELECT object_type,
       object_schema,
       object_name,
       lock_type,
       lock_duration,
       lock_status,
       owner_thread_id
FROM performance_schema.metadata_locks
WHERE object_schema = DATABASE()
  AND object_name = 'mdl_lock_view_demo'
ORDER BY owner_thread_id, lock_type;

DROP TABLE mdl_lock_view_demo;

실행 결과(MySQL 8.0.46):

mysql> DROP TABLE IF EXISTS mdl_lock_view_demo;

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE mdl_lock_view_demo (
    ->   id BIGINT NOT NULL PRIMARY KEY,
    ->   note VARCHAR(100) NOT NULL
    -> ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT object_type,
    ->        object_schema,
    ->        object_name,
    ->        lock_type,
    ->        lock_duration,
    ->        lock_status,
    ->        owner_thread_id
    -> FROM performance_schema.metadata_locks
    -> WHERE object_schema = DATABASE()
    ->   AND object_name = 'mdl_lock_view_demo'
    -> ORDER BY owner_thread_id, lock_type;

Empty set (0.00 sec)

mysql> DROP TABLE mdl_lock_view_demo;

Query OK, 0 rows affected (0.00 sec)

이 결과가 비어 있다고 해서 metadata lock 기능이 꺼져 있다는 뜻은 아니다. 단일 세션에서 짧은 문장만 실행하면 조회 시점에 관찰 가능한 lock row가 없을 수 있다. 장애 분석에서는 대기 중인 DDL이 존재하는 시점에 조회해야 하며, 필요하면 performance_schema.threads, performance_schema.events_statements_current, information_schema.processlist, information_schema.innodb_trx를 함께 본다.

6. 장기 트랜잭션과 MDL을 함께 해석하는 방법

Metadata lock 대기의 원인을 찾을 때 가장 흔한 실수는 ALTER TABLE 세션만 종료하는 것이다. 물론 긴급 상황에서는 대기 중인 DDL을 먼저 중단해 application 영향도를 줄여야 할 수 있다. 그러나 재발 방지를 위해서는 대기열 앞쪽의 세션을 찾아야 한다.

확인 순서는 다음과 같이 잡을 수 있다.

  1. SHOW PROCESSLIST 또는 performance_schema.threads에서 Waiting for table metadata lock 상태의 세션을 찾는다.
  2. 해당 세션이 어떤 테이블에 대해 DDL을 시도했는지 확인한다.
  3. 같은 테이블을 참조한 오래된 트랜잭션이나 sleeping transaction을 찾는다.
  4. application 배포, migration 도구, ORM schema sync, 수동 DBA 작업이 동시에 실행되었는지 확인한다.
  5. 대기 중인 DDL을 취소할지, 선행 트랜잭션을 종료할지, application traffic을 줄일지 결정한다.

MySQL 8.0에서는 row lock 대기 분석에는 performance_schema.data_lock_waitsperformance_schema.data_locks가 중요하지만, metadata lock은 별도의 performance_schema.metadata_locks를 본다. Row lock과 metadata lock을 혼동하면 원인 분석이 늦어진다. 예를 들어 UPDATE가 막혀 있어도 실제 원인은 record lock이 아니라 앞에 대기 중인 DDL일 수 있다.

7. Aurora MySQL에서의 운영 해석

Aurora MySQL은 스토리지 계층, redo 처리, replica 구조, 백업 방식이 Community MySQL과 다르다. 그러나 SQL 계층의 metadata lock 개념은 여전히 중요하다. Aurora에서도 DDL은 객체 정의를 바꾸며, 실행 중인 SQL과 충돌하지 않도록 metadata lock을 사용한다.

Aurora 운영에서 특히 주의할 점은 다음과 같다.

  • Writer 인스턴스의 metadata lock 대기는 application write path에 직접 영향을 준다.
  • Reader에서 실행되는 긴 조회가 writer의 DDL과 어떤 방식으로 상호작용하는지는 Aurora 버전, 복제 지연, DDL 종류, reader endpoint 사용 방식에 따라 운영 해석이 달라질 수 있다.
  • Performance Insights와 CloudWatch 지표는 대기 이벤트를 파악하는 데 도움이 되지만, SQL 레벨 원인 확인에는 MySQL 내부 뷰 조회가 여전히 필요하다.
  • Blue/Green deployment, clone, online schema change 도구를 쓰더라도 cutover 또는 rename 단계에서 metadata lock을 완전히 피할 수는 없다.

따라서 Aurora라고 해서 DDL 락 리스크가 사라지는 것은 아니다. 오히려 자동 failover, reader endpoint, application retry가 결합되면 장애 양상이 더 복잡해질 수 있으므로 변경 작업 전 metadata lock 관찰 방법을 표준화해야 한다.

8. 흔한 오해와 주의점

8.1 “온라인 DDL이면 metadata lock이 없다”는 오해

ALGORITHM=INPLACEALGORITHM=INSTANT가 가능한 작업이라도 metadata lock이 전혀 없는 것은 아니다. 온라인 DDL은 데이터 복사나 DML 차단 범위를 줄이는 기술이지, 객체 정의 변경을 보호하는 metadata lock을 제거하는 기술이 아니다. 특히 DDL 시작과 종료 단계에서 강한 lock이 필요할 수 있다.

8.2 “SELECT는 잠금을 잡지 않는다”는 오해

일반 SELECT는 row lock을 잡지 않는 경우가 많지만, metadata lock 관점에서는 테이블 정의를 안정적으로 참조해야 한다. 명시적 트랜잭션 안에서 SELECT 후 commit을 잊으면 DDL을 막는 원인이 될 수 있다.

8.3 “data dictionary는 직접 고치면 된다”는 위험한 접근

MySQL 8.0의 data dictionary는 InnoDB 기반이지만 사용자가 직접 수정하는 관리 테이블이 아니다. Dictionary 불일치나 DDL 실패를 수동 UPDATE로 해결하려는 접근은 더 큰 손상을 만들 수 있다. 백업에서 복구하거나, 공식 복구 절차와 MySQL이 제공하는 도구를 사용해야 한다.

8.4 “metadata_locks가 비어 있으면 문제가 없다”는 오해

performance_schema.metadata_locks는 관찰 시점, instrumentation 설정, workload 상태에 따라 결과가 달라진다. 문제가 재현되는 순간에 조회하지 않으면 비어 있을 수 있다. 또한 결과를 해석할 때는 LOCK_STATUS='PENDING'인 행뿐 아니라 같은 객체의 granted lock과 소유 thread도 함께 봐야 한다.

9. 운영 체크리스트

DDL 또는 schema migration을 실행하기 전에는 다음 항목을 확인한다.

  • ALTER TABLEALGORITHM, LOCK

장애 중에는 다음 순서를 권장한다.

  • Waiting for table metadata lock

10. 결론

InnoDB data dictionary는 MySQL 8.0에서 객체 정의의 저장과 복구 일관성을 크게 개선한 기반 구조다. Metadata lock은 그 객체 정의가 실행 중인 SQL과 충돌하지 않도록 보호하는 동시성 제어 장치다. 두 기능은 평소에는 눈에 잘 띄지 않지만, DDL, 배포, 장애 대응, 온라인 스키마 변경에서 운영 안정성을 좌우한다.

운영자는 data dictionary를 직접 다루려 하기보다 MySQL이 제공하는 메타데이터 조회 인터페이스와 Performance Schema를 통해 상태를 해석해야 한다. 또한 온라인 DDL을 사용하더라도 metadata lock 대기 가능성을 전제로 변경 절차를 설계해야 한다. 다음 단계의 InnoDB 학습에서는 DDL 알고리즘, instant DDL의 한계, 그리고 row lock·metadata lock·table lock이 실제 장애에서 어떻게 겹치는지 더 구체적으로 다룰 수 있다.