[DB] [mysql] 데드락 발생시켜보기 & 트랜잭션과 락

SINGING BIRD·2023년 11월 11일

데드락 개념

서로 다른 트랙잭션이 서로에 대한 락을 소유하게 되어 교착상태에 빠져서 다운되는 것


데드락 발생시키기

1. 클라이언트 A 에서 아래의 명령어를 이용하여 log_login 테이블의 id = 1 인 레코드에 락을 걸어줍니다.

start transaction;
select * from log_login where id = 1 for update;


2. 아래의 명령어를 사용하여 현재 transaction 이 생성되었고 running 상태임을 확인할 수 있습니다.

SELECT * FROM information_schema.INNODB_TRX;


3. 클라이언트 B 에서 아래의 명령어를 이용하여 log_login 테이블의 id = 2 인 레코드에 락을 걸어줍니다.

start transaction;
select * from log_login where id = 2 for update;

4. 아래의 명령어를 사용하여 총 2개의 transaction 이 생성되었고 running 상태임을 확인할 수 있습니다.

SELECT * FROM information_schema.INNODB_TRX;


5. 클라이언트 A 에서 아래의 명령어를 사용하여, 클라이언트 B 에서 락을 걸어준 리소스에 접근해봅니다.

select * from log_login where id = 2 for update;

클라이언트 B 에서 락을 걸어준 리소스에 접근하였으므로, 대기하게 됩니다.

클라이언트 A 에서 발생시킨 트랜잭션의 상태가 LOCK WAIT 로 바뀐 것을 확인할 수 있습니다.


6. 클라이언트 B 에서 아래의 명령어를 사용하여, 클라이언트 A 에서 락을 걸어준 리소스에 접근해봅니다.

select * from log_login where id = 1 for update;

클라이언트 A 의 트랜잭션은 클라이언트 B 의 트랜잭션이 끝나길 기다리게 되고,

클라이언트 B 의 트랜잭션은 클라이언트 A 의 트랜잭션이 끝나길 기다리는 상태가 됩니다.

이 상태가 deadlock 이며, 다음과 같은 에러와 함께 트랜잭션이 종료됩니다.

(mysql 에서 기본적으로 deadlock 을 감지한 뒤 종료시키도록 세팅되어 있습니다.)

트랜잭션을 확인해보면 클라이언트 A 의 트랜잭션만 있는 것을 확인할 수 있습니다.


실제 서비스 트랜잭션 상태 스크린샷

위는 프로젝트 꾸럼이에서 유저가 사용중인 상태에서 트랜잭션을 조회해본 스크린샷입니다.

project 테이블의 하나의 record 를 업데이트 하는 구문이 동시에 여러개가 들어올 때,

가장 아래쪽의 쿼리 (트랜잭션) 은 진행중 상태이므로, 이 쿼리 (트랜잭션) 이 작업중인 리소스에 락을 걸었습니다.

그 위에 있는 4개의 쿼리 (트랜잭션) 은 먼저 진행중인 쿼리 (트랜잭션) 이 끝나고 락이 풀리기를 대기하고 있는 모습입니다.


lock 에 대한 추가 설명

  • 기본적인 select 구문에서는 락이 걸리지 않습니다.

  • select 에 for update 구문을 추가하게 되면 조건에 해당하는 레코드가 lock 이 걸리게 됩니다.

예시 )

SELECT * FROM log_login WHERE id = 1 FOR UPDATE;
  • update, delete 구문에서는 조건에 해당하는 레코드에만 lock 이 걸립니다.

예시 )

UPDATE employees SET salary = 50000 WHERE department = 'Sales';

profile
good things take time

0개의 댓글