Lock
lock 은 DB에서 사용하는 개념으로 트랜잭션 처리의 순차성을 보장하기 위한 방법 중 하나이다.
이러한 DB의 특징을 Atomic하다고 할 수 있다. (앞선 글에서 봤던 원자성)
Synchronized
- 현재 데이터를 사용하는 스레드를 제외하고, 나머지 스레드의 접근을 막아 순차적으로 데이터에 접근하도록 해준다.
- @Transactional 어노테이션과 사용하면 안된다.
- 선언적 트랜잭션 방식은 프록시로 수행되는데, 프록시에는 synchronized 가 안먹힌다.
- 단점은 하나의 프로세스에서만 락이 보장된다. 여러 개의 인스턴스가 있는 운영환경에서는 정합성이 보장되지 않는다.
비관적 Lock
- 실제로 데이터에 락을 걸어서 정합성을 맞추는 방식이다.
- 자원 요청에 따라서 동시성 문제가 발생할 것이라고 "예상" 하고 락을 거는 방식이다.
- DB의 격리성 수준은 Reeatable Read or Serializable 정도
- 비관적 락을 걸면, 다른 트랜잭션에서 락이 해제되기전에 데이터를 가져갈 수 없다.
- 그러나 데이터 자체에 락을 걸기에 속도가 느리다.
- 동시성 문제가 잦은 경우에는 RollBack 횟수를 줄일 수 있어 낙관적 락보다는 성능이 더 좋다.
- 롤백 시에는 하나의 트랜잭션으로 묶이기에 데이터베이스가 롤백된다.
예를 들어보자.
- transaction 1 에서 id 가 1인 데이터를 select
- transaction 2 에서 id가 1인 데이터를 select
- transaction 2 에서 id가 1인 데이터에 대해서 update 처리를 요청, 그러나 transaction 1 에서 이미 lock을 잡고 있기에 Blocking 처리가 되고 대기
- transaction 1에서 transaction 을 해제 (commit)
낙관적 Lock
- 데이터의 버전으로 데이터 정합성을 준수하는 방법이다.
- 데이터를 조회하고 업데이트를 수행할 때, 내가 조회한 버전이 맞는지 확인하고, 아니라면 대기후에 재시도를 한다.
- 장점은 정합성 충돌이 잦지 않다는 전제하에 별도의 락을 걸지 않기에 비관적 락에 비해 성능이 좋다.
- 단점은 업데이트를 실패하고 재시도를 하는 로직을 개발자가 직접 구현해야한다.
- 충돌이 잦으면 롤백처리가 잦기에, 비관적 락이 더 좋다.
- 롤백시에 트랜잭션이 필요하지 않기에 개별적인 롤백을 할 수 있으나 위에 말하였듯 직접 구현해야 한다.
예를 들어서 좀 더 쉽게 이해해보자.
- transaction 1 에서 id가 1 인 데이터를 select
- transaction 2 에서 id가 1 인 데이터를 select
- transaction 2 에서 id가 1인 데이터를 갱신, 성공 (ver 1 -> ver 2)
- transaction 1 에서 id가 1인 데이터의 row를 찾지 못하며 갱신에 실패 (ver이 2로 바뀌었기 때문)
낙관적 락은 꼭 version이 아니라 timeStamp등을 사용하기도 한다.