Search
🔒

데이터베이스 락(Lock)에 대하여

AI custom autofill
This blog explains database locks and their importance for data consistency.
Published
2024/11/23
Category
Programming
Tags
Database
동시성 제어에 대해서 관심이 많아지게 된 계기가 있었는데..
특정 서비스에 구독한 유저에게 횟수에 따라 권한이 주어졌는데, 횟수를 저장하는 데이터에 동시성에 제어되지 않은 상태에서 수 많은 업데이트가 일어나게 되었고, 데이터 일관성이 없게 되며 회사 이윤과도 연관된 이슈가 발생하게 되었다.
미리 예측이 가능했지만, 기술부족으로 대처할 수 없었기에 대응하며 공부했던 내용을 정리해보고자 한다.
데이터베이스는 말 그대로 데이터를 저장하는 공간이다.
그렇기에, 하나의 저장 공간에 여러 시스템에서 접근하게 되기 때문에 데이터의 일관성과 무결성을 유지하는 건 매우 중요하다.
예를 들어, 재고가 1개밖에 남지 않은 상품이 있는데 두명의 유저가 거의 동시에 구매를 하게 된 경우, 구매 성공은 1명만 되어야 한다.
이런 상황에서 DBMS(DataBase Management System)에서 사용하는 공통적인 방법이 Lock이다.

락(Lock)이란?

동시성과 데이터 일관성을 보장하기 위해 사용되는 메커니즘이다.
여러 커넥션에서 동시에 동일한 자원을 요청할 경우 순서대로 하나의 커넥션만 변경할 수 있게 해주는 기능
동시성을 제어하기 위한 기능
DMBS마다 Lock을 구현하는 방식은 각각 다르기때문에, Document를 참고하는 것이 중요!

락의 종류

공유락(Shared Lock; Read Lock)

공유락은 데이터를 읽을 때 사용되는 락이기 때문에, Read Lock이라고 불리며 Shared의 앞 글자를 따서 주로 S로 표기한다.
여러 사용자가 동시에 데이터를 읽어도 데이터의 일관성에는 아무런 영향을 주지 않기 때문에, 공유락끼리는 동시에 접근이 가능하다. 하지만 베타락은 걸 수 없다.
하나의 세션에서 읽기 작업을 수행할 때, 다른 세션에서 해당 데이터를 읽어도 데이터의 정합성은 지켜지기 때문에 다른 세션의 공유락을 막을 필요가 없다.
하나의 세션에서 읽기 작업을 수행할 때, 다른 세션에서 해당 데이터에 쓰기 작업을 수행한다면, 기존 세션의 결과가 달라질 수 있기 때문에 정합성을 지킬 수 없으므로 다른 세션의 베타락 획득은 막는다.

베타락(Exclusive Lock; Write Lock)

베타락은 데이터에 변경을 가하는 쓰기 명령들에 대해 주어지는 락으로 Write Lock으로 불리며, Exclusive의 X를 따서 X로 표기한다.
베타락은 다른 세션이 해당 자원에 접근하는 것을 막는다. (SELECT, INSERT, UPDATE…)
베타락은 트랜잭션동안 유지되며, 공유락 및 베타락을 걸고 접근할 수 없다.
하나의 세션에서 쓰기 작업을 수행할 때, 다른 세션에서 해당 데이터를 읽는다면 작업 결과가 중간에 달라질 수 있기 때문에 데이터 정합성이 지켜지지 않으므로 다른 세션의 공유락 획득을 막는다.
하나의 세션에서 쓰기 작업을 수행할 때, 다른 세션에서 해당 데이터 쓰기 작업을 한다면 기존 쓰기 작업 결과가 달라질 수 있기 때문에 데이터 정합성이 지켜지지 않으므로 다른 세션의 베타락 획득을 막는다.

락의 설정 범위

락의 범위가 클수록(데이터베이스>파일>테이블>페이지>블록>컬럼>행) 동시성은 낮아지고 관리 부하는 감소한다.
실제 상황에서는 DBMS가 자동으로 적절한 락 레벨을 선택하지만, 필요한 경우에만 명시적으로 락을 사용하는 게 좋다고 한다.
데이터베이스
파일
테이블
페이지와 블록
컬럼

락의 부작용

특정 데이터 범위에 대한 권한을 부여받아 리소스 관리를 한다면 더할나위 없이 좋아보이지만, 발생할 수 있는 대표적인 2가지 문제점이 있다.

블로킹(Blocking)

블로킹은 Lock간(베타-베타, 베타-공유)의 경합이 발생하여 특정 트랜잭션이 작업을 진행하지 못하고 멈춰있는 상태를 말한다.
블로킹을 해소하기 위해서는 베타락을 가지고 있는 트랜잭션이 완료(커밋 혹은 롤백)되어야 한다.
블로킹을 해소하기 위해서는
쿼리튜닝
트랜잭션 작업 단위를 최대한 적게 구성
동일한 데이터를 동시에 변경하는 작업을 하지 않도록 설계 (비관적 락, 낙관적 락을 사용하여 해결)
트랜잭션이 활발할 때, 대용량 데이터 작업 수행 지양
LOCK_TIMEOUT 설정 (MySQL 8.0 innodb_lock_wait_timeout의 디폴트값은 50초)

데드락(DeadLock)

데드락은 두 트랜잭션 모두가 블로킹 상태에 진입하여 서로의 블로킹을 해결할 수 없는 상태를 말한다.
DBMC가 둘 중 한 트랜잭션에 에러를 발생시킴으로써 문제를 해결한다.
트랜잭션1은 Balance 테이블에 베타락을 얻었고, 트랜잭션2는 Member 테이블에 베타락을 얻었다. 트랜잭션1은 Member 테이블에 공유락을 얻길 원하기 때문에 베타락이 걸려있는 Member의 트랜잭션2가 종료되길 기다린다. 동시에 트랜잭션2는 Balance 테이블에 공유락을 얻길 원하기 때문에 베타락이 걸려있는 Balance의 트랜잭션1이 종료되길 기다린다. 서로 기다리는 데드락이 발생.
데드락을 해소하기 위해서는
MySQL은 기본적으로 데드락을 감지하는 옵션이 on으로 활성화 되어 있다.
set global innodb_deadlock_detect=on;
데드락이 발생한 경우 데드락 감지가 된 트랜잭션이 자동으로 롤백되어 종료되고, 다른 트랜잭션은 정상적으로 작업이 수행된다.
만약 off로 변경한다면, MySQL에 설정된 lock_timeout 시간이 지날 시 에러가 발생하고, 트랜잭션이 롤백되고 종료되어 데드락이 해결된다.
Ref:
트랜잭션 충돌에 예방하여 처리할 수 있는 비관적락과 낙관적락이 존재하는데..