Database

[MySQL] SELECT FOR UPDATE

비번변경 2023. 10. 24. 22:41

개요

하나의 트랜잭션 내에서 데이터를 쿼리 한 후 관련 데이터를 삽입하거나 갱신하는 경우, 일반적인 SELECT 문은 충분한 보호를 제공하지 않는다. 쿼리 한 데이터와 동일한 데이터를 다른 트랜잭션에서 갱신하거나 삭제할 수 있기 때문에 추가적인 안정성이 필요하다.

 

SELECT FOR UPDATE는 이러한 동시성 문제를 해결하기 위해 InnoDB가 제공하는 locking read 방법 중 하나다.

 

 

Locking Read

InnoDB 테이블에 대해나 잠금 작업을 수행하는 SELECT 문이다. SELECT ~ FOR UPDATE 또는 SELECT ~ LOCK IN SHARE 문 두 가지가 해당된다. locking read는 교착 상태를 발생시킬 수 있으므로 사용에 주의해야 한다.

 

 

SELECT FOR UPDATE

SELECT FOR UPDATE는 쿼리 된 데이터에 write-lock을 걸어 트랜잭션이 끝날 때까지 해당 레코드를 수정할 수 없게 한다. 데이터를 업데이트하기 위해 쿼리 했으므로 다른 트랜잭션에서 수정할 수 없도록 한 것이다.

 

lock이 풀릴 때까지 대기하지 않을 때는 NOWAIT 또는 SKIP LOCKED 옵션을 사용할 수 있다.

  • NOWAIT : 쿼리가 즉시 실행되며 요청된 데이터가 잠겨 있으면 오류가 발생하면서 실패한다.
  • SKIP LOCKED : 쿼리가 즉시 실행되며 잠긴 행은 제외되고 실행된다.

 

 

동작 방식

다음과 같은 테이블을 예시로 동작 방식을 확인해 본다.

CREATE TABLE test (
  id INT
);
INSERT INTO test (id) VALUES (1);
INSERT INTO test (id) VALUES (2);

 

session 1에서 id가 1인 데이터에 FOR UPDATE를 수행하고, session 2에서 id가 1인 데이터에 SELECT / SELECT FOR UPDATE 모두 수행해 본다.

 

session 1

session2

session 2에서 일반적인 SELECT 문은 잘 수행되지만, SELECT FOR UPDATE 문은 session 1에서 이미 잠금 처리되어 있어 수행되지 않는 것을 확인할 수 있다. session 2에서 수행한 SELECT FOR UPDATE는 session 1에서 걸린 lock이 해제될 때까지, 즉 commit/rollback에 의해 트랜잭션이 끝날 때까지 무한정 대기한다. UPDATE 문을 수행했을 때도 무한정 대기하며, write-lock이 걸린 데이터에 대한 갱신은 트랜잭션이 끝난 시점부터 수행할 수 있게 된다.

 

+

SELECT FOR UPDATE는 행 단위로 lock이 걸리므로, lock이 걸리지 않은 다른 데이터에 대해서는 SELECT FOR UPDATE를 수행할 수 있다고 한다. 하지만 이 글을 작성하면서 테스트했을 때는 락이 걸리지 않은 데이터에 대한 SELECT도 수행하지 않았다……🤔.

 

 

참고 문서

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html

https://dev.mysql.com/doc/refman/8.0/en/glossary.html#glos_locking_read

https://jinhokwon.github.io/mysql/mysql-select-for-update/

 

 

728x90