SELECT WITH UPDATE
select * from money_event_code where id = 100 for update;
SQL
복사
MYSQL에서 해당 쿼리를 실행하면 읽기 연산과 함께 두 가지 락이 생성된다.
MYSQL 8.0 이상에서는 performance_schema의 data_locks 테이블에서 락 상태를 조회할 수 있다.
SELECT * FROM performance_schema.data_locks;
SQL
복사
조회 결과에서 두 가지 락을 살펴보자.
{
"LOCK_TYPE": "TABLE",
"LOCK_MODE": "IX",
"LOCK_STATUS": "GRANTED"
}
SQL
복사
첫 번째 락은 테이블 수준 락이다. 트랜잭션이 테이블에 변경 작업을 수행하려고 하면서 설정된다.
Lock Mode는 IX (Intent Exclusive), 즉 의도적 배타 락으로, 테이블의 특정 레코드에 배타적 락을 설정하기 위한 준비 작업이라고 이해할 수 있다. 이는 레코드 단위의 X락과 연계되어 다른 트랜잭션의 동시적인 읽기 작업은 허용하지만 쓰기 작업은 막게 된다.
Lock Status 에서 GRANTED로 정상적으로 락이 획득되었음을 알 수 있다.
{
"LOCK_TYPE": "RECORD",
"INDEX_NAME": "PRIMARY",
"LOCK_MODE": "X,REC_NOT_GAP",
"LOCK_STATUS": "GRANTED",
"LOCK_DATA": "100"
}
SQL
복사
두 번째 락은 레코드 수준의 락이다. 특정 행에 대한 트랜잭션 락이라고 볼 수 있다.
LOCK MODE 에서 X는 Exclusive Lock으로 해당 레코드에 대한 배타적 락이다. 다른 트랜잭션이 해당 레코드를 수정할 수 없다. GAP은 인접한 레코드 사이의 빈 공간에도 락을 설정하도록 하며 GAP 으로 설정되어 있다면 새로운 레코드를 삽입하지 못하도록 한다. 팬텀 리드를 방지하기 위한 용도로 보인다.
INDEX_NAME, LOCK_DATA 은 레코드의 프라이머리 키 값을 기준으로(PK Index) 락을 걸었다고 알려주고 있다.
SELECT FOR UPDATE는 격리 수준과 상관 없이 X락의 획득에 실패하면 Timeout Exceeded 에러를 발생시킨다.
-- Transaction 1
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
SELECT @@transaction_isolation;
start transaction ;
select * from money_event_code where id in (100) for update;
SQL
복사
-- Transaction 2
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
SELECT @@transaction_isolation;
SET SESSION innodb_lock_wait_timeout = 5;
SELECT @@innodb_lock_wait_timeout;
start transaction ;
-- 아래 쿼리에서 에러: Lock wait timeout exceeded; try restarting transaction
select * from money_event_code where id in (100) for update;
SQL
복사