2009년 6월 27일 토요일

Falcon : 트랜잭션제어3

처리 대상의 레코드만 lock걸기

처리대상의 레코드만 lock을 걸어보자.

처리예
1. 트랜잭션1 START TRANSACTION;
2. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE value=1;
3. 트랜잭션2 START TRANSACTION;
4. 트랜잭션2 UPDATE tbl2 SET value=100 WHERE value=2;

value컬럼에는 인덱스가 없기 때문에 full scan을 수행한 다음에 조건을 만족하는 레코드만을 갱신하게 된다.

이때 Falcon에서는 어디도 lock대기로는 되지 않지만 InnoDB에서는 4.에서 lock대기상태가 된다.

이것은 InnoDB에서는 갱신대상 레코드만 아니라 unique인덱스검색의 경우를 빼고 lock대상의 레코드 범위를 넓혀서 다른 트랜잭션의 INSERT문등을 막아내기 위한 특징이다.

다른 예도 알아보자.

처리예2
1. 트랜잭션1 START TRANSACTION;
2. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE id >= 10;
3. 트랜잭션2 START TRANSACTION;
4. 트랜잭션2 INSERT INTO tbl2 VALUES(11,11);
5. 트랜잭션2 COMMIT;
6. 트랜잭션1 UPDATE tbl2 SET value=100 WHERE id >=10;
7. 트랜잭션1 COMMIT;
8. 트랜잭션1 SELECT * FROM tbl2 WHERE id >= 10;

Falcon의 경우 모든 처리가 lock대기상태에 빠지지 않고 끝난다.
또 8.의 결과는 다음과 같이 된다.

실행예
mysql> SELECT * FROM tbl2 WHERE id >= 10;
+---------+-------------+
| id | value |
+---------+-------------+
| 10 | 100 |
| 11 | 11 |
+---------+-------------+

의외라고 생각할지도 모르겠지만 id=11의 value컬럼은 6. UPDATE문에서 갱신대상이 되지 않고 4.에 넣었다 값 11이 그대로 남아 있게 된다.

2.와 6.에 따른 갱신대상 레코드는 같고(id=10의 레코드만) 다른 트랜잭션에서의 INSERT결과에 따른 영향을 받지 않는다. (분리레벨 Repeatable Read의 경우. Read Committed의 경우는 id=11의 value 값도 100이 된다. )

InnoDB의 경우, 2.의 영향으로 4.가 lock대기상태가 된다. 이것에 따라서 2.하고 6.의 갱신결과는 같게된다.

예상외의 레코드가 갑자기 갱신되어버리는 사태를 방지할 수 있게 되지만 동시 실행성이 떨어지는 결점이 있다.

InnoDB에서는 innodb_locks_unsafe_for_binlog파라미터로 처리 대상레코드만을 lock되게 되지만 이번에는 6.에 의해서 id=11의 컬럼값이 100으로 갱신되어버리게 된다.

또, SQL문에 따른 replication시에 문제를 일으키는등의 과제도 있으므로 안이한 설정은 금물이다.

Falcon에서는 관계없는 레코드에 lock을 거는 것 없이 이런 문제를 회피하는 것이 가능하다.