프로그래밍/General

Redis의 개념과 활용법

Lou Park 2021. 11. 16. 21:59

Redis란?

 

Redis는 인 메모리 키 값 데이터 구조 스토어다. 일반 Database는 데이터를 디스크에 저장하지만, 레디스는 데이터를 서버의 주 메모리에 저장한다. 그렇기 때문에 전통적 Database보다 데이터 접근 시간이 약 1ms 정도로 월등히 빠르며, 다양한 형식의 데이터를 저장할 수 있다. 대표적으로는 List, Set, Hash, Bitmap 등이 있다. 메모리에 저장하다보니 데이터가 휘발하지 않을까?라는 생각이 들 수도 있지만, 예기치 못한 셧다운 등 필요한 경우 Snapshot을 떠서 저장할 수 있다. 


Redis의 활용법

캐시(Caching)

캐시는 데이터의 접근시간을 줄이기 위해 가져온 데이터를 복사해두는 임시 공간이다. Redis는 앞서 설명했듯 메모리에 데이터가 저장되어있어 캐시로 활용할 수 있다. 레디스는 키 값 데이터 구조 스토어라고 말했는데, Key-Value 쌍으로 데이터가 저장된다.

 

다음은 Redis에 fruit 이라는 키에 apple이라는 값을 저장하고 불러오는 예제다.

SET fruit apple
OK

GET fruit
apple

 

 

 

세션 스토어

사용자 프로필, 자격증명, 상태같은 세션정보를 웹 애플리케이션에서 들고있다고 하면, 애플리케이션 업데이트 등으로 재시작시에 모든 사용자 세션정보가 날아가게 된다. 레디스에서 세션을 저장하게 되면 Redis 서버를 재시작하지 않는 이상 세션정보가 유지되어 유저들이 로그아웃되는 현상을 막을 수 있고, 세션 클러스터링을 통해 로드밸런싱을 할 경우 각 세션정보가 다른 서버에 붙어서 로그인 정보가 일관되지 않게되는 문제를 방지할 수 있다. 또한 레디스에서는 데이터에 TTL을 적용할 수 있기때문에 유효기간이 있는 세션 관리에 적합하다.

 

 

실시간 랭킹 시스템

Redis Sorted Set 자료구조를 이용하면 실시간 랭킹 시스템을 구현하기 용이하다. 쿠키런 킹덤같은 게임에서 수십만명의 유저들이 아레나에 참가하여 각자 점수를 올리고, 내리고 하면서 랭킹이 바뀌게 되는데, 바로 이럴때다! 특히나 1위부터 ~N위까지 순위를 구하는 것은 관계형 데이터 베이스에서 LIMIT N같은 식으로 가져오면 비교적 간단한 편이지만, 어떤 유저 X가 랭킹이 몇 위인가?를 빠르게 알아내기는 어렵다.

 

Sorted Set은 Set의 정렬된 버전이다. Redis의 ZADD 명령어를 이용하면 Sorted Set 데이터를 추가할 수 있다. 

다음은 arena라는 이름의 Sorted Set에 점수와 Sorted Set 키 값을 추가한 예시이다. 쿠키런 킹덤 아레나 점수와 닉네임 같은 느낌으로 적어보았다.

ZADD arena 283 "지숭이"
ZADD arena 958 "알록달록"
ZADD arena 592 "포스틱"
ZADD arena 190 "내일은쿠런왕"

 

ZRANGE를 이용하면 오름차순 기준의 랭킹, ZREVRANGE의 경우 내림차순 랭킹을 가져올 수 있다. 

아래는 아레나 1위부터 3위까지의 랭킹을 보여준다.

ZREVRANGE arena 1 3
// 알록달록
// 포스틱
// 지숭이

 

ZRANK를 이용하면 특정 유저가 몇 위 인지도 단번에 알 수 있다.

ZRANK arena "내일은쿠런왕"
// 4

 

공유 자원 관리

선착순 1명 PS5 증정 이벤트에서 당첨자가 2명이상이 나온다면?  여러 서버를 운영하고 있을때, 선착순 이벤트 같은 것이 일어나면 데이터의 동기화를 보장하기 어렵다. 이런 경우 데이터의 원자성을 유지하는 방법으로, 레디스 분산 락(Distributed Lock)을 활용하여 공유 자원을 관리 할 수 있다. 

 

Redis는 RedLock 알고리즘을 사용하며, 다음 3가지 특성을 보장해야한다고 한다.

1. 상호배제성 - 한 순간에 하나의 작업자만이 락을 걸 수 있다.

2. 데드락 방지 - 락을 획득한 작업자가 장애로 인해 락을 반납하지 못했을때 다른 작업자가 영원히 락을 획득하지 못하게 되는 사태가 없어야 한다.

3. 여러 레디스 노드가 존재할때 일부 노드에 장애가 발생하더라도 작업자들은 락을 걸고 해체할 수 있어야한다.

 

나는 식당 화장실을 생각해봤다. (^-^) 모두다 들어 맞는 이야기다.

 

Redis에서 락을 획득시 while 문에서 락을 획득할때까지 try catch를 하는식으로 구현할 수도 있는데...이를 스핀락이라고 한다. 스핀락은

이렇게 임계 구역에 진입이 가능할 때까지 루프를 돌면서 락을 획득하는 시도를 계속하는 방식의 락으로, 나올때까지 화장실 문 앞에서서 문을 계속 두드리는건데...짧은 시간안에 끝날 작업의 경우 이렇게 구현해도 상관없지만, 스핀락에서 오랜 시간을 잡아먹을 경우 다른 작업을 실행하지 못하고 최종적으로는 프로그램의 성능을 저하시키게되는 문제를 일으킬 수 있다.

 

따라서 Lock을 걸때는 기다리는 시간을 설정(Timeout)할 수 있도록 하며 Pub/Sub을 통해서 락이 해제 되었다는 메세지가 수신되면 락획득을 시도하는것으로 구현하는 것이 바람직한 방법이다.

 

채팅방 구현

http://intro2libsys.info/focused-redis-topics/day-two/pubsub

단일 서버를 사용하는 채팅방이라면 상관없겠지만, 여러 서버를 사용하는 채팅방의 경우 메세지 내용을 공유할 수 없다. 이럴 경우 Redis를 공통 Pub/Sub 채널로 사용함으로서 해당 역할을 대신할 수 있다. Pub/Sub은 특정한 주제에 대해 구독(Subscribe)하게 되면, 해당 주제에 이벤트가 생길때마다 구독자들에게 메세지를 발행(Publish)하는 형태의 통신 방법이다. 

 


 

참고자료

https://ko.wikipedia.org/wiki/%EC%BA%90%EC%8B%9C

https://comart.io/blog/realtime-ranking-with-redis-sorted-set

https://blog.juraffe.dev/87

http://3.37.238.75/danil-inseuteonseuro-guseongdoen-rediseueseoyi-bunsan-rag-guhyeon/

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

https://aws.amazon.com/ko/elasticache/what-is-redis/

https://daddyprogrammer.org/post/4731/spring-websocket-chatting-server-redis-pub-sub/