Redis 대신 MySQL을 이용해 Lock을 구현하는 방법으로는 대표적으로 다음 두 가지가 있다.
JPA에서 @Lock(LockModeType.PESSIMISTIC_WRITE)를 사용하면 조회 시점에 해당 row에 배타적 락을 걸 수 있다.
이 방식은 다른 트랜잭션이 같은 row를 수정하거나 락을 획득하려고 할 때 대기(wait) 하게 만들어 동시성을 제어한다.
예를 들어 재고 차감, 결제, 입찰 등 동일 데이터에 대한 동시 수정이 발생할 수 있는 상황에서 사용할 수 있다.
MySQL에서는 row-level lock을 통해 특정 row에 대해 Exclusive Lock(X Lock) 을 걸 수 있다.
대표적으로 SELECT ... FOR UPDATE 방식이 이에 해당하며, 트랜잭션이 종료될 때까지 다른 트랜잭션의 수정 접근을 막는다.
즉, MySQL 자체의 락 메커니즘을 활용하여 데이터 정합성을 보장하는 방식이다.
1) 별도 인프라가 필요 없다
Redis를 따로 구축하지 않아도 되므로 기존 MySQL만으로 동시성 제어를 구현할 수 있다.
즉, 시스템 구성이 단순해지고 운영 복잡도가 줄어든다는 장점이 있다.
락 대상 데이터와 락을 관리하는 시스템이 모두 MySQL에 있기 때문에 트랜잭션과 함께 다루기 편하다.
예를 들어 row 수정과 락 획득이 같은 DB 안에서 이뤄지므로 정합성 측면에서 이해하기 쉽다.
JPA에서는 @Lock만 추가해도 비관적 락을 사용할 수 있어 구현이 직관적이다.
락을 DB가 직접 처리하기 때문에 동시 요청이 많아질수록 DB 부하가 커질 수 있다.
특히 락 대기가 길어지면 전체 처리량이 감소할 수 있다.
Redis는 분산 락 용도로 많이 사용되지만, MySQL Lock은 결국 DB 의존적이기 때문에 트래픽이 커질수록 확장성 측면에서 불리할 수 있다.
비관적 락은 충돌을 예방하는 대신 다른 트랜잭션이 대기하게 만든다.
그래서 충돌이 많은 환경에서는 처리 속도가 느려질 수 있다.
Redis는 여러 인스턴스에서 공통된 락 저장소로 사용하기 좋지만, MySQL은 본질적으로 DB 락이기 때문에 분산 락 전용 솔루션에 비해 유연성이 떨어진다.
MySQL 기반 락은 다음과 같은 상황에서 적합하다.
별도 Redis 인프라를 두기 어려운 경우
트래픽이 매우 크지 않은 경우
데이터 정합성이 중요하고, DB 트랜잭션과 함께 관리하고 싶은 경우
동일 row에 대한 동시 수정 제어가 필요한 경우
예:
재고 차감
포인트 사용
주문 상태 변경
MySQL을 이용한 Lock은 별도의 Redis 없이도 JPA 비관적 락이나 SELECT ... FOR UPDATE를 통해 구현할 수 있다.
구조가 단순하고 DB 트랜잭션과 함께 다루기 쉽다는 장점이 있지만, 락 대기로 인한 DB 부하와 확장성 한계가 존재한다.
따라서 트래픽 규모와 서비스 특성에 따라 MySQL Lock과 Redis Lock 중 적절한 방식을 선택하는 것이 중요하다.