[DB] DB Lock 무엇인가?
🔒 락 (Lock) 이란?
여러 커넥션에서 동시에 동일한 자원을 요청할 경우 순서대로 하나의 커넥션만 변경할 수 있게 해주는 기능.
동시성을 제어하기 위한 기능
락을 비유하는 말 중에서 '내가 화장실 칸을 사용하기 위해 화장실 칸을 직접 들어가서 내가 잠그는 것'의 관점으로 이해하면 편하다..!
✨ Lock의 종류
데이터베이스의 락은 크게 다음과 같은 종류로 분류할 수 있다.
👥 공유 락 (Shared Lock)
공유 락은 데이터를 변경하지 않는 읽는 명령에 대해 주어지는 락으로 Read Lock이라고도 불리며 Shared의 앞 글자를 따서 주로 S로 표기한다. 여러 사용자가 동시에 데이터를 읽어도 데이터의 일관성에는 아무런 영향을 주지 않기 때문에 , 공유 락끼리는 동시에 접근이 가능하다.
🫸 베타 락 ( Exclusive Lock, Wirte Lock, X-Lock)
베타 Lock은 데이터를 변경하는 작업을 위해 잠그는 것을 말한다. 이는 Write Lock으로도 불리며, X로 표기한다. 베타 락은 이름처럼 다른 세션이 해당 자원에 대한 접근 ( SELECT, INSERT) 하는 것을 막는다.
🔄 락 flow
예시) 세션1에서 회원 데이터를 money를 500원으로 바꾸고 싶고, 세션2는 같은 회원 데이터의 money 1000원으로 바꾸고 싶을 때, 베타락이 이루어진다. 자원을 동시에 사용하는 경우이기에 이러한 경우 데이터 베이스는 락(Lock)이라는 개념을 제공한다.
동시에 데이터를 수정할 때, 어떻게 해당 방법을 해결하는지 살펴보자.
세션1에서 가장 먼저, 트랜잭션이 시작함.
세션1은 회원에 대한 데이터를 money 500으로 변경을 시도한다. 이때 해당 로우의 락을 가장 먼저 획득하게 된다. 락이 남아 있으므로 세션1은 락을 획득한다.
세션 1은 락을 획득했으므로 해당 로우에 update sql을 수행한다.
이 때, 세션2는 해당 회원에 데이터 money 1000으로 변경을 시도한다. 해당 로우의 락을 얻으려고 시도한다. 하지만, 락이 없으므로 락이 올 때까지 대기한다.
세션2는 무한정 기다리는게 아니라, 락 대기 시간을 넘어가면 락 타임아웃 오류가 발생한다.
테이블 생성문이다.
create table member (
id varchar(10),
money integer not null default 100,
primary key (id)
);
우리는 이걸 가지고 실습을 해보려고 한다.
h2 데이터 베이스를 가지고 할 예정
h2 데이터베이스를 두개 띄웠다.
기본 데이터 설정
set autocommit true;
delete from member;
insert into member(id, money) values ('둥',10000);
set autocommit false;
update member set money=500 where id = '둥';
SET LOCK_TIMEOUT 60000;
set autocommit false;
update member set money=1000 where id = '둥';
락 예시 그림
대기 중인 모습
60초 후, 락이 안와서 에러를 뱉는 걸 볼 수 있음.
commit 을 해준다면 아래에서 락이 죽지 않고 성공적으로 반영되는 것을 확인할 수 있다.
🔥 S-Lock, X-Lock 경쟁 여부 관계
S-Lock | X-Lock | |
S-Lock | X | O |
X-Lock | O | O |
1. 여러 트랜잭션이 한 row 에 S Lock을 걸 수 있다.
: 여러 트랜잭션이 한번에 여러 row 읽을 수 있음.
2. S Lock이 걸려 있는 row 에 다른 트랜잭션이 X Lock 을 걸 수 없음.
: 다른 트랜잭션이 읽고 있는 row를 수정/삭제 불가능
3. X Lock 이 걸려 있는 row에 다른 트랜잭션이 S Lock, X Lock을 걸 수 없다.
: 다른 트랜잭션이 수정/삭제 하고 있는 row 는 읽기, 수정, 삭제 모두 불가능
Intention Lock
Table-Level Lock : row에 대해서 나중에 어떤 row-level 락을 걸 것을 알려주기 위해 미리 table-level에 걸어두는 것
SELECT ... LOCK IN SHARE MODE
1. Intention Shared Lock(IS) 테이블에 걸림
2. row-level에 S-Lock걸림.
MySQL
START TRANSACTION;
-- 특정 레코드를 읽고 공유 잠금을 설정
SELECT * FROM employees WHERE id = 10 LOCK IN SHARE MODE;
-- 공유 잠금 상태에서 트랜잭션 커밋 또는 롤백 전까지 다른 트랜잭션이 수정할 수 없음
COMMIT;
PostgreSQL
BEGIN;
-- 특정 레코드를 읽고 공유 잠금을 설정
SELECT * FROM employees WHERE id = 10 FOR SHARE;
-- 트랜잭션이 종료되기 전까지는 해당 레코드를 수정하거나 삭제할 수 없음
COMMIT;
SELECT ... FROM UPDATE, INSERT, DELETE 실행
1. intention exclusive lock(lx) 이 테이블에 걸림
2. row-level 에 X-Lock 이 걸림
IS, IX 락은 여러 트랜잭션에서 동시에 접근이 가능하지만, row-level의 실제 락인 S, X락에서 접근 제어를 하게 됨
LOCK TABLES, ALTER TABLE, DROP TABLE이 실행 될 때는 IS, IX를 모두 block 하는 table-level 락이 걸린다.
row-level 및 Table-level 에서 두번 Lock 하는 이유
SELECT ... FROM UPDATE
BEGIN;
-- 특정 레코드를 조회하고 수정할 수 없도록 잠금을 설정
SELECT * FROM employees WHERE id = 10 FOR UPDATE;
-- 이 트랜잭션이 완료되기 전까지 다른 트랜잭션은 해당 레코드를 수정할 수 없음
COMMIT;
A 트랜잭션에서 이미 테이블에 대해 락이 걸려있는데,
B 트랜잭션에서 해당 테이블의 특정 row에 lock을 거는것을 원천적으로 방지 할 수 있다.
X | IX | S | IS | |
X | O | O | O | O |
IX | O | X | O | X |
S | O | O | X | X |
IS | O | X | X | X |
Record Locks
Record Lock은 index record 잠금이다. 예를 들어, SELECT a1 from s1 where a1.v1 = 10 from update; 인 경우 a1.v1 값이 10인 레코드에 잠금이 걸린다. 테이블에서 따로 인덱스를 정의해주지 않더라도 mysql 에는 clustered index를 생성해, record lock을 위해 해당 인덱스를 사용한다.
Gap Locks
Gap Lock은 인덱스 사이의 범위에 잠금을 거는 것을 말한다. 예를 들어 SELECT a1 from s1 where a1 BETWEEN 3 AND 9 FOR UPDATE; 를 트랜잭션에서 실행하고, B 트랜잭션에서 INSERT INTO s1(a1) VALUES(6); 을 실행하면 s1 3과 9사이 인덱스에 락이 걸려 있으므로 INSERT문이 대기하게 된다. 이외에 UPDATE, DELETE 문도 대기함.
격리 수준을 READ_COMMITED로 변경하여, gap lock을 비활성화
락 내용이 너무 방대하여 데드락등은 뒤에서 정리하겠습니다. 일단 1차 작성 -완-