티스토리 뷰
동일한 데이터 레코드에 대해 서로 다른 두 세션이 동시에 접근해 중복처리가 이루어지는 문제가 발생했다. 이 레코드에는 처리상태(미처리, 처리완료)를 나타내는 컬럼이 존재한다. 그러나 첫 세션에서 해당 컬럼이 수정되고 commit이 일어나기 전에 또 다른 세션에서 이 레코드에 대한 처리가 시도되었다. 두 번째 세션에서도 이 레코드를 미처리 상태의 데이터로 인지하여 결국 한 번만 실행되었어야 하는 프로세스가 중복 수행되었다.
Oracle에서는 이러한 문제를 보완할 수 있는 한 가지 방안을 제시한다. 데이터의 수정이 일어나게 될 대상 레코드에 Lock을 설정해 이 레코드의 수정작업이 완료되기 전 까지는 타 세션의 접근(엄밀히 말하면 SELECT)을 제한하는 것이다.
SELECT * FROM TBL WHERE ... FOR UPDATE |
이와 같이 SELECT 문장 맨 뒤에 FOR UPDATE 라는 구문을 명시해주면 Oracle은 WHERE 조건을 만족하는 레코드에 Lock을 설정하고, 현재 세션이 종료될 때 까지는 해당 레코드에 대한 모든 접근을 대기시킨다.
이 구문을 활용하면 거의 동일한 시간에 발생하는 같은 데이터의 중복처리를 방지할 수 있다. 단, 활용할 때에는 다음과 같은 내용에 유의해야 한다.
1. Lock을 설정하는 시점을 잘 설정할 것
- 'Lock을 설정하는 시점' 은 곧 FOR UPDATE 구문이 포함된 SELECT문이 실행되는 시점이다. 이 때부터 해당 레코드에 대해 추가로 유입되는 세션은 대기 상태가 된다.
- 때문에 중복처리 방지를 위한 로직은 Lock이 설정되는 시점 이후에 존재해야 한다.
/*
CASE 1 ================================================================
LockSelect 문장으로 시작하는 경우 BizLogic 1, 2, 3 이 모두 중복방지된다.
추가세션은 1, 2, 3 처리가 모두 끝나고 return이 일어날 때 까지 기다리기 때문이다.
*/
this.executeLockSelect(Param par);
executeBizLogic1();
executeBizLogic2();
executeBizLogic3();
return null; // commit or rollback
/*
CASE 2 ================================================================
LockSelect 문장으로 시작하는 경우 Biz Logic 2, 3 이 중복방지된다.
추가세션은 BizLogic 1 처리를 실행한 후 대기상태에 진입한다.
원 세션이 2, 3 처리를 끝내고 return이 일어나면 BizLogic 2, 3의 처리를 시작한다.
*/
executeBizLogic1();
this.executeLockSelect(Param par)
executeBizLogic2();
executeBizLogic3();
return null; // commit or rollback
(세션의 종료를 표현하기 위해 return문장을 사용했습니다. 실제 lock이 해제되는 시점은 commit이나 rollback 문장을 만나 데이터의 처리완료가 명시되는 시점입니다.)
2. Lock을 설정하는 레코드의 키 설정
- 중복처리 방지는 Unique한 단위의 비즈니스 로직을 실행할 때 쓰인다. 따라서 Lock을 설정하기 위한 레코드의 Key는 비즈니스 처리 단위를 나타내야 하며 타 실행건들과 비교했을 때 유일해야 한다.
3. Join 처리에 유의할 것
- FOR UPDATE 구문이 들어가는 SELECT 문장에 시스템 공통 table과 같이 시스템의 동작에 영향을 주는 레코드가 join되는 것은 좋지않다.
- 비록 아주 잠깐동안의 Lock일지라도 자칫하면 전체 시스템의 온라인 처리에 대한 대기를 유도할 수 있다.
- Total
- Today
- Yesterday