[DB] Concurrency Control Techniques (Locking, MVCC..)
동시성 제어는 여러 프로세스나 스레드가 동시에 공유 자원에 접근할 때 발생할 수 있는 문제를 방지하기 위해 필요하다.
동시성을 허용하면 데이터 불일치, 경쟁 조건(Race Condition), 데드락(Deadlock), 정합성(Consistency) 및 무결성(Integrity) 문제가 발생할 수 있다. 반면, 동시성을 과도하게 제어하면 성능 저하 문제가 발생할 수 있어 적절한 균형이 필요하다.
동시성 제어의 방법에는 크게 아래와 같은 방법들이 존재한다.
Concurrency Control Protocols
- Optimistic Concurrency Control
- Locking
- MVCC (Muli-Version Concurrency Control)
- Timestamps
1. Optimistic Concurrency Control
= 낙관적 동시성 제어
데이터에 대한 충돌 가능성이 낮다고 가정하고, 트랜잭션이 실행되는 동안 별도의 락을 걸지 않는 방식이다.
대신, 트랜잭션이 종료될 때(커밋 시점) 변경된 데이터를 검증하여 충돌이 발생했는지 확인한다.
일반적으로 버전 번호(Versioning)나 타임스탬프(Timestamp)를 사용하여 변경 여부를 체크하며, 충돌이 발생하면 트랜잭션을 롤백하고 다시 시도한다.
장점
- 락을 사용하지 않아 성능이 좋고 확장성이 뛰어나다. 특히 읽기 위주의 시스템에서 유리하다.
당점
- 충돌이 자주 발생하면 롤백 비용이 증가하여 성능이 저하될 수 있다.
→ 일반적으로 동시 접근이 많지만, 충돌 가능성이 낮은 환경에서 많이 사용된다.
만일, 충돌 가능성이 높다면? 아래에 나오는 비관적 동시성 제어 방식을 사용하면 된다.
2. Pessimistic Concurrency Control - Locking
= 비관적 동시성 제어
데이터에 대한 충돌 가능성이 높다고 가정하고, 트랜잭션이 실행되는 동안 Lock을 걸고, 완료될때까지 이를 유지하는 방식이다.
Locking Rule
- transaction에서 read(x) 또는 write(x)하기 전에 lock(x)를 수행한다.
- transaction이 종료하기 전에 t에 의해 잠긴 모든 object에 대해 잠금을 해제한다.
- 다른 transaction에 의해 잠긴 object를 잠글 수 없다.
- 다른 transaction에 의해 잠긴 object를 잠금 해지할 수 없다.
Locking Modes
- read_lock(x): shared_lock(공유락)이라고 한다.
: 읽는 동안 다른 Transaction의 write를 방지한다. read의 경우에는 (데이터가 일관되므로) 동시에 처리할 수 있도록 한다.
⇒ data 무결성, 일관성 유지하면서 가능한 (read) 동시성을 제공할 수 있다.
- write_lock(x): exclusive_lock(배타락)이라고 한다.
: 쓰거나 읽는 동안 다른 Transaction의 write/read를 방지한다. 이때, 다른 Transaction은 자신의 차례가 올 때까지 대기해야 한다.
- unlock(x)
: 트랜잭션 커밋과 롤백 시에만 호출해서 락을 해제할 수 있다.
Lock 장점
- 낙관적 동시성 제어(OCC)나 MVCC는 충돌 발생 시 롤백이 필요하지만, 락은 애초에 충돌을 방지하므로 롤백 비용이 발생하지 않는다.
- 락을 사용하면 복잡한 충돌 감지 로직을 구현할 필요가 없다.
- 특정 코드 블록에서 동시에 실행되는 스레드가 하나만 존재하도록 보장하여 경쟁 조건(Race Condition)을 원천적으로 차단할 수 있다.
Lock 단점
- 다수의 스레드가 동일한 리소스를 경쟁적으로 사용하면 락 경합(Lock Contention)이 발생하는 등 락을 획득하고 해제하는 과정에서 시스템 자원이 소비되는 등의 성능 저하 문제가 발생할 수 있다.
- 두 개 이상의 스레드가 서로가 가진 락을 기다리며 영원히 멈추는 Deadlock(교착상태)이 발생할 수 있다.
- 우선순위가 낮은 스레드가 계속해서 높은 우선순위의 스레드에게 밀려 락을 획득하지 못하고 무한 대기하는 Starvation(기아상태)가 발생할 수 있다.
→ 위와 같은 락 경합, 데드락, 기아상태 등의 문제를 해결하기 위해 버전을 사용하는 MVCC가 탄생했다.
데드락의 해결방법이 궁금하다면, 아래 포스팅을 참고하자.
https://persi0815.tistory.com/6
[OS] Deadlock
TerminologyDeadlock은 두 개 이상의 프로세스가 서로의 자원을 필요로 하면서 상대방의 작업 완료를 무한정 기다리는 현상이다. 이 상황에서 각 프로세스는 상대방이 필요로 하는 특정 이벤트를 제
persi0815.tistory.com
Locking에도 여러 방식이 있는데, 이는 아래와 같다.
Locking Protocol
1) Two-Phase Locking Protocol(2PLP)
: 트랜잭션이 Growing Phase(락 획득) → Shrinking Phase(락 해제)의 두 단계를 따르는 방식으로, locking을 통해, Serializable 스케쥴을 얻는 방법이다. (필수조건은 x)
- growing phase: lock만 허용하고 unlock은 허용 x. lock을 걸기 시작하면 계속 lock
- shrinking phase: 한 번 잠금 해제하면 더 이상 새로운 잠금 허용 x. 풀기 기작하면 새로운 lock 불가
한 번 Lock을 해제하면 더 이상 새로운 Lock을 획득할 수 없는 구조로, 성능이 낮고, Deadlock 발생 가능성이 존재하고 Recoverability도 보장이 안된다는 문제가 있다.
-> 추가 제약을 통해 성능과 직렬 가능성을 개선한 방식이 아래와 같은 Conservative 2PL, Strict 2PL, Rigorous 2PL 방식이다.
2) Conservation 2PL (보수적 2PL)
: Transaction이 실행되기 전에 Access 하는 모든 item을 lock하여 Deadlock을 원천 차단하는 방식이다.
하지만, 락을 오래 보유하므로 성능 저하의 가능성이 있다.
3) Strict 2PL
: Transaction이 commit 되거나 abort될 때까지 write lock을 해지하지 않는 방식이다. (read lock은 해지 해준다)
Uncommitted Data(Dirty Read) 문제를 방지하여 회복 가능성(recoverability)을 보장하지만, Deadlock 가능성이 있다.
4) Rigorous 2PL
: Transaction이 commit되거나 abort될 때까지 lock(exclusive or shared)이 해지하지 않는 방식이다.
가장 강력한 동기화와 직렬 가능성을 보장하지만, 성능 저하 가능성이 커서, data에 대한 정확성이 극도로 높아야 하는 경우 사용한다.
3. MVCC (Muli-Version Concurrency Control)
MVCC(Multi-Version Concurrency Control, 다중 버전 동시성 제어)는 데이터의 여러 버전을 유지하여 트랜잭션이 동시에 데이터를 읽고 쓸 수 있도록 하는 방식이다. 이를 위해 원본 데이터와 변경 중인 데이터를 동시에 유지하며, 트랜잭션이 수행되는 동안 각각의 일관된 스냅샷(Snapshot)을 기반으로 데이터를 읽는다.
트랜잭션이 데이터를 읽을 때에는 해당 시점의 데이터 상태(Snapshot)를 기반으로 데이터를 읽어, 다른 트랜잭션이 데이터를 수정하거나 삭제해도 (스냅샷은 트랜잭션이 끝날때까지 유지되기에) 현재 트랜잭션이 보는 데이터는 변하지 않는다.
트랜잭션이 데이터를 수정할 때에는 이전 데이터를 그대로 두고, 새로운 버전을 생성하여 데이터 변경을 진행하고, 트랜잭션이 커밋되면 새로운 데이터가 유효해진다. 해당 버전(데이터)이 유효해진 후, 시작되는 다른 트랜잭션들은 최신 버전의 데이터를 읽게 된다.
즉, 읽기 작업과 쓰기 작업이 충돌 없이 동시에 수행될 수 있으며, 다른 트랜잭션의 변경 사항에 영향을 받지 않는다.
장점
- Lock 없이 읽기와 쓰기 작업을 동시에 수행할 수 있어, 읽기 작업이 블로킹되지 않고 성능이 향상된다.
- 변경 사항을 기존 데이터를 덮어쓰지 않고 새로운 버전으로 관리하므로, 각 트랜잭션이 독립적으로 실행될 수 있다.
단점
- 여러 트랜잭션이 동일한 데이터를 수정할 경우, 최종 커밋 시점에서 충돌이 발생할 수 있다. 이러한 충돌은 자동으로 해결되지 않으며, 애플리케이션 레벨에서 적절한 충돌 해결 로직을 구현해야 한다.
- MVCC는 데이터를 여러 버전으로 관리하는 구조이므로, UNDO 블록 I/O, CR Copy 생성, CR 블록 캐싱 등의 추가 작업이 필요해 부가적인 오버헤드가 발생할 수 있다. 이러한 과정에서 시스템 리소스가 증가할 수 있으며, 특히 고부하 시스템에서는 성능 저하로 이어질 수 있다.
- MVCC는 기존 데이터를 직접 수정하는 대신 새로운 버전을 생성하여 관리하기 때문에, 사용되지 않는 이전 버전의 데이터가 계속 쌓이는 문제가 발생한다. 이를 방지하기 위해 Garbage Collection(Vacuum) 같은 정리 작업이 필요하며, PostgreSQL의 Autovacuum, MySQL InnoDB의 Purge Thread가 이를 수행한다.
참고 자료
https://mangkyu.tistory.com/53
[Database] MVCC(다중 버전 동시성 제어)란?
오늘은 단일 쿼리로는 해결할 수 없는 로직을 처리할 때 필요한 개념인 트랜잭션에 대해 알아보고, Spring에서 어떻게 활용하는지 확인해보도록 하겠습니다. 1. 동시성 제어(Concurrency Control) [ 동시
mangkyu.tistory.com
What is MVCC? How does multiversion concurrency control work?
What is MVCC? (multiversion concurrency control) Multiversion concurrency control (MVCC) is a database optimization technique. MVCC creates duplicate copies of records so that data can be safely read and updated at the same time. With MVCC, database reads
www.theserverside.com