본문 바로가기
공부/RDBMS

[DB] MySQL 8.0 잠금

by persi0815 2026. 1. 14.

MYSQL의 내부 동작과 그 원리에 대해 궁금해져, Real MySQL 8.0 책을 읽었는데 정리를 안하니 계속 내용이 휘발되어서 내 방식으로 정리해보고자 글을 쓴다. 


 

잠금은 동시성을 제어하기 위한 기능으로, 여러 커넥션에서 동시에 동일한 자원을 요청할 경우 순서대로 한 시점에는 하나의 커넥션만 변경할 수 있게 해주는 것이다. 

 

MySQL에서 사용하는 잠금은 크게 스토리지 엔진 레벨 MySQL 엔진 레벨로 나눌 수 있다. 

- MySQL 엔진 레벨: 글로벌 락 , 테이블 락, 테이블 메타데이터 락, 네임드 락

- 스토리지 엔진 레벨: 레코드 락, 갭락, 넥스트 키 락, 자동 증가 락

이들에 대해 자세히 알아보자!

 

1. MySQL 엔진 레벨의 락

 

[ 글로벌 락 ]

가장 범위가 큰 잠금이다. MySQL  서버 전체에 영향을 미치며, 테이블이나 데이터베이스가 달라도 영향을 미친다. 한 세션이 획득하면, 다른 세션에서 SELECT를 제외한 대부분의 DDL과 DML이 글로벌 락이 해제될 때까지 대기 상태가 된다.

 

FLUSH TABLES WITH READ LOCK 명령으로 획득할 수 있다. 

 

InnoDB 엔진이 트랜잭션을 지원하기에 일관된 데이터 상태를 위한 글로벌 락은 필요가 없어졌고, 좀 더 가벼운 글로벌 락인 백업 락이 도입됐다.

 

[ 테이블 락 ]

개별 테이블 단위로 설정되는 잠금이며, 명시적 또는 묵시적으로 획득할 수 있다.

- 명시적으로는 LOCK TABLES table_name [ READ | WRITE ]로 획득 가능하고, UNLOCK TABLES로 해제 할 수 있다. 다만, 특별한 사용이 아니면 명시적으로 사용할 일은 거의 없다. 

- 묵시적으로는 MyISM이나 MEMORY 테이블에 데이터 변경하는 쿼리 실행시 발생한다. InnoDB의 경우 스토리지 엔진 차원에서 레코드 기반의 잠금을 지원하기에 단순 데이터 변경 쿼리로 인해 락이 설정되지는 않는다. 

 

*InnoDB 스토리지 엔진은 상위 레이어인 MySQL 엔진에서 관리되는 테이블 잠금은 볼 수 없어서 데드락 감지가 불확실할 수 있는데, innodb_table_locks 시스템 변수를 활성화하면, InnoDB 스토리지 엔진 내부의 레코드 잠금과 테이블 레벨의 잠금 모두를 감지할 수 있다. 

 

[ 네임드 락 ]

임의의 문자열에 대해 잠금을 설정하는 기능인데, GET_LOCK() 함수로 획득할 수 있다. 즉, 잠금의 대상이 데이터베이스 객체가 아니라 사용자가 지정한 문자열에 대해 획득하고 반납하는 구조다. 흔히 분산락이라고 불리며 데이터베이스 서버 1대에 여러 웹서버가 접속해서 서비스 하는 상황에 상호 동기화(동시성 처리)를 처리해야 할 때 자주 사용된다. 

 

특정 시간 동안만 잠금을 획득할 수도 있고, 잠금이 설정돼 있는지 확인도 가능하다. 

> SELECT GET_LOCK('mylock', 2); // 2초 동안만 잠금 획득. 이후 해제됨

> SELECT IS_FREE_LOCK('mylock'); // 해당 문자열에 대해 잠금 설정돼 있는지 확인

더불어, 중첩해서 여러 네임드 락을 사용할 수도 있으며, 현재 세션에서 획득한 네임드 락을 한 번에 모두 해제할 수도 있다. 

> SELECT RELEASE_ALL_LOCKS();

 

[ 메타 데이터 락 ]

데이터베이스 객체(ex. 테이블, 뷰)의 이름이나 구조를 변경하는 경우에 자동으로 획득하는 잠금이다.

이름을 바꿀 때에는 원본 이름과 변경될 이름 모두 한꺼번에 잠금이 설정되고, 이를 통해 tab_a 테이블이 존재하지 않는 순간을 없앨 수 있다. ex. RENAME TABLE tab_a TO tab_b, tab_c TO tab_a; 

구조를 바꿀 때에는 MySQL서버의 DDL이 단일 스레드로 작동하기에 시간이 많이 소요될 수 있어, 새로운 구조의 테이블을 생성하고, id값을 범위로 나눠서 여러개의 스레드로 빠르게 복사한 뒤, INSERT하는 것이 좋다. 

 

2.  스토리지 엔진 레벨의 락

InnoDB 스토리지 엔진은 MySQL에서 제공하는 잠금과는 별개로 스토리지 엔진 내부에서 "레코드 기반의 잠금 방식"을 탑재하고 있다. 해당 잠금에 대한 정보는 MySQL 명령을 이용해 접근하기가 까다로운데, information_schema 데이터베이스의 INNODB_TRX, INNODB_LOCKS, INODB_LOCK_WAITS 테이블을 이용하면 해당 잠금을 어느 트랜잭션이 가지고 있는지 확인할 수 있고, 잠금을 가진 클라이언트를 찾아서 종료시킬 수도 있다. 

 

락을 직접 걸어보고 분석해보고 싶다면 아래 글을 참고하자.

https://persi0815.tistory.com/161 

 

[DB] MySQL 락 걸어보고 분석해보기!

MySQL InnoDB의 잠금에 대해 공부하다가 직접 락을 확인해보고 싶어서 실습해보았다. 1. Autocommit이란? Auto commit은 DML문이 실행될 때, 사용자가 명시적으로 저장 명령을 내리지 않아도 즉시 DB에 영구

persi0815.tistory.com

 

 

대표적인 락으로는 레코드락, 넥스트 키 락, 갭락이 있다. 자세히 알아보자!

 

 

[ 레코드 락 ]

InnoDB의 잠금은 레코드 자체를 잠그는 것이 아니라 "인덱스의 레코드"를 잠그는 방식으로 처리된다. 즉, 변경해야 할 레코드를 찾기 위해 검색한 인덱스의 레코드에 모두 락을 걸어야 한다. 만일 테이블에 인덱스가 하나도 없다면, 내부적으로 자동 생성된 클러스터 인덱스를 통해 잠금을 설정한다(인덱스를 통하지만 결국 테이블을 풀 스캔하게 되고 모든 레코드를 잠그게 된다). 그래서 쿼리를 위한 적절한 인덱스가 준비되어 있지 않다면, 각 클라이언트간 동시성이 상당히 떨어질 수도 있다. 

 

[ 갭락 ]

레코드와 바로 인접한 레코드 사이의 간격만을 잠그는 것이다. 이를 통해 레코드와 레코드 사이의 간격에 새로운 레코드가 INSERT 되는 것을 제어할 수 있다. 그 자체보다 넥스트 키 락의 일부로 자주 사용된다. 

 

[ 넥스트 키 락 ]

레코드락과 갭 락을 합쳐 놓은 형태의 잠금이다.

STATEMENT 포맷의 바이너리 로그를 사용하는 MySQL 서버에서는 REPEATABLE READ 격리 수준을 사용해야 하는데, 이때 변경을 위해 검색하는 레코드에 넥스트 키 락 방식으로 잠금이 걸린다. 바이너리 로그에 기록되는 쿼리가 레플리카 서버에서 실행될 때 소스 서버에서 만들어 낸 결과와 동일한 결과를 만들어내는 데에 사용된다. 왜냐하면 레플리카 서버에서는 바이너리 로그를 통해 소스 서버와 같은 상태를 만드는데, 이때 STATEMENT 포맷(SQL 문장 자체)의 바이너리 로그를 사용하는 경우, 레코드 사이의 갭에 락을 걸지 않으면 같은 STATEMENT를 실행시켜도 다른 결과가 도출될 수 있기 때문이다.

*MySQL의 Master-Slave의 복제(Replica) 과정에 대해 궁금하다면, 효율적인 MySQL 성능이라는 책의 7장을 참고하자. 

 

그래서 가능하다면, 바이너리 로그 포맷을 ROW 형태로 바꾸어 넥스트 키 락과 갭락을 줄이는 것이 좋다. ROW 형태는 변화한 ROW 자체를 바이너리 로그에 기록하기에 복제시에 항상 같은 결과를 만들어낼 수 있기 때문이다. 

*MySQL 8.0에서는 ROW 포맷의 바이너리 로그가 기본 설정으로 변경됐다. 

 

[ 자동 증가 락 ]

MySQL에서는 자동 증가하는 숫자 값을 추출하기 위해 AUTO_INCREMENT 라는 칼럼 속성을 제공한다. 이를 위해 InnoDB에서는 내부적으로 Auto increment lock이라고 하는 '테이블 수준의 잠금'을 사용한다. (테이블에 하나)

다른 잠금과 달리 트랜잭션과 관계없이 INSERT나 REPLACE와 같이 새로운 레코드를 저장하는 쿼리에서 AUTO_INCREMENT 값을 가져오는 순간만 락이 걸렸다가 쿼리가 완료되면 해제된다. 다만, 시스템 변수를 통해 INSERT되는 레코드의 건수를 정확히 예측할 수 있을 때에는 자동 증가 락 대신 더 가볍고 빠른 뮤텍스를 통해 처리하게 할 수 있다. 대량의 INSERT가 수행될 때에는 InnoDB가 여러 개의 자동 증가 값을 한번에 할당 받아서 사용할 수도 있다. 또한, ID의 유니크함을 보장하면서 INSERT문 여러개가 동시에 수행되게끔할 수도 있다.(8.0 기준 해당 설정이 기본값이다) 다만, 이때는 연속된 자동 증가 값을 보장하진 못한다.