STUDY/database

lock을 활용한 concurrency control

까미이모 2023. 12. 11. 23:57

transaction1이 x를 20으로 바꾸는 write를 실행할때, 이것은 단순히 값 하나 바꾸는 것보다 더 복잡한 과정이다. 같은 데이터에 또다른 read/write 가 있다면 예상치 못한 동작을 할 수 있다.

→ Lock을 사용하여 해결할 수 있다

 

Lock 사용설명

데이터마다 Lock이 있어서 변경하거나 읽으려면 lock을 취득해야한다. lock을 취득하지 못하면 취득할때까지 기다려야 한다

 

Lock 사용 예시

예시1) x = 10

Transaction 1 : x를 20으로 바꾼다

Transaction 2 : x를 90으로 바꾼다

Transaction 1  Transaction 2
write_lock(x)
(lock획득)
 
  write_lock(x)
(이미 tx1이 획득했기 때문에 lock획득 실패 → 기다림)
write(x = 20)  
unlock(x)
(lock 해제)
 
  write(x = 90)
(tx1이 lock을 해제하였으니 lock획득하여 진행)
  unlock(x)
(lock 해제)

 

 

예시2) x = 10

Transaction 1 : x를 20으로 바꾼다

Transaction 2 : x를 읽는다

Transaction 1  Transaction 2
write_lock(x)
(lock획득)
 
  read_lock(x)
(읽을 목적으로 lock을 획득하려하지만,
tx1이 쓰기목적으로 lock을 획득하고 있는 상태라 block → 기다림)
write(x = 20)  
unlock(x)
(lock 해제)
 
  read(x) ⇒ 20
(tx1이 lock을 해제하였으니 진행)
  unlock(x)
(lock 해제)

write-lock(exclusive lock)

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

 

read-lock(shard lock)

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

예시) x = 10

Transaction 1 : x를 읽는다

Transaction 2 : x를 읽는다

Transaction 1  Transaction 2
  read_lock(x)
read_lock(x)
(read_lock은 허용)
 
read(x) ⇒ 10 read(x) ⇒ 10
unlock(x) unlock(x)

 

Lock 호환성

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

 

lock을 써도 serializability 가 보장되지 않는다

예시) x = 100, y = 200
Transaction 1 : x와 y의 합을 x에 저장
Transaction 2 : x와 y의 합을 y에 저장

나올수 있는 결과값

  • serial schedule #1 : t1 → t2 순서일때 x = 300, y = 500
  • serial schedule #2 : t2 → t1 순서일때 x = 400, y = 300
Transaction 1  Transaction 2
  read_lock(x)
  read(x) ⇒ 100
  unlock(x)
read_lock(y)  
  write_lock(y)
→ block 기다림
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 이상한 결과값이 된다

그런데 위의 순서에서 tx2의 unlock(x) 와 write_lock(y) 순서를 바꾸면?

Transaction 1 Transaction 2
  read_lock(x)
  read(x) ⇒ 100
  write_lock(y)
read_lock(y)
→ block 기다림
 
  unlock(x)
  read(y) ⇒ 200
  write(y = 300)
  unlock(y)
read(y) ⇒ 300  
unlock(y)  
write_lock(x)  
read(x) = 100  
write(x = 400)  
unlock(x)  

x = 400, y = 300으로 serial schedule #2 결과값이 된다.

 

 

2PL protocol (two-phase locking)

tx에서 모든 locking operation이 최초의 unlock operation보다 먼저 수행되도록 하는 것

Expanding phase(growing phase) : lock을 취득하기만 하고 반환하지 않는 phase

Shrinking phase(contracting phase) : lock을 반환만 하고 취득하지 않는 phase

Serializability 보장

 

2PL과 deadlock

deadlock: 모든 트랜잭션들이 lock을 반납하기를 기다리며 트랜잭션 실행이 멈춰버리는 현상

위의 예시를 2PL protocol 로 진행해보자

Transaction 1 Transaction 2
  read_lock(x)
read_lock(y)  
read(y) ⇒ 200  
write_lock(x)
→ block 기다림
 
  read(x) ⇒ 100
  write_lock(y)
→ block 기다림

이렇게 tx1 은 tx2의 read_lock(x)이 해제되길 기다리고 있고, tx2는 tx1의 read_lock(y)가 해제되길 기다리고 있는 Deadlock 발생 하였다

 

 

2PL protocol의 종류

예시) Transaction : x와 y와 z를 더해서 y에 쓴다, x에 2를 곱해서 z에 쓴다

conservative 2PL

  • 모든 lock을 취득한 뒤 transaction을 시작
  • deadlock-free
  • 실용적이진 않다conservative 2PL
  • read_lock(x) - write_lock(y) - write_lock(z) - read(x) - unlock(x) - read(y) - read(z) - write(y=x+y+z) - unlock(y) - write(z=x*2) - unlock(z)

 

strict 2PL (S2PL)

  • strict schedule을 보장하는 2PL
  • recoverability 보장
  • write-lock을 commit / rollback 될 때 반환strict 2PL (S2PL)
  • read_lock(x) - read(x) - write_lock(y) - read(y) - write_lock(z) - unlock(x) - read(z) - write(y=x+y+z) - write(z=x*2) - commit - unlock(y) - unlock(z)

 

strong strict 2PL(SS2PL or rigorous 2PL)

  • strict schedule을 보장하는 2PL
  • recoverability 보장
  • read-lock / write-lock 모두 commit / rollback될 때 반환
  • S2PL보다 구현이 쉽다
  • read_lock(x) - read(x) - write_lock(y) - read(y) - write_lock(z) - read(z) - write(y=x+y+z) - write(z=x*2) - commit - unlock(x) - unlock(y) - unlock(z)

 

출처: 쉬운코드