Post

<오라클 성능 고도화 원리와 해법1> Ch02-04 동시성 구현 사례

오라클 성능 고도화 원리와 해법1 - Ch02 트랜잭션과 Lock - 04 동시성 구현 사례

지금까지 동시성 제어의 개념과 기본적인 구현 패턴을 살펴봤고, 이들 개념을 실제 프로젝트에 적용해서 효과적으로 동시성 문제를 해결했던 사례를 2가지만 소개하고자 한다.

(1) 일련번호 채번 동시성 높이기

첫 번째는 Locking을 최소화하면서 채번 테이블로부터 일련번호를 채번하고자 할 때 사용할 수 있는 사례이다. 일련번호를 채번하고자 할 때 가장 좋은 선택은 DBMS가 제공하는 Sequence 기능을 이용하는 것이다. 하지만 어떤 상황에서는 이런 기능을 사용할 수 없는 경우가 존재하고 그럴 때 주로 사용되는 방법이, ①데이터가 삽입되는 시점에 실시간으로 현재의 MAX 값을 취해 1만큼 증가시킨 값을 이용하거나, ②MAX 값을 관리하는 별도의 채번 테이블에서 값을 가져오는 방식이다.

①실시간으로 MAX 값을 얻어 처리할 때는 두 개의 트랜잭션이 동시에 같은 값을 읽었을 경우, insert하려는 순간 PK 제약에 위배되므로 예외(Exception) 처리를 통해 그다지 어렵지 않게 동시성을 제어할 수 있다. 하지만 ②채번 테이블을 사용할 때는 채번 후 다음 처리로 진행하기 전에 채번 테이블 값이 1만큼 증가시키는 갱신을 수행해야 하는 어려움이 있다.

문제는 커밋 시점인데, 만약 앞서 정의한 seq_nextval 함수처럼 라인 14에서 커밋을 한다면, 어떤 이유에서 메인 트랜잭션(바로 위 소스 코드)이 라인 4 insert 이후에 롤백될 경우 문제가 생긴다. 라인 2에서의 update는 이미 커밋된 상태가 되어 데이터의 일관성이 깨지기 때문이다.

그렇다고 seq_nextval 함수 라인 14에서 커밋을 안 하고 함수를 빠져나오면 어떻게 될까? 아래 메인 트랜잭션이 모두 종료될 때까지 채번 테이블에 Lock이 걸린 상태가 유지되므로 동시 채번이 빈번히 발생하는 상황에서 심각한 성능 저하를 일으키게 될 것이다.

다행히 오라클은, 메인 트랜잭션에 영향을 주지 않고 서브 트랜잭션만 따로 커밋하는 기능을 제공하는데, 이를 ‘autonomous 트랜잭션’이라고 한다. 위에서 정의한 seq_nextval 함수에서 3번 라인에 사용된 주석을 제거해주면 된다. 직접 테스트를 통해 사용 방법을 익히고 실제 업무에 적용한다면 트랜잭션 동시성을 향상시키는 데에 큰 효과를 거둘 수 있다.

(2) 선분 이력 정합성 유지

두 번째는, 선분 이력을 추가하고 갱신할 때 발생할 수 있는 동시성 이슈를 해결한 사례이다.

선분 이력 모델은 여러 측면에서 장점이 있지만, 잘못하면 데이터 정합성이 쉽게 깨질 수 있다는 단점이 있다. 선분 이력을 모델로 채택하면서 이를 정확히 핸들링하는 방법들을 개발 초기에 미리 교육하지 않아 문제가 발생하는 프로젝트를 자주 본다. 아래 모델을 예로 들면서, 선분 이력이 동시성과 관련해 어떤 문제를 일으킬 수 있고 어떻게 해결할 수 있는지 살펴보기로 하자.

따라서 부가 서비스 이력의 상위 엔터티인 고객 테이블에 Lock을 걸면 완벽하게 동시성 제어를 할 수 있다.

또 다른 상위 엔티티인 부가 서비스는 여러 사용자가 동시에 접근할 가능성이 커서 여기에 Lock을 설정하면 동시성에 나빠질 수 있지만, 고객 테이블은 그럴 가능성이 희박하기 때문에 동시성에 미치는 영향은 거의 0에 가깝다.

이외에도 선분 이력의 정합성을 유지하기 위해 사용할 수 있는 몇 가지 솔루션이 더 있지만, 지면을 통해 설명하기에는 다소 복잡한데다 본서는 성능 문제를 다루는 책이므로 데이터 정합성을 위해 너무 많은 지면을 할애할 수 없는 한계가 있다. 다 소개하지 못하는 게 아쉽지만 다른 저서나 강좌를 통해 소개할 것을 약속한다.

This post is licensed under CC BY 4.0 by the author.