Database/Real MySQL Season1 ,2

7) select for update

Tony Lim 2024. 9. 24. 15:11
728x90

select for update/ share 모두 현재 record에 잠금을 걸어야 하기 때문에 격리수준에 관계 없이
최신 commit을 읽어오게된다.

A의 balance가 100일 때 for update없이 2개의 session에서 select한후에 -100을 수행하면 계좌가 -100인 상태가 되어버린다.

이를 방지하기 위해 select ~ for update를 통해 exclusive lock을 걸어서 더 이상 dml이 작동못하게 막은후에 현 트랜잭션에서만 dml 작업을 수행하게 한다.

굳이 select for update를 쓰지말고 update where절에 조건을 넣어주고 처리된 row 행 숫자 값으로 부가적인 처리를 할 수 있다.

select for update자체에 where 절을 추가해서 불필요한 lock을 잠금한다.

It is not recommended to mix locking statements (UPDATE, INSERT, DELETE, or SELECT ... FOR ...) with non-locking SELECT statements in a single REPEATABLE READ transaction, because typically in such cases you want SERIALIZABLE. This is because a non-locking SELECT statement presents the state of the database from a read view which consists of transactions committed before the read view was created, and before the current transaction's own writes, while the locking statements use the most recent state of the database to use locking. In general, these two different table states are inconsistent with each other and difficult to parse.

repeatable read 에서 그냥 select는 바로 직전까지 commit에 대한 readview를 생성하지만 select for update는 가장 최신 commit에 대한 lock을 잡아서 조금 어려운 상태가 된다고 한다.

 

select for share (Shared Lock)

  • 부모 테이블의 레코드 삭제 방지 
  • 부모 테이블의 select와 자식 테이블의 insert 시점 사이에, 부모 삭제 방지

만약 for share가 없다면 select 와 insert 사이에 다른 트랜잭션에서 부모 테이블인 article table record를 삭제해 버릴 수도 있다. 이를 방지해준다.

하지만 select for share이후 dml (update, delete) 가 필요한 경우 for share를 사용을 자제해야한다.

특정 record에 shared lock 이 걸리고 dml 시행시 exclusive lock도 동시에 걸어줘야한다. (lock upgrade)
이런 상황은 dead lock을 유발한다.

특히 select for share 한 이후 dml까지의 시간간격이 넓어지면 넓어질수록 deadlock 가능성이 높아진다.

 

  • Initial Shared Locks:
    • Transaction T1 reads Row A, acquiring a shared lock on Row A.
    • Transaction T2 reads Row B, acquiring a shared lock on Row B.
  • Attempted Lock Upgrades:
    • T1 attempts to update Row B, needing to upgrade its lock to an exclusive lock on Row B. However, T2 holds a shared lock on Row B, so T1 must wait.
    • T2 attempts to update Row A, needing to upgrade its lock to an exclusive lock on Row A. However, T1 holds a shared lock on Row A, so T2 must wait.
  • Deadlock Situation:
    • T1 is waiting for T2 to release the lock on Row B.
    • T2 is waiting for T1 to release the lock on Row A.
    • Neither transaction can proceed, resulting in a deadlock.

 

JPA Optimisitc vs Pessimistic 

mysql에서 record를 변경할 때 optimistic lock은 있을 수 없는일이다.

jpa 사용시 실제 전달되는 쿼리에 따라 optimistic 이냐 pessimistic이냐가 결정이 된다.

optimisitic 은 다른 트랜잭션에서 동시에 변경(select , update 사이에 끼어들 가능성) 이 낮을 떄 사용된다.

pessimistic 은 다른 트랜잭션도 동일 record를 변경할 가능성이 높다고 판단하면 for update로 미리락을 걸고 들어간다.

table column에 version을 추가해서 관리한다. 

A tx에서 update 쿼리시에 version도 추가해서 이것도 일치해야하만 쿼리를 수행하고 안그러면 ObjectOptimisticLockingFailureException이 던져진다.

중간에 끼어드는 트랜잭션이 많아지면 예외처리가 중요하게 된다.

B tx가 먼저 exclusive lock을 잡고 commit 이 끝나야만 A tx가 락을 잡게된다. 또한 B가 변경한 record를 보게 된다.

Exception이 따로 발생하지는 않지만 트랜잭션 처리시간이 늘어날 수 도 있다.

특정 record가 아닌 여러 record로 트랜잭션이 분산되는 상황이면 거의 잠금없이 빠르게 처리될 수 도 있다.

 

 

 

728x90