프로젝트를 진행하면서 동시성 문제를 해결하고자 낙관적 락을 설정하고 여러 사용자가 동시에 요청하는 상황을 만들어보았는데
데드락이 발생하여 왜 그런가에 대해 알아보았다
[이커머스 프로젝트] DB 동시성 문제 해결하기 - 2 (낙관적 락 적용)
이번엔 실제 프로젝트에 락을 적용해보려고한다비관적 락을 적용해야할지 낙관적 락을 적용해야할지 판단이 잘 서질않는다과연 이 프로젝트가 실제로 서비스된다면 충돌이 잦을지 적을지 잘
mrxx.tistory.com
데드락이란?
데드락이란 여러개의 트랜잭션이 교착 놓여 서로 락을 취득하고자 무한정 대기하는 상황이다
다음 4가지 조건이 모두 성립할때 발생한다
1. 상호 배제
- 자원은 한번에 한 프로세스만 사용 할 수 있어야한다
2. 점유 대기
- 최소 하나의 자원을 사용중이면서 다른 프로세스가 사용중인 자원을 추가적으로 사용하기 위해 대기하는 프로세스가 있어야한다
3. 비선점
- 다른 프로세스가 사용중인 자원은 사용이 끝날때까지 강제로 빼앗을 수 없어야 한다
4. 순환 대기
- 대기하는 프로세스가 순환하는 형태로 대기하고 있어야한다
- 예) A -> B -> C -> D -> A
다른 말로하면 4가지 조건중 하나라도 성립하지않으면 데드락이 발생하지않는다
MySQL 로그 내역
SHOW ENGINE INNODB STATUS;
명령어로 최근 데드락 발생 내역을 볼 수 있다
------------------------
LATEST DETECTED DEADLOCK
------------------------
2025-01-20 05:57:05 281472523104000
*** (1) TRANSACTION:
TRANSACTION 458103, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 7 lock struct(s), heap size 1128, 3 row lock(s), undo log entries 1
MySQL thread id 1792, OS thread handle 281473194127104, query id 39982 172.17.0.1 root updating
update product set category_id=9,description='description',is_active=1,modified_at='2025-01-20 14:57:05.659044',name='product 1',review_count=1,star_avg=3.0,store_id=1 where product_id=1
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 12391 page no 7 n bits 232 index PRIMARY of table coupang.product trx id 458103 lock mode S locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 12; compact format; info bits 0
0: len 8; hex 8000000000000001; asc ;;
1: len 6; hex 00000006ab61; asc a;;
2: len 7; hex 81000000a80110; asc ;;
3: len 1; hex 01; asc ;;
4: len 4; hex 80000000; asc ;;
5: len 8; hex 0000000000000000; asc ;;
6: len 8; hex 8000000000000009; asc ;;
7: len 8; hex 99b58e439d000000; asc C ;;
8: len 8; hex 99b58e439d000000; asc C ;;
9: len 8; hex 8000000000000001; asc ;;
10: len 11; hex 6465736372697074696f6e; asc description;;
11: len 9; hex 70726f647563742031; asc product 1;;
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 12391 page no 7 n bits 232 index PRIMARY of table coupang.product trx id 458103 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 12; compact format; info bits 0
0: len 8; hex 8000000000000001; asc ;;
1: len 6; hex 00000006ab61; asc a;;
2: len 7; hex 81000000a80110; asc ;;
3: len 1; hex 01; asc ;;
4: len 4; hex 80000000; asc ;;
5: len 8; hex 0000000000000000; asc ;;
6: len 8; hex 8000000000000009; asc ;;
7: len 8; hex 99b58e439d000000; asc C ;;
8: len 8; hex 99b58e439d000000; asc C ;;
9: len 8; hex 8000000000000001; asc ;;
10: len 11; hex 6465736372697074696f6e; asc description;;
11: len 9; hex 70726f647563742031; asc product 1;;
*** (2) TRANSACTION:
TRANSACTION 458107, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 7 lock struct(s), heap size 1128, 3 row lock(s), undo log entries 1
MySQL thread id 1788, OS thread handle 281473202515712, query id 39979 172.17.0.1 root updating
update product set category_id=9,description='description',is_active=1,modified_at='2025-01-20 14:57:05.659086',name='product 1',review_count=1,star_avg=3.0,store_id=1 where product_id=1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 12391 page no 7 n bits 232 index PRIMARY of table coupang.product trx id 458107 lock mode S locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 12; compact format; info bits 0
0: len 8; hex 8000000000000001; asc ;;
1: len 6; hex 00000006ab61; asc a;;
2: len 7; hex 81000000a80110; asc ;;
3: len 1; hex 01; asc ;;
4: len 4; hex 80000000; asc ;;
5: len 8; hex 0000000000000000; asc ;;
6: len 8; hex 8000000000000009; asc ;;
7: len 8; hex 99b58e439d000000; asc C ;;
8: len 8; hex 99b58e439d000000; asc C ;;
9: len 8; hex 8000000000000001; asc ;;
10: len 11; hex 6465736372697074696f6e; asc description;;
11: len 9; hex 70726f647563742031; asc product 1;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 12391 page no 7 n bits 232 index PRIMARY of table coupang.product trx id 458107 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 12; compact format; info bits 0
0: len 8; hex 8000000000000001; asc ;;
1: len 6; hex 00000006ab61; asc a;;
2: len 7; hex 81000000a80110; asc ;;
3: len 1; hex 01; asc ;;
4: len 4; hex 80000000; asc ;;
5: len 8; hex 0000000000000000; asc ;;
6: len 8; hex 8000000000000009; asc ;;
7: len 8; hex 99b58e439d000000; asc C ;;
8: len 8; hex 99b58e439d000000; asc C ;;
9: len 8; hex 8000000000000001; asc ;;
10: len 11; hex 6465736372697074696f6e; asc description;;
11: len 9; hex 70726f647563742031; asc product 1;;
*** WE ROLL BACK TRANSACTION (2)
이렇게 현재 진행중인 프로젝트에서 발생한 데드락 내역을 볼 수 있는데
1번 트랜잭션과 2번 트랜잭션에서 데드락이 발생하여 2번 트랜잭션을 롤백하여 해소한것을 볼수있다
로그 분석
1번 트랜잭션(458103)
update product
set
category_id=9,
description='description',
is_active=1,
modified_at='2025-01-20 14:57:05.659044',
name='product 1',
review_count=1,
star_avg=3.0,
store_id=1
where product_id=1
product_id 1인 행을 업데이트하려고 시도중이고
2번 트랜잭션(458107)
update product
set
category_id=9,
description='description',
is_active=1,
modified_at='2025-01-20 14:57:05.659086',
name='product 1',
review_count=1,
star_avg=3.0,
store_id=1
where product_id=1
2번 트랜잭션도 product_id 1인 행을 업데이트하려고 시도중이다
1번 트랜잭션(458103)과 2번 트랜잭션(458107)은 모두
product_id 1에 대한 공유 잠금(S-LOCK)을 소유중이고
추가로 둘다 베타 잠금(X-LOCK)을 요청중인데 공유 잠금이 걸려있어
순환 대기가 발생해 데드락 상황이 된것이다
그렇다면 공유 잠금과 베타 잠금이 무엇인가를 알아야 이 상황이 이해가 될것같다
공유 잠금(Shared Lock)과 베타 잠금(Exclusive Lock)
공유 잠금(Shared Lock)이란
데이터를 읽을 때 사용 하는 잠금이다
여러 트랜잭션이 동시에 읽을 수 있지만 수정은 불가능하다
공유 잠금이 걸려있는 상태에서 다른 트랜잭션이 수정하려고 하면 대기상태가 된다
베타 잠금(Exclusive Lock)이란
데이터 읽기, 쓰기를 모두 독점하는 잠금이다
베타 잠금이 걸려있는 데이터는 어떤 트랜잭션이던 접근이 불가능하다
베타 잠금은 하나의 트랜잭션만 걸 수 있다
MySQL :: MySQL 8.4 Reference Manual :: 17.7.1 InnoDB Locking
This section describes lock types used by InnoDB. Shared and Exclusive Locks InnoDB implements standard row-level locking where there are two types of locks, shared (S) locks and exclusive (X) locks. If transaction T1 holds a shared (S) lock on row r, then
dev.mysql.com
결론
따라서 위에 상황에서 두개의 트랜잭션이 각각 공유 잠금을 가지고있고
수정을 위해서 베타 잠금을 요청하는데 다른 트랙잭션에서 공유 잠금을 소유하고 있으니 순환 대기가 발생한것이다
그래서 MySQL에선 2번 트랜잭션(458107)을 롤백시켜 데드락 상황을 해소한것이고
1번 트랜잭션(458103)이 정상적으로 수정된것이다
데드락이 왜 발생한지에 대하여 알아보았다
해결방법을 찾아보던중 데드락이 완벽하게 발생하지않게 하는 방법은 불가능하다
DB의 무결성을 보장하기 위해선 락(Lock)이 필요한데 이런 메커니즘으로 인해 발생하는 것으로 현실적으로 불가하다는 것을 알게되었다
그래서 데드락이 발생하는 상황을 최소화 시켜야하는데 방법은 여러가지가있다
하지만 방법마다 장단점이 존재하고 성능이나 DB영향을 끼칠 수 있기때문에
상황에따라 적절한 방법을 사용해야 적절할 것 같기 때문에
조금 더 공부를 해보아야겠다