SERIALIZABLE 격리 수준에서는 read -> update 연산이 데드락을 발생시켰습니다.
SERIALIZABLE은 select를 select... for share 로 S lock을 걸고, update 쿼리는 X lock을 걸려 합니다.
하지만 S lock과 X lock은 함께 존재할 수 없기 때문에 다른 트랜잭션과 서로 S lock을 release하길 기다리는 무한 대기 상태로 빠져 데드락이 발생 했습니다.
SERIALIZABLE은 데드락을 발생시키므로 다시 InnoDB의 기본 격리 수준(REPEATABLE READ)로 돌아오겠습니다.
하나의 자원을 공유하는 상황일 때 트랜잭션으로는 동시성 문제를 해결할 수 없었습니다.
때문에 락을 사용해보겠습니다.
낙관적 락을 직접 다루진 않습니다.
낙관적 락을 사용하지 않는 이유
- 낙관적 락은 트랜잭션이 충돌하지 않는다고 가정하기 때문에 충돌이 발생하는 타임딜 프로젝트에는 적합하지 않습니다.
- 현재 프로젝트는 Mybatis를 사용하고 있습니다. JPA와 달리 낙관적 락을 지원하지 않아 직접 구현해야하는 불편함이 있습니다.
- 낙관적 락은 실제로 DB에 락을 걸지 않는 애플리케이션 레벨의 락 입니다. 하지만 특정 경우에는 데드락을 발생 시킵니다.(외래키 변경시 락 전파 주의 참고)
하지만 낙관적 락에 대한 개념은 알아봅시다.
낙관적 락은 버전(Version)으로 관리하는 애플리케이션 레벨의 락 입니다.
필드에 버전이라는 컬럼을 두어 다른 트랜잭션이 동일 조건으로 수정할 수 없도록 막는 방법 입니다.
낙관적 락 예시
update PRODUCT set productUniqueId = ?, stock = ?, ... version = version + 1 where productUniqudId = ?, version = ?(트랜잭션을 시작한 스레드가 조회한 버전)
만약 다른 트랜잭션이 먼저 수정했다면 버전이 이미 증가했기 때문에 해당 쿼리는 실패하게 됩니다.
JPA는 낙관적 락을 사용할 수 있도록 버전에 대한 정보가 있습니다.
외래키를 가진 컬럼을 변경하려고 할 때 해당 컬럼과 연관 관계가 있는 테이블들로 락이 전파됩니다.
이 때 외래키 컬럼에 S lock이 걸리므로 다른 트랜잭션에서 변경을 하려 할 때(X lock 시도) 데드락 상태가 되므로 주의해야 합니다.
- 낙관적 락은 이름은 락이지만 버전을 체크하는 애플리케이션 레벨 락이다.
- 낙관적 락은 특정 경우에 S lock을 걸기 때문에 데드락을 발생시킬 수 있다.(멀티 스레드 환경에서 그런걸까? DB 락을 사용하기 때문에 그런걸까?)