Loading...
MySQL 9.5 Reference Manual 9.5의 10.11.4 Metadata Locking의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
MySQL은 메타데이터 락킹을 사용하여 데이터베이스 오브젝트에 대한 동시 접근을 관리하고 데이터 일관성을 보장합니다. 메타데이터 락킹은 테이블뿐만 아니라 스키마, 저장 프로그램(프로시저, 함수, 트리거, 스케줄된 이벤트), 테이블스페이스, GET_LOCK() 함수로 획득한 사용자 락(자세한 내용은 Section 14.14, “Locking Functions” 참조), 그리고 Section 7.6.8.1, “The Locking Service”에 설명된 락킹 서비스로 획득한 락에도 적용됩니다.
Performance Schema의 metadata_locks 테이블은 메타데이터 락 정보를 노출하며, 어떤 세션이 락을 보유하고 있는지, 어떤 세션이 락을 기다리느라 블록되어 있는지 등을 확인하는 데 유용합니다. 자세한 내용은 Section 29.12.13.3, “The metadata_locks Table”를 참조하십시오.
메타데이터 락킹에는 일부 오버헤드가 수반되며, 쿼리 양이 증가함에 따라 그 오버헤드도 증가합니다. 여러 쿼리가 동일한 오브젝트에 접근하려 할수록 메타데이터 경합이 증가합니다.
메타데이터 락킹은 테이블 정의 캐시를 대체하는 것이 아니며, 여기서 사용하는 뮤텍스와 락은 LOCK_open 뮤텍스와는 다릅니다. 다음 설명에서는 메타데이터 락킹이 어떻게 작동하는지에 대한 정보를 제공합니다.
특정 락에 대해 대기 중인 웨이터가 여러 개 있는 경우, 가장 높은 우선순위를 가진 락 요청이 먼저 만족되며, 여기에는 max_write_lock_count 시스템 변수와 관련된 예외가 있습니다. 쓰기 락 요청은 읽기 락 요청보다 우선순위가 높습니다. 그러나 max_write_lock_count가 어떤 낮은 값(예: 10)으로 설정된 경우, 읽기 락 요청이 이미 10개의 쓰기 락 요청에 대해 건너뛰어졌다면 보류 중인 쓰기 락 요청보다 읽기 락 요청이 우선될 수 있습니다. 일반적으로 max_write_lock_count의 기본값이 매우 큰 값이기 때문에 이러한 동작은 발생하지 않습니다.
스테이트먼트는 메타데이터 락을 동시에가 아니라 하나씩 획득하며, 이 과정에서 데드락 감지를 수행합니다.
DML 스테이트먼트는 보통 스테이트먼트에서 테이블이 언급된 순서대로 락을 획득합니다.
DDL 스테이트먼트, LOCK TABLES 및 기타 유사한 스테이트먼트는 명시적으로 지정된 테이블에 대해 이름 순으로 락을 획득하여 동시 DDL 스테이트먼트 간에 발생할 수 있는 데드락의 수를 줄이려 합니다. 외래 키 관계에 있는 테이블처럼 암묵적으로 사용되는 테이블의 경우(이들 역시 락이 필요함)에는 락이 다른 순서로 획득될 수 있습니다.
예를 들어, RENAME TABLE은 이름 순으로 락을 획득하는 DDL 스테이트먼트입니다:
RENAME TABLE 스테이트먼트는 tbla를 다른 이름으로 바꾸고, tblc를 tbla로 바꿉니다:1RENAME TABLE tbla TO tbld, tblc TO tbla;
이 스테이트먼트는 이름 순서에 따라 tbla, tblc, tbld에 대해 메타데이터 락을 획득합니다(tbld가 이름 순서에서 tblc 뒤에 오기 때문입니다).
tbla를 다른 이름으로 바꾸고, tblc를 tbla로 바꿉니다:1RENAME TABLE tbla TO tblb, tblc TO tbla;
이 경우, 스테이트먼트는 이름 순서에 따라 tbla, tblb, tblc에 대해 메타데이터 락을 획득합니다(tblb가 이름 순서에서 tblc보다 앞에 오기 때문입니다).
두 스테이트먼트 모두 tbla와 tblc에 대해 이 순서대로 락을 획득하지만, 나머지 테이블 이름에 대한 락이 tblc 이전에 획득되는지 이후에 획득되는지만 다릅니다.
여러 트랜잭션이 동시에 실행될 때 메타데이터 락 획득 순서는 작업 결과에 차이를 만들어낼 수 있으며, 이는 다음 예에서 설명합니다.
동일한 구조를 가진 두 테이블 x와 x_new로 시작한다고 가정합니다. 세 개의 클라이언트가 이들 테이블을 포함하는 스테이트먼트를 실행합니다:
Client 1:
1LOCK TABLE x WRITE, x_new WRITE;
이 스테이트먼트는 이름 순서에 따라 x와 x_new에 대한 쓰기 락을 요청하고 획득합니다.
Client 2:
1INSERT INTO x VALUES(1);
이 스테이트먼트는 x에 대한 쓰기 락을 요청하고, 이를 기다리며 블록됩니다.
Client 3:
1RENAME TABLE x TO x_old, x_new TO x;
이 스테이트먼트는 이름 순서에 따라 x, x_new, x_old에 대해 배타 락을 요청하지만, x에 대한 락을 기다리며 블록됩니다.
Client 1:
1UNLOCK TABLES;
이 스테이트먼트는 x와 x_new에 대한 쓰기 락을 해제합니다. Client 3의 x에 대한 배타 락 요청은 Client 2의 쓰기 락 요청보다 우선순위가 높으므로 Client 3은 먼저 x에 대한 락을 획득하고, 이어서 x_new와 x_old에 대한 락도 획득한 후 rename 작업을 수행하고 락을 해제합니다. 그 후 Client 2가 x에 대한 락을 획득하고 insert를 수행한 뒤 락을 해제합니다.
락 획득 순서의 결과로 RENAME TABLE이 INSERT보다 먼저 실행됩니다. insert가 수행되는 x는 Client 2가 insert를 실행했을 당시 x_new라는 이름을 가지고 있다가 Client 3에 의해 x로 rename된 테이블입니다:
1mysql> SELECT * FROM x; 2+------+ 3| i | 4+------+ 5| 1 | 6+------+ 7 8mysql> SELECT * FROM x_old; 9Empty set (0.01 sec)
이제 동일한 구조를 가진 x와 new_x라는 이름의 테이블로 시작한다고 가정합니다. 다시 세 개의 클라이언트가 이들 테이블을 포함하는 스테이트먼트를 실행합니다:
Client 1:
1LOCK TABLE x WRITE, new_x WRITE;
이 스테이트먼트는 이름 순서에 따라 new_x와 x에 대한 쓰기 락을 요청하고 획득합니다.
Client 2:
1INSERT INTO x VALUES(1);
이 스테이트먼트는 x에 대한 쓰기 락을 요청하고, 이를 기다리며 블록됩니다.
Client 3:
1RENAME TABLE x TO old_x, new_x TO x;
이 스테이트먼트는 이름 순서에 따라 new_x, old_x, x에 대해 배타 락을 요청하지만, new_x에 대한 락을 기다리며 블록됩니다.
Client 1:
1UNLOCK TABLES;
이 스테이트먼트는 x와 new_x에 대한 쓰기 락을 해제합니다. x에 대해서는 보류 중인 요청이 Client 2의 요청뿐이므로 Client 2가 락을 획득하고 insert를 수행한 뒤 락을 해제합니다. new_x에 대해서는 보류 중인 요청이 Client 3의 요청뿐이므로 Client 3이 그 락(및 old_x에 대한 락) 획득을 허용받습니다. rename 작업은 여전히 x에 대한 락을 기다리며 블록되는데, 이는 Client 2의 insert가 완료되고 락을 해제할 때까지입니다. 이후 Client 3은 x에 대한 락을 획득하고 rename을 수행한 뒤 락을 해제합니다.
이 경우, 락 획득 순서의 결과로 INSERT가 RENAME TABLE보다 먼저 실행됩니다. insert가 수행되는 x는 원래의 x이며, rename 작업에 의해 old_x로 이름이 변경된 테이블입니다:
1mysql> SELECT * FROM x; 2Empty set (0.01 sec) 3 4mysql> SELECT * FROM old_x; 5+------+ 6| i | 7+------+ 8| 1 | 9+------+
위 예에서와 같이 동시에 실행되는 스테이트먼트의 락 획득 순서가 애플리케이션의 작업 결과에 차이를 만들어낸다면, 락 획득 순서에 영향을 주기 위해 테이블 이름을 조정할 수 있습니다.
메타데이터 락은 외래 키 제약 조건으로 연결된 테이블에 대해서도 필요에 따라 확장되며, 관련 테이블에서 상충하는 DML 및 DDL 작업이 동시에 실행되지 못하도록 합니다. 부모 테이블을 업데이트할 때, 외래 키 메타데이터를 업데이트하는 동안 자식 테이블에 대해 메타데이터 락이 획득됩니다. 외래 키 메타데이터의 소유자는 자식 테이블입니다.
트랜잭션 직렬 가능성을 보장하려면, 서버는 하나의 세션이 다른 세션에서 명시적 또는 암묵적으로 시작된 미완료 트랜잭션에서 사용 중인 테이블에 대해 DDL 스테이트먼트를 실행하는 것을 허용해서는 안 됩니다. 이를 위해 서버는 트랜잭션 내에서 사용되는 테이블에 대해 메타데이터 락을 획득하고, 트랜잭션이 종료될 때까지 해당 락의 해제를 지연시킵니다. 테이블에 대한 메타데이터 락은 그 테이블의 구조 변경을 방지합니다. 이러한 락킹 방식의 결과로, 한 세션의 트랜잭션에서 사용 중인 테이블은 트랜잭션이 종료될 때까지 다른 세션에서 DDL 스테이트먼트에 사용될 수 없습니다.
이 원칙은 트랜잭션 테이블뿐만 아니라 비트랜잭션 테이블에도 적용됩니다. 예를 들어, 한 세션이 다음과 같이 트랜잭션 테이블 t와 비트랜잭션 테이블 nt를 사용하는 트랜잭션을 시작한다고 가정합니다:
1START TRANSACTION; 2SELECT * FROM t; 3SELECT * FROM nt;
서버는 트랜잭션이 종료될 때까지 t와 nt 모두에 대한 메타데이터 락을 유지합니다. 다른 세션이 두 테이블 중 하나에 대해 DDL 또는 쓰기 락 작업을 시도하면 트랜잭션 종료 시 메타데이터 락이 해제될 때까지 블록됩니다. 예를 들어, 두 번째 세션이 다음 작업 중 어느 하나라도 시도하면 블록됩니다:
1DROP TABLE t; 2ALTER TABLE t ...; 3DROP TABLE nt; 4ALTER TABLE nt ...; 5LOCK TABLE t ... WRITE;
동일한 동작은 LOCK TABLES ...\ READ에도 적용됩니다. 즉, 어떤 테이블(트랜잭션 또는 비트랜잭션)을 업데이트하는 명시적 또는 암묵적으로 시작된 트랜잭션은 해당 테이블에 대해 LOCK TABLES ... READ와 서로 블록하고 블록됩니다.
서버가 문법적으로는 유효하지만 실행 중 실패하는 스테이트먼트에 대해 메타데이터 락을 획득한 경우, 락을 미리 해제하지 않습니다. 실패한 스테이트먼트는 바이너리 로그에 기록되며, 해당 락은 로그 일관성을 보호하므로 락 해제는 여전히 트랜잭션 종료 시점까지 지연됩니다.
오토커밋 모드에서는 각 스테이트먼트가 사실상 완전한 트랜잭션이므로, 스테이트먼트에 대해 획득된 메타데이터 락은 스테이트먼트 종료 시점까지만 유지됩니다.
PREPARE 스테이트먼트 중에 획득된 메타데이터 락은 스테이트먼트 준비가 완료되면 해제되며, 준비가 다중 스테이트먼트 트랜잭션 내에서 이루어진 경우에도 마찬가지입니다.
PREPARED 상태의 XA 트랜잭션의 경우, 메타데이터 락은 클라이언트 연결 해제 및 서버 재시작 이후에도 XA COMMIT 또는 XA ROLLBACK이 실행될 때까지 유지됩니다.
10.11.3 Concurrent Inserts
10.11.5 External Locking