안녕하세요오 S-HOOK 백엔드 스플릿입니다 🙇
💡 Lock 은 무엇일까??
다들 Lock 이라는 단어는 익숙하시죠?
( 저희가 집에들어갈 때 번호를 누르는 것도 도어-Lock 이니까요 )
이처럼 Lock은 무엇인가 열리지 않도록 막는 장치를 뜻합니다.
DataBase Lock 은 간단하게 데이터베이스에서 사용하는 Lock 이라고 생각하시면 될 것 같습니다.
💡 DB 에서 Lock이 왜 필요할까요??
S-HOOK 서비스의 예시와 함께 파악해봅시다.
- 동시간대에 여러명의 사용자가 사용하는 초~인기 서비스입니다. ( 바란다 초인기.. )
- 100 개의 요청을 동시에 받아서 처리할 수 있는 서버 보유하고 있습니다.
- DB 를 사용해서 데이터를 관리 중입니다.
- 킬링파트의 총
좋아요
수를 볼 수 있습니다.- 좋아하는 킬링파트에
좋아요
를 누를 수 있습니다. ( 처리 방식은 아래와 같습니다 )
- DB 에서 해당 킬링파트 데이터를 조회합니다.
- 킬링파트
좋아요
개수 데이터를 기존 값에 1을 더한 값으로 업데이트 합니다.
- 동시에 100명의 서로 다른 사용자가
- 총 좋아요 수가 1인 A 킬링파트에
좋아요
를 눌렀습니다.- 사용자가 보낸 모든 요청은 에러없이 처리되었습니다.
- ‘A’ 킬링파트의 총
좋아요
수가 1 에서 101 이 되었습니다.
- ‘A’ 킬링파트의 총
좋아요
수가 1에서 고작 2가 되었습니다.
그 이유는 동시에 여러 요청을 처리할 때 문제 발생 가능성이 있었기 때문입니다.
동시에 100개의 좋아요 요청을 처리한 과정은 다음과 같습니다.
- 서버에서 동시에 ‘A’ 킬링파트 데이터를 DB 에게 100번 조회하고 이 때 조회한 ‘A’ 킬링파트의 좋아요 개수는 모두 ‘1’ 입니다.
- 1에서 1을 더한 값인 2로 좋아요 수 업데이트를 100번 하게 됩니다.
- DB 에 저장되어 있는 킬링파트의 총 좋아요 개수는 최종적으로 2가 됩니다.
이런 상황에서는 동시에 여러 요청이 오더라도 하나씩 차례차례 처리가 되어야 합니다.
이처럼 여러 작업의 순차적인 진행을 DB 차원에서 보장하고 싶을 때 DB Lock 을 사용할 수 있습니다.
흔히 SLock 혹은 공유 잠금이라고 불리며 조회할 때 데이터에 거는 Lock 입니다.
MySql 에서는 아래와 같은 쿼리로 조회하는 데이터에 Shared-Lock 을 걸 수 있습니다.
SELECT * FROM killing_part WHERE id=1 LOCK in SHARE MODE; //MySql 5.7 버전
SELECT * FROM killing_part WHERE id=1 FOR SHARE // MySql 8.0 버전
트랜잭션을 시작한 뒤 위에 쿼리를 실행한 후 LOCK 을 조회해보면 LOCK_MODE 에 S 가 있는 것을 확인할 수 있습니다.
흔히 XLock 혹은 배타적 잠금이라고 불리며 데이터 수정,삭제할 때 데이터에 걸리는 Lock 입니다.
( 조회시에도 XLock 을 걸 수 있습니다. )
MySql 에서는 아래와 같은 쿼리가 실행시 데이터에 Exclusive-Lock 이 걸립니다.
SELECT * FROM killing_part WHERE id=1 FOR UPDATE; //조회
UPDATE killing_part SET like_count=2 WHERE id=1; //수정
DELETE FROM killing_part WHERE id=1; //삭제
트랜잭션을 시작한 뒤 위에 쿼리중 하나를 실행한 후 LOCK 을 조회해보면 LOCK_MODE 에 X 가 있는 것을 확인할 수 있습니다.
💡 DB Lock 을 사용해서 위에 문제를 어떻게 해결할 수 있나요?
문제가 생긴 주된 이유는 여러 트랜잭션이 동시에 같은 킬링파트 데이터에 접근해서 사용했기 때문이었습니다.
문제를 해결하려면 같은 킬링파트 데이터에 여러 트랜잭션이 동시에 접근을 못하게 막아야합니다.
💡 SLock, XLock 중에 어떤걸 쓰면 될까요??
위에 설명했듯이 SLock 이 걸린 데이터에는 다른 트랜잭션도 SLock 을 걸 수 있습니다. 이 뜻은 여러 트랜잭션이 데이터에 접근할 수 있다는 말이죠.
즉 XLock 을 사용해야 다른 트랜잭션에서 데이터 접근을 막을 수 있습니다.
이제 서비스에서 데이터를 조회하는 쿼리에 XLock 을 사용하는 구문을 추가해서 문제가 해결됩니다!!
//이전 쿼리
SELECT * FROM killing_part WHERE id=1;
//이후 쿼리
SELECT * FROM killing_part WHERE id=1 FOR UPDATE;
💡 비관적 락이 무엇인가요??
서비스에서 데이터에 충돌이 발생할 것으로 예상될 때 Database-Lock 을 사용하여 트랜잭션이 데이터를 선점하여 충돌을 처리할 때 비관적 락을 사용했다고 말합니다.
글에서 해결하려 했던 상황에서 XLock 이 아닌 SLock 을 사용했을 때는 다음과 같은 상황이 발생합니다.
( 2개의 트랜잭션에서 1번 킬링파트에 SLock을 걸었을 때 SLock이 2번 걸려있는 것을 볼 수 있습니다. )
….
💡 ?????
쉬운 설명을 위해 2개의 동시 요청으로 바꾸어 보겠습니다. ( 트랜잭션 A,B )
이처럼 A는 B를, B 는 A를 기다리는 상황이 발생하여 둘다 아무 작업도 하지못하고 멈춰있는 교착 상태를 DeadLock 이라고 합니다.
💡 그러면 DeadLock 에 걸린 트랜잭션들은 아무일도 못하고 무한정 기다려야 하는 건가요??
DB에서 DeadLock 을 감지하고 처리해주는 것을 DeadLock Detection 이라고 합니다.
InnoDB 에서는 DeadLock 에 빠진 트랜잭션들 중 하나를 RollBack 하여 DeadLock 을 해결합니다.
데이터에 Lock 을 걸기 위해 대기할 수 있는 최대 시간 뜻합니다.
InnoDB 에서는 만약 이 시간안에 Lock 을 걸지 못할시 해당 트랜잭션을 RollBack 하여 DeadLock 을 해결합니다.