개발자A가 트랜잭션을 시작하고 데이터를 수정하고 있다고 가정한다. A가 열심히 데이터를 수정하고 있고 아직 커밋은 하지 않은 상태다. 여기서 개발자 A의 수정사항을 알지 못하는 개발자 B가 동일한 데이터를 수정한다. 이런 상황이 발생하면 서로 엉뚱한 데이터를 수정하게 되는 문제가 발생할 수 있다.
그렇다면 하나의 여러 사람이 하나의 데이터를 수정해서 발생하는 문제를 어떻게 방지할 수 있을까? 간단하게 생각해보면 한 사람이 데이터를 수정하고 커밋이나 롤백을 하기 전에는 다른 사람이 동일한 데이터를 수정할 수 없게 만들어 두면 가능하다.
세션1에서 db 트랜잭션을 시작하려면 먼저 db 락을 시작해야한다. 그 이후 모든 트랜잭션이 완료되면 세션2에서 다시 db락을 시작하고 트랜잭션을 시작하는 방식으로 이루어진다.
세션1에서 트랜잭션을 시작했고 데이터를 수정중인 상황이다. 여기서 세션2는 db락을 획득하기 위해 대기하고 있는중이다. 여기서 세션2가 db락을 대기하는 시간을 설정할수 있다.
SET LOCK_TIMEOUT 60000;
set autocommit false;
update member set money = 20000 where member_Id = 'memberA';
db락을 기다리는 시간을 60초로 설정했고 autocommit은 fasle로 설정해서 수동커밋 모드로 만든다. LOCK_TIMEOUT은 별도로 설정해주지 않으면 데이터베이스에 설정된 기본값을 사용하게된다.
SET LOCK_TIMEOUT 60000 락 획득 시간을 60초로 설정했기 때문에 60초 안에 락을 얻지 못하면 예외가 발생한다. 세션1이 커밋을 하면서 락을 반납하면 세션2가 락을 획득하면서 세션2의 업데이트 쿼리가 실행된다.
SET LOCK_TIMEOUT[millisecond] 로 락 타임아웃을 설정한다. 예를들어 SET LOCK_TIMEOUT 30000 이면 3초 db락을 설정하게된다. 만약 락을 대기하는 시간이 초과된다면 타임아웃 오류가 발생하기 때문에 설정한 시간 내로 락을 얻는것이 중요하다.
Column "MEMBER_IDDD" not found; SQL statement:
update member set money=10000 + 2000 where member_iddd = 'memberB' [42122-200]
42S22/42122
보통 데이터를 조회할때는 락을 얻지 않고 바로 조회가 가능하다. 세션1에서 데이터를 수정하고 있어도 세션2에서는 데이터 조회가 가능한 것이다. 물론 변경 전의 데이터를 조회하는 것만 가능하다. 그리고 세션1의 트랜잭션이 끝나면 세션2에서 락을 획득해 데이터 조회가 아닌 수정도 가능하다. 다만 데이터를 조회할때도 락을 걸어서 조회해야하는 경우가 있는데 이번에는 그런 경우의 락을 살펴본다.
'select for update' 구문으로 락 획득
set autocommit false;
select * from member where member_id = 'memberA' for update;
'select for update' 구문을 사용하면 데이터를 조회할때 락을 획득하기 때문에 다른 세션에서 해당 데이터를 변경할 수 없다. 그렇다면 조회를 할 때 락을 획득해야 하는 경우는 어떤 경우일까? 예를 들어 데이터베이스의 데이터를 활용해서 계산 로직을 수행해야하는 경우에는 조회일 경우라 하더라도 다른 세션에서 데이터를 중간에 수정해버리면 계산 오류가 발생하기 때문에 이런 경우에는 데이터 조회만 한다고 하더라도 락을 획득하는 것이 필요하다.