티스토리 뷰

반응형

https://golf-dev.tistory.com/53

 

Redis INCR을 이용한 분산환경에서의 동시성 제어하기

문제 상황 회사에서 분산환경에서 하루에 한 번만 요청이 가능한 기능이었지만 한 사람이 3번 이상 요청을 보낸 기록이 있어 원인을 찾아보았습니다. 우선 샘플 코드는 다음과 같습니다. fun save(

golf-dev.tistory.com

위 글을 보면 Redis의 INCR (increment)은 분산환경에서도 연산의 원자성을 지켜주기 때문에 이 성질을 이용하여 분산환경에서 동시요청에 대한 count를 한 후 일정 count 이상의 요청이 들어오면 해당 요청을 discard 시킬 수 있었습니다. 

 

그렇다면 별 다른 로직 없이 분산환경에서 Redis로 동시성을 제어할 수 있었던 이유는 무엇일까요?

 

Single Thread로 동작하는 Redis

보통 동시성 문제란 하나의 공유자원을 두고 여러 스레드가 접근하면서 Race Condition이 발생하고 이 과정에서 데이터 일관성이 깨지는 경우를 말합니다.

하지만 Redis의 경우 single Thread이기 때문에 연산에 원자성이 보장되어 스레드가 작업하는 동안 다른 Task는 기다려야하기 때문에 race condition이 발생하진 않습니다.

 

그렇기 때문에 위 같은 count로직을 분산환경에서 여러 스레드가 동시에 요청하더라도 race condition이 발생하지 않던 것이었습니다.

그렇다면 Redis가 이토록 성능이 빠를 수 있는 이유는 무엇일까요? 

 

Single Thread임에도 성능이 좋은 이유

Redis 클라이언트에 따라 다르지만 기본적으로 Lettuce를 많이 사용합니다. Lettuce는 Netty 기반의 비동기로 동작하는 고성능 클라이언트기 때문에 외부 요청에 대해 응답을 기다리지 않고 바로 다음 요청을 이벤트 루프가 처리합니다. 

또한 multiplexing I/O 모델을 사용하고 있는데 이는 입출력 다중화 모델로 하나의 프로세스로도 여러 클라이언트에게 입출력이 가능해집니다. 또한 이벤트 기반으로 처리하기 때문에 소켓 연결의 도착, 데이터의 도착, 데이터의 전송 가능 상태 등과 같은 상황을 받아 작업을 선택하고 처리합니다. 

설명이 좀 복잡할 수 있습니다. 

https://subscription.packtpub.com/book/data/9781783988167/1/ch01lvl1sec16/understanding-the-redis-event-model

 

Understanding the Redis Event Model | Redis 4.x Cookbook

Redis, known for its high performance, makes the most of a single thread, non-blocking I/O model to process requests rapidly. Therefore, understanding the event

www.prod.packt.com

이 글을 읽어보시면 좋습니다.

 

하지만 Redis라고 동시성 문제가 발생하지 않는 것은 아닙니다. 애플리케이션에서 어떻게 호출하고 사용하느냐에 따라 동시성 문제가 발생할 순 있습니다. 예를 들어서 제가 위 INCR로 문제를 해결할 때 마치 RDBMS 처럼 GET을 해오고 다른 작업들을 처리한 후 SET을 통해 counting을 해주었으면 동시성 이슈가 발생할 수 있습니다. 

시나리오/스레드 T1 T2 data
Read 현재 : 0 현재 : 0 0
Increment 현재 : 1 현재 : 1 1
Read 현재 : 1 현재 : 1 1

이러한 현상을 방지하기 위해 Spring 에서는 incrementAndGet 메소드를 제공해줍니다. Redis를 애플리케이션에서도 Thread-safe할 수 있게 제공해주는 것이죠.

 

하지만 single Thread라고 무조건 좋은가는 아닙니다. transaction이 길어지는 경우 single thread기 때문에 병목이 걸릴 수 있습니다. 그렇기 때문에 Redis에서는 O(N) 명령어를 주의하여야 합니다. 한 두개는 괜찮을 수 있지만 그런 함수가 여러 곳에서 호출 되면 충분히 병목이 걸릴 수 있습니다. 

 

O(N) 명령어 모음 

  • KEYS
  • FLUSHALL
  • FLUSHDB
  • DELETE COLLECTIONS
  • GET ALL COLLECTIONS

이러한 상황에 해결책으로 우아한 Redis에서는 다음과 같은 얘기를 했습니다.

KEYS는 scan을 쓰고, 조회 대상을 나누자

GET ALL을 하는 것도 일부만 가져오던지 컬렉션 자체를 쪼개자. 

후기

Redis 싱글 스레드라고만 생각했지 내부적으로 이렇게 복잡한 내용들이 있을 줄은 몰랐습니다. 특히 Multiplexing I/O 의 event poll 을 나타내는 함수 epoll은 정말 이해하기 어려웠던 것 같습니다. 

또한 Redis를 공부하면서 CS 지식이 정말 중요하다는것을 또 깨닫게 되네요 이상입니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함