작성
·
273
0
생각하다 보니까 고립도라는 것은 근본적으로 이런 경우에는 이런 고립도만 쓰지 않으면 100% 안전하기 때문에, 쓸 수 있는 고립도 중 가장 성능이 좋은 고립도를 쓰는게 아니라, 어떤 고립도를 쓰더라도 확률적으로 위험한 경우가 발생할 수 있지만 잘 일어나지 않기 때문에 Serializable 아닌 성능적으로 빠른 REPEATABLE READ 등을 사용하는 것인지 궁금합니다.
======================
제가 생각한 일반적인 예는 아래와 같습니다.
아버지와 아들이 있습니다.
아들은 다음날 수학 여행에 필요한 버스비가 1000원이라고 아버지께 말해놓았습니다. 그래서 아버지는 저녁때 아들의 계좌를 확인해서 1000원으로 맞춰놓기로 약속하셨습니다.
그날 저녁 아버지의 행동을 함수로 구현하면 아래와 같습니다.
아들 계좌를 확인하고 1000원이 되도록 채워주기_f()
{
select해서 아들 계좌의 돈을 확인합니다.
1000 - 아들 계좌의 돈 만큼 다시 아들 계좌로 송금합니다.
}
그날 저녁 아들 계좌에는 700원이 있었는데 자기 돈을 사용해서 200원짜리 포켓몬 빵을 사먹었습니다. (아들의 update문)
그런데 그때 하필 아버지가
아들 계좌를 확인하고 1000원이 되도록 채워주기_f()
를 실행한 겁니다.
아버지가 select 할 때는 아들 계좌는 700원이었습니다. 그래서 아버지는 300원만 더 넣어주면 1000원이 되겠군, 이라고 결론을 내리고 300원을 아들 계좌로 송금합니다.
300원을 송금하는 부분을 쪼개어보면 select 해서 아들의 계좌 잔액을 가져오고 거기에 300원을 더한 값을 set 하는 방식이었습니다. (SQL에 add라는 기능이 없기 때문에)
그래서 결국 락이 풀림과 동시에 set 1000이 될 겁니다. 아들은 분명 200원짜리 포켓몬을 샀는데도 계좌에는 800원이 아닌 1000원이 남게 됩니다. 무에서 유가 창출되는 사건이 발생하는 것입니다!
============================================
위의 예처럼 update문에만 락이 걸려도 무결성에 위배되는 경우가 생길 것 같습니다. Serializable이라는 고립 레벨이 있긴 해도 성능상의 이유로 Serializable은 잘 안 쓴다고도 알고 있습니다. 그럼 Serializable 아닌 경우라면 위와 같이 절묘하게 타이밍만 맞아 떨어지면 데이터 무결성이 깨지는 경우가 발생할 수 있다는 것인데... 그럼 일반적으로 REPEATABLE READ를 사용할 수 있는 논리와 아닌 논리로 나누어져 있다기 보다는 위와 같이 절묘한 순간이 잘 일어니지 않기 때문에 성능적으로 더 좋은 REPEATABLE READ 등을 사용한다고 보면 되나요?
답변 1
4
안녕하세요. 컴퓨터공부하자님
결론부터 말씀드리면, 생각하신 예시는 트랜잭션 입장에서는 순차적으로 실행이 된 것이기 때문에 아무런 문제가 없습니다^^
질문하신 부분은 사실 일반적인 트랜잭션으로 해결 가능한 범위를 넘어갑니다.
이것은 두 번의 갱신 분실 문제(second lost updates problem)이라는 것인데요.
예를 들어서 A 사용자가 공지사항을 편집하고, B 사용자가 같은 공지사항을 편집하고 있다고 할께요.
그렇다면 동시에 같은 공지사항을 저장하게 되면, 결과적으로 처음 저장한 사용자의 데이터는 사라지게 됩니다.
이런 부분은 트랜잭션 입장에서는 A 사용자의 글을 저장하고, 그 다음에 B 사용자의 글을 저장한 것이기 때문에 아무런 문제가 없습니다.
트랜잭션 격리 레벨과 두 번의 갱신 분실 문제에 대해서 궁금하신 부분을 더 자세히 이해하려면 JPA 책 16.1 트랜잭션과 락 부분을 자세히 읽어보시는 것을 권장드립니다.
감사합니다.
감사합니다!