> For the complete documentation index, see [llms.txt](https://yihyuns-gitbook.gitbook.io/today-i-learned/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yihyuns-gitbook.gitbook.io/today-i-learned/database/database_lock.md).

# Database Lock

## 데이터베이스 락 (Database Lock)

여러 사용자가 같은 데이터에 접근할 때, 데이터 무결성을 보장하기 위해 데이터에 접근(쓰기/읽기)하지 못하도록 데이터를 잠그는(Lock) 행위를 말한다.

## 락이 필요한 이유

멀티 쓰레드 환경에서 동시에 특정 데이터가 수정된다면 데이터 무결성이 깨지는 문제가 발생할 수 있다.

간단한 예시로, 쇼핑몰에서 재고가 1개 남았으며 상품 구매 트랜잭션에서는 다음의 순서로 쿼리를 수행한다고 하자.

```sql
SELECT 재고 수 FROM 재고; -- 1. 재고 개수 확인
UPDATE 사용자 SET 잔액 = 잔액-상품가격; -- 2. 결제
UPDATE 재고 SET 재고 수 = 0; -- 3. 재고 업데이트
INSERT INTO 주문 VALUES 주문자, 상품, ...; -- 4. 주문 생성
```

그리고 거의 동시에 요청된 사용자 A, B의 주문에 의해 다음의 순서로 쿼리가 처리된다고 하자.

```sql
A가 재고 개수 확인 (1)
B가 재고 개수 확인 (1)
A가 결제
B가 결제
A가 재고 업데이트 (0)
B도 재고 업데이트 (0이 덮어씌워짐)
A의 주문 생성
B의 주문 생성
```

재고는 1개인데 두 사람 모두 구매에 성공하고 말았다. 이는 하나의 재고에 대해 중복된 주문이 생성된 것으로 명백히 데이터 무결성이 침해된 것이다.

이러한 문제 상황의 예방을 위해 `동시성 제어`를 해야 하고, 이를 위한 방법이 바로 데이터베이스에 `락(Lock)`을 거는 것이다.

## 데이터베이스 락의 종류

락의 종류로는 두 가지가 있다.

### 낙관적 락 (Optimistic Lock)

* 낙관적 락은 말 그대로 낙관적으로 생각하는 잠금 기법이다.
* 낙관적 락에서는 데이터 갱신 시 경합(충돌)이 발생하지 않을 것이라고 가정한다.
* 따라서, 데이터베이스를 잠그는 대신 버전 정보를 관리하며 데이터 갱신 시 자신이 조회한 데이터와 버전 일치 여부를 확인하고, 일치한다면 데이터 갱신과 함께 버전 정보도 갱신한다. 일치하지 않는다면 데이터 갱신 내역은 롤백(RollBack)한다.
* 낙관적 락은 `동시에 데이터가 갱신될 일이 거의 없는 경우` 사용한다.

### 비관적 락 (Pessimistic Lock)

* 비관적 락은 말 그대로 비관적으로 생각하는 잠금 기법이다.
* 비관적 락에서는 데이터 갱신 시 경합(충돌)이 발생할 확률이 높다고 가정한다.
* 따라서, 데이터를 갱신할 때 해당 DB/테이블/레코드/필드를 잠근다.
* 해당 데이터를 갱신하려는 다른 사용자는 잠금이 해제될 때까지 대기한다.
* 비관적 락은 `동시에 데이터가 갱신될 일이 잦은 경우` 사용한다.
* 주로 멀티 쓰레드 환경에서는 동시에 데이터 갱신을 요청하는 일이 많다. 따라서, 비관적 락이 주로 사용된다.
* 비관적 락은 다시 `공유 락(Shared Lock)`과 `배타 락(Exclusive Lock)`으로 나뉜다.

## 배타 락 연산의 종류

### 공유 락 (Shared Lock)

* 공유 락은 데이터를 안전하게 읽기 위한 락이다.
* 해당 데이터를 읽는 것(SELECT)만 허용하고, 쓰는 것(INSERT/UPDATE/DELETE)은 막는다.
* 공유 락 끼리는 동시 접근이 가능하다. 여러 사용자가 동시에 데이터를 조회하는 것은 무결성을 침해하지 않기 때문이다.

### 배타 락 (Exclusive Lock)

* 배타 락은 데이터를 안전하게 쓰기 위한 락이다.
* 해당 데이터를 읽는 것, 쓰는 것을 모두 막는 락이다.
* 배타 락은 공유 락과도, 배타 락 끼리도 동시 접근이 불가능하다. 데이터를 새로 쓸 때에는 조회하거나 동시에 수정하는 행위 모두 무결성을 침해할 수 있기 때문이다.

## 락의 적용 가능 범위

* 락은 DB, 파일, 하나의 행(레코드), 하나의 필드 단위로 적용할 수 있다.
* 큰 범위에 적용할 수록 구현이 단순해지지만 성능이 저하된다. (병행 가능성 낮아짐)
* 반대로 작은 범위에 적용할 수록 구현이 복잡해지지만 성능이 좋아진다. (병행 가능성 높아짐)

## 성능 저하 요소

### Blocking

* 접근하려는 데이터에 락이 걸려 있어 대기하는 상태를 말한다.
* 해당 데이터의 락이 해제되면(진행 중이던 트랜잭션이 commit되면 보통 해제됨) 블로킹도 해제된다.

### Deadlock

* 서로 다른 락이 서로 다른 데이터 제어권을 선점한 상태에서 상대의 데이터를 갱신하려 할 때, 누구도 제어권을 얻지 못해 영원히 블로킹 상태가 되는 경우를 말한다.
* A가 데이터1에 락을 걸고, B가 데이터2에 락을 걸고, A는 데이터2를 요구하고, B는 데이터1을 요구하는 경우를 생각해보자. 결국 A와 B 모두 자신의 락을 해제하지 않고 상대의 락이 해제되길 기다리므로 누구도 트랜잭션을 완료할 수 없다.
* 데드락은 극심한 성능 저하(심하면 서버 장애)로 이어지므로, 다음과 같은 해결방법을 시도해 최대한 해결하는 것이 좋다.
  * 트랜잭션이 너무 오래 지속되면 타임아웃 예외를 발생시키도록 하여 교착상태를 강제종료한다.
  * 데이터의 접근 순서를 최대한 동일하게 유지한다. 위의 예시에서는 A와 B 모두 데이터1, 2를 사용해야 하므로, 둘 다 1→2 순서로 사용하도록 하면 데드락이 발생할 일이 없다.

## 결론

* Lock은 데이터 무결성 보장을 위해 약간의 성능 저하를 감내하는 기법이다. 성능 저하를 최소화하기 위해서는 트랜잭션을 최대한 효율적으로 설계하는 방법 뿐이다.
* 자신이 주로 사용하는 DBMS에서 어떤 Lock을 지원하는지 알고 있는 것이 좋다.

## Reference

[\[10분 테코톡\] 마루의 데이터베이스 Lock](https://www.youtube.com/watch?v=ZXV6ZqMyJLg)
