[Java] 동시요청에 대한 무결한 처리를 보장하는 방법 7(redis, 이외 트래픽 상황과 환경에 기반한 lock 사용)

Hyo Kyun Lee·2025년 1월 13일
0

Java

목록 보기
74/87

이 글에서 이어진다.

1. 개요

동시성 제어를 할 수 있는 다양한 방안과 개념을 정리하기 위해 작성한다.

(현재 ui에서 한번 버튼을 클릭할 시점에서 몇초간 누름 상태를 유지하여 따닥 발생 이슈는 고려하지 않고, 다수의 사용자

지금의 동시성 제어는 단일 스레드 환경에서 비관적 락이나 분산 락, 채번테이블 재설계를 통해 조치가 되었지만, 향후 더 큰 트래픽 환경에서 어떠한 방법을 고려해야 하는지 생각의 틀을 넓힐 필요가 있다고 생각하였다.

더불어 지금의 동시성 제어도 단순히 11TPS를 넘어 그 이상의 동시요청 상황을 생각하면서 최대한 안정적으로 조치하였으며, 향후 동일한 이슈가 발생하였을때 밑거름이 될 것으로 생각한다.

2. LOCK - 애플리케이션(분산락)

애플리케이션에서 공유 자원에 접근하는 상황에 대해 lock을 설정할 수 있다.

이때 단순히 접근을 못하게 해줄 뿐 만 아니라, 락 해제를 확인하고 락을 요청하고 처리를 진행하는 등의 과정이 원자성이 지켜질 수 있도록 구성하는 것이 중요하다.

공부한 내용으로는 redis가 가장 대표적인데, redis를 활용하여 멀티스레드 접근을 막는 것도 좋지만 개인적으로 DB이력이나 key값 등을 같이 사용해서 안정성을 강화할 수 있지 않을까라는 생각을 하였다(dbms의 lock 기능을 사용하는 것이 아님).

물론 공유자원에 멀티 스레드가 접근하면서 발생할 수 있는 race condition을 방지하는 것도 고려해야 하겠으나, 일단 제어를 할 수 있다는 측면으로(구현) 접근하도록 한다.
또한 분산락 사용할 경우 @Transactional과 양립할 수 없으므로 별도의 exception처리가 필요하다.

  • spin lock(SETNX)
    lettuce, 스레드가 지속적으로 락을 사용할 수 있을지에 대한 확인 요청을 보내며 setIfAbsent 함수를 통해 key 값이 존재하는지(=락을 사용할 수 있을지) 확인한다.

spin lock은 레디스 서버에 지속적인 요청을 보내는데, 그만큼 성능적으로 부하가 생길 수 있기에 비교적 적은 트래픽 환경에서 사용해야 한다.

  • Redission(Rlock)
    락 획득(tryLock), 락 소실(unLock), 락 임대시간leaseTime) 등을 통해 락을 획득할 수 있는 시점에서 락을 획득한다.

락을 소실하면 pub/sub 방식을(내부적으로 redission 로직은 스핀락 개념이 아예 없지는 않다) 통해 해당 락을 얻고, 처리를 진행하는 방식이다.

아예 스핀락 기능이 없지는 않지만, 부하가 개선된 방식으로 이해하여 사용하면 될 것 같다.

  • 이외
    멀티스레드 비동기처리 환경에서 사용할 수 있는 동시성 제어 방법으로, 스레드풀 등을 통해 비동기 연산의 원자성을 확보한다.
    ExecutorService는 쓰레드 풀을 관리하는 서비스로, 병렬 작업을 수행할 때 사용한다.
    CountDownLatch는 다른 쓰레드들이 특정 작업을 완료하길 기다리게 하는 동기화 도구.
    Future는 비동기 연산의 결과, 각 스레드의 연산 결과를 저장한다.

spin lock, redission까지는 해봤는데 다른 방안들은 한번 시도해봐야겠다.

3. LOCK - DBMS

DB에서 동시성 제어를 시도한다. 사실 자원의 제어보다는 접근의 제어이고, 그만큼 애플리케이션 lock에 비해 느리다.

공유 락 / 배타 락

  • 공유 락 : 한 자원을 점유하고 있을 때 읽기는 허용하지만 쓰기는 허용하지 않는다.
  • 배타 락 : 한 자원을 점유하고 있을 때 읽기 포함 모든 접근늘 허용하지 않는다.

비관 락 / 낙관 락
이 두 방식은 jpa에서 많이 사용하는 방식으로, 애플리케이션 단에서 수행하는 락이지만 dbms 락의 개념을 사용하므로 이곳에 분류하여 기술한다.

  • 비관 락 : 트랜잭션이 시작될 때 Shared Lock 또는 Exclusive Lock을 걸고 시작하는 방법으로, 커밋한 후에 다른 트랜잭션이 트랜잭션을 진행하는 것을 허가한다. DB자체가 자원이고 하나의 gate가 된다.

  • 낙관 락 : 트랜잭션이 충돌할 수 있다는 상황, 즉 동시적으로 db에 접근하는 것은 허용한다. 단, 수정사항 및 버전 등의 인자를 통해 자원 변경이 발생할 경우 수정을 허용하지 않고 exception처리한다. jpa의 엔티티 version을 사용하는 것이 일반적이다.

상기와 같이 정리한 내용을 시작으로 무결한 데이터 처리에 대한 기반을 잘 마련해두자.

4. 참고자료

따닥이슈(lettuce) - https://tech.kakaopay.com/post/troubleshooting-logs-as-a-junior-developer/

동시성 제어 방법 - https://wookjongbackend.tistory.com/m/44

DB lock - https://battleracoon.tistory.com/m/2

배타락, 낙관락 - https://sabarada.tistory.com/m/175

pub/sub이 스핀락이 아예 없지는 않다 - https://incheol-jung.gitbook.io/docs/q-and-a/spring/redisson-trylock

분산락 구현 - https://www.google.co.kr/search?q=redis+lock+spring+boot&client=ms-android-samsung-rvo1&sca_esv=bcd23df53ede1dde&sxsrf=ADLYWILVIahBakBpja7HZ1Db5F5R7Mn0Eg%3A1736818603628&ei=q7-FZ92AJp-Nvr0PwbzpuQY&oq=redis+&gs_lp=EhNtb2JpbGUtZ3dzLXdpei1zZXJwIgZyZWRpcyAqAggAMgoQIxiABBgnGIoFMgQQIxgnMgQQIxgnMgUQABiABDILEAAYgAQYsQMYgwEyCxAAGIAEGLEDGIMBMgUQABiABDIFEAAYgARI9hxQmANY2A5wAXgAkAEAmAHEA6AB7RWqAQowLjEwLjEuMS4xuAEByAEA-AEBmAICoALBAcICChAAGLADGNYEGEeYAwCIBgGQBgqSBwMxLjGgB7tL&sclient=mobile-gws-wiz-serp

0개의 댓글

관련 채용 정보