프로젝트/redis

Redis를 통해 분산락 구현하기 - 이론편

Junuuu 2023. 10. 25. 00:01
728x90

분산락이란 무엇일까?

여러 개의 분산된 서버가 존재할 때 동일한 데이터에 대한 동기화를 보장하기 위해 사용하는 락의 종류 중 하나입니다. 

 

분산락은 어떤 상황에서 필요할까?

Java Spring 기반의 웹 애플리케이션은 기본적으로 멀티 스레드 환경에서 기동 됩니다.

이에 따라 여러 스레드가 함께 접근할 수 있는 공유자원에 대해 race condition이 발생하지 않도록 별도의 처리가 필요합니다.

 

Java의 synchonized라는 키워드를 통해 해결할 수 있지만, 서버를 여러 대 사용하여 분산되어 있는 상황에서는 사용하기 어렵습니다.

 

이런 상황일 때 분산락을 활용하여 해결할 수 있습니다.

 

MySQL의 네임드락 대신에 SELECT FOR UPDATE을 활용할 수도 있습니다.

하지만 그런경우 timeout 구성이 까다로워 무한 대기에 빠질 가능성이 있으며 단일 업데이트가 아니라 여러 개의 테이블을 조회해야 하는 경우라면 성능 저하와 데드락 가능성이 높아지게 됩니다.

 

분산락 구현 방법

MySQL의 네임드 락, Redis, Zookeeper 등을 활용할 수 있습니다.

 

일반적으로 웹 애플리케이션의 공유 자원으로 데이터베이스를 가장 많이 활용합니다.

그렇기 때문에 DB의 락 기능을 활용하여 MySQL의 네임드 락을 활용해 볼 수 있습니다.

별도의 커넥션풀을 관리해야하지만 Redis, Zookeeper 등에 대한 추가 인프라 구축 및 유지보수의 부담이 사라지게 됩니다.

 

반면 Redis를 활용하게 되면 인메모리 기반의 저장소이기 때문에 더 높은 처리량과 락에 관련된 부하를 분리하여 받을 수 있는 장점이 있습니다.

 

Redis Lettuce와 Redisson

lettuce와 redisson은 Java에서 사용되는 Redis 클라이언트입니다.

 

lettuce의 경우 SETNX를 활용하여 스핀락을 구현할 수 있습니다.

즉, Redis 서버에 지속적으로 SETNX 명령을 보내 락을 획득할 수 있는지 확인하기 때문에 부하를 유발합니다.

 

반면 redisson을 활용하게 되면 pub/sub을 기반으로 락을 획득할 수 있는지 알려주기 때문에 lettuce 방식에 비해 부하가 상대적으로 적습니다. 내부적으로는 lua script를 활용하여 ttl을 적용합니다.

 

 

분산락을 사용할 때 주의해야 할 점 

  • 적절한 타임아웃 설정
  • 락의 해지 시점은 트랜잭션 커밋 이후로 진행한다. (db의 격리 수준이 read-uncommitted가 아니라고 가정 시)
  • 타임아웃의 시간차로 발생하는 만약의 상황에 Race Condition이 발생할 수 있어 Optimistic Locking으로 관리해 줍니다.

  • 주요 테이블은 envers로 변경점을 파악하여 만약의 사항을 대비

 

 

 

참고자료

https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html

https://techblog.woowahan.com/2631/

https://helloworld.kurly.com/blog/distributed-redisson-lock/