본문 바로가기
Computer Science/Database

[DB] Lock을 활용한 Concurrency Control 기법

by HelloJudy 2023. 12. 24.

다시 돌아온 CS 공부하는 주디!

블로그 포스팅 스터디를 하고 있지만 이미

벌금 12000원을 납부한 그녀..

 

🤖 : 야 너 취업하더니 의지가 다 어디 갔냐

🐰 : 칼퇴해도 운동하고 집 가면 하루가 없다고!!!

🤖 : 변명이야!!! 열공해라 주디

🐰 : ㄴ ㅔ . . .

 

 

오랜만에 CS 공부하러 출~발~


⛄️ DB Locking

오늘은 Lock을 활용한 Concurrency Control 기법을 공부해보려고 한다.

 

Concurrency Control 기법?! 오!! 이거 주디 블로그 시리즈물 아닌가?

맞다!!

 

아래 내용을 참고하면 된다!


 

[DB] 트랜잭션 #1 | 개념과 ACID 속성
[DB] 트랜잭션 #2 | concurrency control(Serializability, Recoverable)
[DB] 트랜잭션 #3 | Isolation level(격리 수준)과 이상 현상 (+면접 질문)

 

 

🔒 Lock

우선 Lock이 무엇인지 알아보자.

 

데이터를 읽고 쓰기 위해서는 반드시 Lock을 취득해야 한다.

 

X = 20이라는 데이터가 있다.

이때 X를 20으로 바꾸려는 1번 트랜잭션이 있고,

X를 80으로 바꾸려는 2번 트랜잭션이 있다.

 

같은 데이터에서 X를 쓰기 하려고 하기 때문에 예상치 못한 동작을 할 수 있다.

그래서 이 문제를 Lock을 사용하여 해결할 수 있다.

 

 

✔️ Lock을 활용한 쓰기

 

- 1번 트랜잭션, 2번 트랜잭션 모두 write 작업을 하는 경우

[X = 10] write_lock(X) - write_lock(X)(1번 lock이 풀릴 때까지 기다림) - write(X = 20) - unlock(X) - write(X = 90)

 

- 1번 트랜잭션은 write 작업, 2번 트랜잭션은 read 작업을 하는 경우

[X = 10] write_lock(X) - read_lock(X)(1번 lock이 풀릴 때까지 기다림)write(X = 20) - unlock(X) - read(X) => 20

 

 

[ write_lock(X) ]

Exclusive Lock : 다른 트랜잭션이 동시에 read나 write 작업을 할 수 없는 lock

 

  • read / write(insert, modify, delete) 할 때 사용한다.
  • 다른 tx 같은 데이터를 read / write 하는 것을 허용하지 않는다.

 

[ read_lock(X) ]

Shared Lock : 다른 트랜잭션이 동시에 read 작업을 할 수 있는 lock

 

  • read 할 때 사용한다.
  • 다른 tx이 같은 데이터를 read 하는 것을 허용한다.

 

✔️ Lock의 호환성

 

트랜잭션1이 read-lock을 취득한 상태에서는 다른 트랜잭션은 read-lock만 획득 가능하고 write-lock은 획득할 수 없다.

트랜잭션1이 write-lock을 취득한 상태에서는 모든 lock을 획득할 수 없다.

  read-lock write-lock
read-lock O X
write-lock X X

 

 

👻 Lock을 써도 발생하는 이상현상

 

우리의 최종 목표는 동시성을 제어하려고 하는 것이다.

이때 중요한 개념 중에 하나가 Serializable이다. (자세한 내용은 여기 정윤 포스팅에서!)

 

복수의 병렬 트랜잭션은 서로 격리되어 마치 순차적으로 실행되는 것처럼 작동되어야 하고, 데이터베이스는 여러 사용자가 같은 데이터에 접근할 수 있어야 한다.

즉, 병렬적으로 작동하지만 순차적으로 실행했을 때와 결과가 같아야 한다는 것이다.

 

Serial schedule은 순차적으로 실행하기 때문에 데이터의 정합성이 보장되지만,

디스크 I/O 작업과 같이 오래 걸리는 작업을 하는 동안 CPU가 놀고 있다면 결국 좋은 성능을 낼 수 없다.

 

그러면 상황을 하나 살펴보자!

 

현재 DB에는 X = 100, Y = 200으로 저장되어 있다.
Transaction 1 : X와 Y의 합을 X에 저장
Transaction 2 : X와 Y의 합을 Y에 저장
[T1] read_lock(Y) - read(Y) - unlock(Y) - write_lock(X) - read(X) - write(X = X + Y) - unlock(X)
[T2] read_lock(X) - read(X) - unlock(X) - write_lock(Y) - read(Y) - write(Y = X + Y) - unlock(Y)

 

 

1) 순차적으로 실행한 경우 :  T1 -> T2 순으로 작업 실행

 

X에 Y를 합해야 되기 때문에 Y부터 읽어와서 합한 값을 X에 쓰기 작업한다.

=> X = 300, Y = 500

 

 

2) 순차적으로 실행한 경우 :  T2 -> T1 순으로 작업 실행

 

X에 Y를 합해야 되기 때문에 Y부터 읽어와서 합한 값을 X에 쓰기 작업한다.

=> X = 400, Y = 300

 

 

3) 두 트랜잭션이 같이 실행된 경우

read_lock(X) - read(X) => 100 - unlock(X) - read_lock(Y) - write_lock(Y)(트랜잭션1을 기다림) - read(Y) => 200 - unlock(Y) - read(Y) => 200 write(Y = 300) - unlock(Y) - write_lock(X) - read(X) => 100 - write(X = 300) - unlock(X)

 

=> X = 300, Y = 300

 

순차적으로 실행했을 때랑 X와 Y 결과와 다르기 때문에 Lock을 사용했지만 Nonserializable 인 것을 알 수 있다.

 

💡 이 문제를 해결하기 위해서는 

unlock(X)과 write_lock(Y)의 순서를 바꿔준다. 그러면 트랜잭션1은 트랜잭션2로 인해 이미 업데이트된 값을 읽어와서 위의 문제를 해결할 수 있다.

 

바뀐 순서로 다시 보면

read_lock(X) - read(X) => 100 - write_lock(Y) - read_lock(Y)(트랜잭션2를 기다림) -  unlock(X) - read(Y) => 200 - write(Y = 300)unlock(Y) - read(Y) => 300 - write_lock(X) - unlock(Y) - read(X) => 100 - write(X = 400) - unlock(X)

 

 

✔️ 2PL protocol (two-phase locking)

 

여기서 알게 되는 점은 트랜잭션에서 모든 Locking Operation이 최초의 unlock operation 보다 먼저 수행되도록 한다.

이것을 2PL protocol이라고 한다.

 

간단하게 말하면 한 번 unlock을 할 때에는 다시 새로운 lock을 획득하지 않는 방법이다.

 

왜냐하면 트랜잭션1을 보면

 

[ Expanding phase(growing phase) ]

 

read_lock(X) - read(X) => 100 - write_lock(Y) 

lock을 취득만 하고 반환하지 않는 phase다.

 

[ Shrinking phase(contracting phase) ]

 

unlock(X) - read(Y) => 200 - write(Y = 300) unlock(Y)

lock을 반환만 하고 취득하지는 않는 phase다.

 

이 방법으로 Serializable를 보장할 수 있다.

 

하지만 이 방법 역시 특정 상황에서 이상 현상이 발생한다.

 

🤖 : 갑자기 튀어나온 로봇. 삐빅 결론부터 말해라!!
🐰 : Dead Lock 발생!!

 

 

✔️ Deadlock 발생 상황

 

두 트랜잭션 모두 2PL protocol로 동작하는 상황이다.

아래 상황에서 밑줄 친 부분에서 서로 상대방이 가지고 있는 lock이 unlock 되길 기다리며 멈춰있다.

Deadlock이 발생했다.

read_lock(X) - read_lock(Y) - read(Y) => 200 - write_lock(X) - read(x) => 100 - write_lock(Y)

 

이 Deadlock을 해결하는 방법은 OS에서 Deadlock 해결 방법과 비슷하다.

 

👉 정윤 포스팅 확인: [OS] 교착상태 (Deadlock)

 

 

여기서 더 나아간 방법이 MVCC이다.

 

지금의 많은 DB가 Lock과 MVCC를 함께 활용하고 있다.

MVCC는 다음에 더 다뤄보도록 하겠다~

 


📌 Reference

 

반응형

댓글