우선 문제상황은 분산 환경에서 여러대의 서버가 하나의 데이터베이스에 접근한다고 할 때, 서버 A가 DB에 접근해서 SELECT를 한 뒤, 거의 동시에 서버 B가 같은 DB table에 접근해서 SELECT를 했다면 아래와같은 상황이된다.
서버 A | 서버 B | |
---|---|---|
id | 1 | 1 |
상품 수량 | 100 | 100 |
상품 식별번호 | 1234 | 1234 |
아래의 표처럼 둘이 동일한 정보를 읽게되버린다. 위의 상태에서 각각의 서버에 수량을 5씩 감소시키는 명령이 들어온다면, 예상한 결과는 각각 5씩 감소시켜 90이 되야하지만 실제로는 95가된다.
이런 형태이다.
서버 A | 서버 B | |
---|---|---|
id | 1 | 1 |
상품 수량 | 95 | 95 |
상품 식별번호 | 1234 | 1234 |
각각 100에서 5씩 감소시킨 95를 db에 기록하여 문제가 생긴다. 해당 사항을 어떻게 해결해야할까? 그때 사용하는 방법중 하나가 lock이다. 다른 종류도 있지만, 추후에 설명하고 이 글에서는 비관적 락, 낙관적 락만 설명하겠다.
비관적 락을 걸게될 경우, db의 row자체에 lock을 걸어 row를 가져간 트랜잭션의 commit이 완료될때까지 설정에 따라 읽기나 쓰기같은 행위가 동작하지않는다. spring으로 동작시켰을때는 쿼리가
select * from product ~~~ for update;
이런식으로 날아갔었다.
낙관적 락을 이용할 경우에는 Product Entity에 version 컬럼을 하나 만들어, 실제로 커밋을 했을때 버전 정보에 문제가 없는지 검사하여 로직을 짜봤고, 둘 다 동시성을 보장하며 제대로 작동하는것을 확인했다.
아래는 해당 테스트의 깃허브 링크다.
https://github.com/xanqus/spring-concurrency-problem_exam