시리즈
Part 1 : 동시성 문제 - 일반론 (NOW)
Part 2 : 동시성 문제 - 데이터베이스와 JPA
동시성 문제란 두 개 이상의 세션이 공통된 자원에 대해 모두 읽고 쓰는 작업(Read→Write) 을 하려고 하는 경우 발생할 수 있는 문제를 말합니다.
애플리케이션을 개발하다보면 여러 동시성 문제들을 만나고, 또 동시성 제어를 도와주는 여러 시스템들(데이터베이스 시스템, JPA 시스템 등) 을 보게됩니다. 이런 여러 시스템들이 제공하는 상세한 기능들은 모두 상이하지만, 동시성 문제를 정의하는 방식과 제어하는 이론적인 기틀은 모두 동일합니다.
그래서 오늘은 특정 시스템에 종속되지 않는 동시성 문제와 제어 방법론에 대해 정리하려고 합니다.
동시성 문제에 “완전한 해결”은 없습니다. “제어”(적절한 해결) 만 있을 뿐입니다. 동시성 문제는 “정확성 과 활동성 을 어떻게하면 모두 최대로 할 수 있을까?” 에 대한 고민이기 때문입니다. 활동성 (얼마나 빠르게) 을 포기하면 정확성을 높일 수 있습니다. 마찬가지로 정확성(얼마나 올바른 데이터) 을 타협하면 활동성을 높일 수 있습니다.
동시성 문제라고 불리우는 2가지 현상은 바로 “일관성 없는 읽기” 와 “손실되는 업데이트” 입니다.
세션 2는 동시에 실행되고 있는 세션 1 때문에, 데이터에 접근하는 시점마다 다른 값을 읽게 됩니다. 이를 “일관성 없는 읽기” 라고 합니다.
일관성 없는 읽기는 해결이 쉽습니다. 바로 “불변성”, 복사본을 이용하면 됩니다. 세션 2가 최초로 데이터를 조회할 때 해당 데이터를 복사한 후, 이후에도 계속 사용하는 것입니다. 비록 세션 1이 변경한 데이터의 원장은 감지하지 못하더라도, 세션 2 내에서는 계속 동일한 복사본을 바라볼 수 있습니다.
더 늦게 시작한 세션 2에 의해 세션 1의 변경사항이 무시되는 현상을 “손실되는 업데이트” 라고 합니다.
데이터베이스 시스템, JPA 애플리케이션 시스템, 혹은 우리가 직접 조성하는 비즈니스 시스템에서 모두 중요하게 고려되어야 하는 문제입니다.
“손실되는 업데이트” 를 방지하기 위한 방법으로는 크게 2가지가 있습니다. 바로 “낙관적 잠금” 과 “비관적 잠금” 입니다.
“낙관적 잠금” 은 ‘저장 시 체크한다’ 입니다. 세션 1이 데이터 A 를 읽어왔더라도 세션 2는 자유롭게 데이터 A 를 읽어올 수 있습니다. 다만 저장하려고 할 때, 저장하려는 대상 데이터가 세션 2가 들고있던 데이터와 상이하면 저장이 되지 않습니다.
낙관적 잠금을 구현하기 위해서는 공통된 리소스 A 에 대한 Versioning 이 되어야합니다. 세션 2가 들고 있는 A 의 Version 이, 저장하려는 대상 A 의 Version 과 같은지 체크해야하기 때문입니다.
“비관적 잠금” 은 ‘이미 읽고 있는 사람이 있다면, 나는 못읽어’ 입니다. 보수적인 잠금이라고 할 수 있겠죠. 낙관적 잠금에 비관적 잠금은 세션이 실패할 확률은 줄여주지만, 여러 세션의 “활동성” 은 높여주지 못합니다.
💡 다음 글 : 동시성 문제 - 데이터베이스와 JPA (Part 2)
참고 문헌 : 마틴 파울러의 “엔터프라이즈 애플리케이션 아키텍쳐 패턴”