2009년 6월 25일 목요일

Falcon : 트랜잭션 제어2

Lost Update의 자동인식

Falcon에서는 여러개의 트랜잭션이 UPDATE문에서 같은 레코드를 타이밍 다르게 갱신했을 때 먼저 온 놈이 이기는 것을 보증하는 것으로 lost update를 방지하는 로직을 가지고 있다.

구체적으로 예를 함 들어보자.

테이블과 레코드 작성예
mysql>CREATE TABLE tbl2 (id INTEGER AUTO_INCREMENT PRIMARY KEY, value INTEGER) ENGINE=Falcon;
mysql>INSERT INTO tbl2 VALUES(1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(10,10);

lost update확인을 위한 처리예
1. 트랜잭션1 START TRANSACTION;
2. 트랜잭션2 START TRANSACTION;
3. 트랜잭션1 SELECT value FROM tbl2 WHERE id=1; //1이 나온다.
4. 트랜잭션2 SELECT value FROM tbl2 WHERE id=1; //1이 나온다.
3. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE id=1;
4. 트랜잭션2 UPDATE tbl2 SET value=10 WHERE id=1;
5. 트랜잭션1 COMMIT;
6. 트랜잭션2 COMMIT;

이 경우 value컬럼값은 InnoDB에서는 트랜잭션1의 갱신값이 트랜잭션2에 의해 덮어씌워져 10이 된다.

그러나 Falcon에서는 어느 분리레벨이어도 6.의 단계에서 다음과 같은 에러가 나와서 최종적으로 value컬럼값은 트랜잭션1에 의한 값인 100이 된다.

ERROR 1020(HY000): Record has changed since last read in table 'tbl2'

InnoDB의 경우 갱신결과는 lost update라는 현상이다. 나중의 녀셕이 이기는 꼴이 되지만 모든 갱신이 성공했다라는 응답이 나오므로 먼저 갱신했던 쪽은 나중에 당황하게 된다.

많은 경우에 많아들여지지 않을지 모르겠지만 대처법도 많이 준비되어있다.

전형적인 것은 SELECT FOR UPDATE로 검색시에 배타lock을 거는 방법이다.

이 예에서는 4.에서의 트랜잭션2에 의한 검색이 5.에서의 트랜잭션1에 의한 커밋이 되기 전까지 기달리게 됨으로 4.에서 취득한 값은 100이 되게 되고 그 값을 확인한 후의 처리가 가능하게 된다.

Falcon의 경우는 갱신시점에서의 컬럼값과 트랜잭션개시 시점의 컬럼값을 비교해서 다른 트랜잭션에 의한 갱신때문에 값이 변화되어 있으면 갱신을 하지 않고 에러처리하게 되는 것이다.

웹 어플리케이션처럼 화면표시를 위한 검색에서 1개의 트랜잭션, 입력값으로 부터 갱신을 위한 1개의 트랜잭션처럼 트랜잭션이 나누어져 있는 경우에는 안타깝지만 효과는 없다.

이 경우는 정석으로 버전번호관리용 정수형 컬럼을 추가하는 것이 좋을 듯 싶다.