한 줄 정의

MySQL 엔진 레벨의 잠금은 모든 스토리지 엔진에 영향 을 미치며, 글로벌 락, 백업 락, 테이블 락, 네임드 락, 메타데이터 락의 5가지가 있습니다.

쉽게 말하면

MySQL 엔진의 잠금은 건물 전체의 보안 시스템이고, InnoDB의 잠금은 각 사무실의 자물쇠입니다. 건물 보안(MySQL 엔진 잠금)이 잠기면 모든 사무실에 영향을 미치지만, 한 사무실의 자물쇠(InnoDB 잠금)는 다른 사무실에 영향을 주지 않습니다.

핵심 내용

글로벌 락 (Global Lock)

MySQL에서 제공하는 가장 범위가 큰 잠금 입니다.

FLUSH TABLES WITH READ LOCK;
  • 한 세션에서 글로벌 락을 획득하면, 다른 세션에서 SELECT를 제외한 대부분의 DDL/DML이 대기 상태가 됩니다
  • 영향 범위가 MySQL 서버 전체이므로, 작업 대상 테이블이나 데이터베이스가 다르더라도 동일하게 영향을 미칩니다
  • mysqldump가 내부적으로 이 명령을 실행할 수 있으므로, 백업 옵션을 반드시 확인해야 합니다
글로벌 락의 위험성

글로벌 락을 걸기 전에 장시간 실행 중인 쿼리가 있으면, 그 쿼리가 종료될 때까지 글로벌 락 자체가 대기합니다. 이 과정에서 새로운 DML도 모두 블로킹되므로, 최악의 경우 서버 전체의 INSERT/UPDATE/DELETE가 장시간 멈출 수 있습니다.

백업 락 (Backup Lock) — MySQL 8.0+

글로벌 락의 무거움을 보완하기 위해 도입된 경량화된 잠금 입니다.

LOCK INSTANCE FOR BACKUP;
-- 백업 실행
UNLOCK INSTANCE;
글로벌 락 vs 백업 락
기준글로벌 락백업 락
DML (INSERT/UPDATE/DELETE)차단허용
DDL (스키마 변경)차단차단
사용자 관리차단차단
복제 영향복제 지연 발생복제 정상 진행
도입 버전MySQL 5.0 이전MySQL 8.0
주요 사용처MyISAM 백업InnoDB 백업 (XtraBackup 등)

백업 락의 핵심 가치는 복제가 정상 진행되면서 백업 중 DDL만 차단 하는 것입니다. XtraBackup이나 Enterprise Backup 실행 중 스키마 변경이 발생하면 백업이 실패하는데, 백업 락이 이를 방지합니다.

테이블 락 (Table Lock)

개별 테이블 단위로 설정되는 잠금입니다.

-- 명시적 테이블 락
LOCK TABLES table_name [READ | WRITE];
UNLOCK TABLES;
  • 명시적 테이블 락 은 온라인 작업에 큰 영향을 미치므로, 특별한 상황이 아니면 사용하지 않습니다
  • 묵시적 테이블 락 은 MyISAM/MEMORY 테이블에서 DML 실행 시 자동으로 획득/해제됩니다
  • InnoDB는 레코드 기반 잠금을 사용하므로, 단순 DML에서는 묵시적 테이블 락이 설정되지 않습니다. DDL에서만 영향 을 받습니다

네임드 락 (Named Lock)

임의의 문자열 에 대해 잠금을 설정하는 기능입니다. 테이블이나 레코드가 아닌, 사용자가 정의한 문자열에 대한 잠금입니다.

-- 잠금 획득 (2초 대기 후 자동 해제)
SELECT GET_LOCK('mylock', 2);
 
-- 잠금 상태 확인
SELECT IS_FREE_LOCK('mylock');
 
-- 잠금 해제
SELECT RELEASE_LOCK('mylock');
활용 사례
  • 여러 웹 서버 간 동기화 : DB 서버 1대에 웹 서버 5대가 접속하는 환경에서, 동일 작업의 동시 실행을 방지
  • 배치 프로그램의 데드락 방지 : 동일 데이터를 변경하는 배치끼리 분류해서 네임드 락을 걸면 데드락을 회피할 수 있음

MySQL 8.0부터는 네임드 락을 중첩 해서 사용할 수 있으며, RELEASE_ALL_LOCKS()로 한 번에 모두 해제할 수 있습니다.

메타데이터 락 (Metadata Lock)

데이터베이스 객체(테이블, 뷰 등)의 이름이나 구조를 변경할 때 자동으로 획득 되는 잠금입니다.

-- 올바른 방법: 하나의 RENAME에서 두 테이블을 동시에 변경
RENAME TABLE rank TO rank_backup, rank_new TO rank;

위와 같이 한 문장에서 처리하면 rank 테이블이 존재하지 않는 순간이 생기지 않습니다.

-- 잘못된 방법: 두 개의 RENAME으로 분리
RENAME TABLE rank TO rank_backup;   -- 이 순간 rank가 없음!
RENAME TABLE rank_new TO rank;
대용량 테이블 구조 변경 실전 패턴

Online DDL이 너무 오래 걸리는 경우, 새 테이블을 만들고 데이터를 복사한 뒤 RENAME하는 방법을 사용합니다:

-- 1. 새 구조의 테이블 생성
CREATE TABLE access_log_new (...) KEY_BLOCK_SIZE=4;
 
-- 2. 여러 스레드로 데이터 복사 (id 범위별 분할)
-- thread1: INSERT INTO access_log_new SELECT * FROM access_log WHERE id>=0 AND id<10000;
-- thread2: INSERT INTO access_log_new SELECT * FROM access_log WHERE id>=10000 AND id<20000;
-- ...
 
-- 3. 나머지 데이터 복사 + RENAME (서비스 중단 최소화)
SET autocommit=0;
LOCK TABLES access_log WRITE, access_log_new WRITE;
 
SELECT MAX(id) as @MAX_ID FROM access_log_new;
INSERT INTO access_log_new SELECT * FROM access_log WHERE pk > @MAX_ID;
COMMIT;
 
RENAME TABLE access_log TO access_log_old, access_log_new TO access_log;
UNLOCK TABLES;
 
-- 4. 정리
DROP TABLE access_log_old;

이 패턴의 핵심은 잠금 시간을 최소화 하기 위해 대부분의 데이터를 미리 복사해두고, 마지막 소량의 데이터만 잠금 상태에서 복사하는 것입니다.

내 생각

  • mysqldump를 무심코 실행하면 내부적으로 글로벌 락이 걸릴 수 있습니다. --single-transaction 옵션(InnoDB 전용)을 사용하면 글로벌 락 없이 일관된 백업이 가능합니다.

  • 메타데이터 락을 이용한 대용량 테이블 구조 변경 패턴은 실무에서 매우 유용합니다. pt-online-schema-change나 gh-ost 같은 도구도 결국 이 패턴을 자동화한 것입니다.

관련 개념

출처

  • Real MySQL 8.0 (1권), 5.2 MySQL 엔진의 잠금