Pessimistic Locking vs. Optimistic Locking

  • 낙관적인 락, 비관적인 락.

  • 들어가기 전에,

    • 아마도 락에 대한 처리를 어떻게 할지에 대한 개념일듯.

    • 락에 대해 낙관적으로 생각하느냐, 락에 대해 비관적으로 생각하느냐로 이해됨.

About

  • 두가지 모두 Lock을 어떻게 다룰 것인가에 대한 모델

    • 데이터베이스 기본 기능은 아닌듯. 즉 데이터베이스에서 기본적으로 낙관적인 락 혹은 비관적인 락을 사용하겠다고 선택하는 옵션은 없는듯.

    • 단지 개념적으로는 락(Lock)을 바라보는 관점

  • 낙관적인 락

    • 비선점 잠금.

    • 변경 사항이 데이터베이스에 커밋될 때만 레코드가 잠길 것임.

      • 락에 대해 조금 느슨하게 바라보는 듯, 트랜잭션 간이 충돌이 발생하지 않을 것.

      • 즉 락 설정에 문제가 없을 거다!

    • 변경사항이 있어도, 아직 커밋이 안되었으면 잠겨있지 않을듯.

  • 비관적인 락

    • 션점 잠금

    • 레코드가 변경이 되고 있다면, 잠겨있을 거임. 즉 Lock이 걸려 있을 것.

    • 트랜잭션 간 충돌이 일어날 것이라 생각하고 (비관적), 락을 좀 미리 빡세게 걸어놓는 느낌.

    • 낙관적인 락 보다는 좀 더 엄격한 느낌.

    • 만약에 어떤 레코드가 수정 중이라면 (아직 커밋이 안된 상태) 락이 걸려있을 거라 기대하는 모델인듯.

Optimistic locking

  • physical, logcial 적으로 구현이 가능한듯.

  • 그렇지만 여러모로 Logical을 사용하는 게 좀 더 효율적으로 볼 수 있을듯

    • 즉 Database 자체 Lock 보다는, 락 처럼 구현한 (Logical) 걸 사용하는게 낫다는 의미인듯.

  • 그래서 위 그림으로 보면, version 이라는 컬럼을 통해, Logcial Lock을 구현 (JPA에서..)

    • commit 순간 version 을 증가시킴으로써, 다른 Update 문에 version = 1 조건이 되어있는 구문은 무시됨을 알 수 있음.

    • 이럴 경우 PreapredStatement 는 0을 리턴하고 data access framework (예를 들면 JPA..?) 에서는 OptimisticLockException 을 던짐.

    • 그럴 경우 Alice 가 진행하던 Transaction 은 롤백됨.

    • 적용하려고 했던 update문에 대한 정보가 없어지니까.. 이 부분에 대한 처리가 application 에서 필요할 지도..

In JPA

  • 보통 엔티티의 @Version 필드를 이용해서 사용함.

  • 그래서 업데이트 전에, 해당 필드를 체크하는 방식을 이용

  • 4가지 Lock Mode를 제공하고 있음.

    1. OPTIMISTIC

    2. optimistic read lock

    3. 비선점 락

    4. 해당 모드를 사용하면, dirty read or non-repeatable read 가 발생하지 않도록 막아줌.

    5. 즉 커밋되지 않은 데이터를 읽는 것을 방지하거나, 한 트랜잭션 내에서 같은 쿼리가 반복 실행 될 때 동일한 값이 나오도록 일관성을 보장해준다는 말.

    6. OPTIMISTIC_FORCE_INCREMENT

    7. OPTIMISTIC 를 기반으로, 추가적인 버젼 증가된 entity가 반환됨.

    8. 커밋이나 플러시 되기 전까지는 에러가 발생할지 특정할 수 없음.

    9. READ

    10. OPTIMISTIC의 alias

    11. WRITE

    12. OPTIMISTIC_FORCE_INCREMENT 의 alias

  • JPA 스펙에서는 READ, WRITE 타입을 사용하기 보다는 1,2번 처럼 명시적인 타입을 사용하는걸 권장하는 듯.

  • 발생할 수 있는 에러

    • OptimisticLockException

    • 해당 에러가 던져졌을 때, 현재 트랜잭션은 rollback only로 mark됨.

    • 보통 이 에러는, 다시 엔티티를 조회하거나 (새로운 트랜잭션으로), 그 후에 업데이트 하도록 권장됨.

  • 정리하자면..

    • 기본적으로 JPA에서는 OPTIMISTIC, OPTIMISTIC_FORCE_INCREMENT 타입이 제공되고 있음.

    • 사용방법은 Query 인터페이스에서 지정하거나, 엔티티매니저를 통해 이용하는 방법도 있는듯.

    • 그리고 보통 에러 발생하면, 추가적으로 어플리케이션에서 핸들링이 필요한 것으로 보이고.

    • 데이터베이스에서 제공하는 락 (s lock, x lock) 을 사용하는 것이 아니므로, deadlock같은 상황이 발생하지 않을 것 같음.

Pessimistic lock

  • 비관적인 락

    • 즉 락이 걸릴 것을 예상하고, 미리 락을 선점함.

  • 하나의 레코드에 동시에 업데이트가 되는 것을 막기 위해 사용됨.

  • 어떤 유저가 어떤 레코드에 업데이트를 실행하면, 해당 레코드에는 락이 걸리고, 이 락이 풀어지기 전(이 전 유저의 커밋이 완료될 때 까지) 에는 다른 유저가 해당 레코드에 업데이트를 실행할 수 없음.

    • Shared, Exclusive lock..

    • 실질적으로 데이터베이스에서 해당 행에 락을 건다고 이해됨.

  • 락이 걸려있을 테니, 충돌을 방지할 수 있음.

    • 트랜잭션 간 충돌을 예방할 수 있음.

    • 그렇지만 성능적으로는 별로 안 좋지 않을까? (락을 걸고, 확인하고 혹은 푸는 것 자체가 꽤 리소스가 소모되지 않을까 생각됨.)

    • 왜? 락을 걸고, 풀고, 혹은 다른 트랜잭션에서 해당 레코드에 락이 걸려있는 지 확인하는 작업이 필요할 테니..

  • 위 첨부된 그림을 보면, Bob의 UPDATE 구문의 트랜잭션 락은, Alice 의 커밋이 완료되기 전까지는 획득할 수 없음.

    • 그런데 만약에 Alice가 commit이 어떤 이유에서 그런지 끝나지 않는다면..?

    • 일정 시간이 지나면 Dead Lock 같은게 발생하지 않을까?

In JPA

  • 3가지 LOCK MODE 타입을 제공하고 있음.

    1. PESSIMISTIC_READ

    2. shared lock (s lock) 사용. 그래서 해당 row의 update or delete를 방지함.

    3. dirty read 를 방지하기 위해서 사용함.

    4. PESSIMISTIC_WRITE

    5. exclusive lock (x lock) 사용. read, update, deleted 모두 막음

    6. 즉 read, update, delete action을 하기 위해서는 무조건 해당 락을 획득해야됨.

    7. 단 어떤 데이터베이스는 해당 락을 사용하고 있어도, read 할 수 있도록 지원해주는 듯 (MVCC)

    8. PESSIMISTIC_FORCE_INCREMENT

    9. PESSIMISTIC_WRITE 를 기반으로 entity의 version 필드를 이용해 버져닝 기능도 지원

    10. @Version 사용

    11. update 시에, 락 (x lock) 획득이 필요한 동시에, version 필드의 매칭도 필요함 (좀 더 안전한듯.)

  • 발생할 수 있는 에러

    1. PessimisticLockException

    2. LockTimeoutException

    3. PersistanceException

    4. 위 에러들은 현재 트랜잭션 상태를 rollback only 상태로 만들어놓음.

  • Lock Timeout 셋팅

    • LockTimeoutException 발생을 위해 설정이 필요한듯.

    • javax.persistence.lock.timeout 의 값을 이용하는 데, 단위는 milliseconds

    • 데이터베이스가 해당 셋팅을 지원하지 않는 경우도 있으니, 확인이 필요함.

  • 정리하자면..

    • 여러 트랜잭션에서, 같은 리소스에서 같은 타임에 접근하지 않도록 데이터베이스 차원에서 락을 이용해서 해결하는 방법을 지원해줌.

    • 사용방법은 위에서 언급한 것과 비슷 (Query, 엔티티매니저...등등)

    • 데이터베이스의 락을 사용하니, deadlock이 발생할 수도 있겠고, 이러한 상황에 대처하기 위해서 timeout 설정이 필수적으로 보임.

추가적인 키워드

  • Shared Lock, Exclusive Lock

  • Isolation Level

  • MVCC

  • Dead Lock

참고

Last updated