Redis

Hyebin Lee·2022년 10월 30일

All about Redis

  • In-Memory Data Structure Store (Remote)
  • Strings, set, sorted-set, hashes, list
  • Hyperloglog, bitmap, geospatial index
  • stream

Cache

나중에 요청한 결과를 미리 저장해두었다가 빠르게 서비스를 해주는 것을 의미

  • look aside cache: 먼저 cache에서 조회해보고 없으면 db에서 읽어오는 구조, db에서 읽어온 데이터는 cache에 업데이트 한다
  • write-back: 쓰기 연산이 많은 경우 cache에 일단 밀어넣었다가 일정 주기마다 해당 내용을 db에 batch insert 하는 방법 (로그 db 저장시 많이 사용)

Collection 제공

개발의 편의성을 제공하고 개발의 난이도를 낮춰준다
Redis의 자료구조는 Atomic하기 때문에 Race condition을 피할 수 있다
비즈니스 로직에 집중할 수 있다

  • String - 단순 key value 형태로 저장
  • List - key List 형태로 저장, 앞뒤로 push, pop 할 수 있음
  • Set
  • Sorted Set - score 값으로 정렬, index 범위 값의 데이터를 조회 가능
  • Hash - key value 안에 key value 형태

Collection 주의사항

하나의 컬렉션에 너무 많은 아이템을 달면 좋지 않음
expire는 collection item 개별로 걸리지 않고 전체 collection에 대해서만 걸림

서버 코드 상에서 직접 쓰지 않는 이유?

  1. 서버가 여러 대인 경우 Consistency의 문제 발생
  2. Multi-Threaded 환경에서 Race Condition 발생

Redis의 Race Condition Solution

Single Thread기반
Redis 자료구조는 Atomic Critical Section에 대한 동기화 제공

Reids 활용

  • 여러 서버에서 같은 데이터 공유시
  • Atomic 한 자료구조 사용 필요시
  • 캐싱 필요시
  • 인증 토큰 등을 저장(String 또는 hash), Ranking 보드로 사용(Sorted set), 유저 API Limit

주의사항

SingleThread 이므로 시간복잡도 고려 필요
전체 데이터를 다루는 O(N)의 시간복잡도 요청은 지양할 것
-> collection 의 모든 item을 가져와야 할 때는 sorted set으로 일부 범위만 가져오거나 Collection을 작은 단위로 쪼개서 저장

ex) 모든 key를 가져오는 명령어,flush, getAll 같은 메서드
In-memory 특성상 메모리 파편화, 가상메모리 등에 대한 이해 필요

Redis 운영

메모리 관리

메모리가 꽉차면 서버가 죽어버린다,,

  • Swap 이 있다면? -> 메모리가 꽉차면 swap을 사용
    swap은 메모리 페이지를 disk에 저장해놓고 필요하면 다시 그것을 로딩시키고 또 다른 걸 내려놓고 처리하는 방식
    따라서 swap이 한번이라도 발생한 메모리 page는 계속해서 swap이 발생 -> 성능이 떨어진다
  • Swap이 없다면? 서버가 죽어버릴 수 있다
  • MaxMemory: 이 메모리 이상 쓰지 말라는 설정, 해당 메모리 이상으로 쓰게 되는 경우 랜덤하게 키를 지우거나 expire 목록에서 제거 -> 그러나 메모리 allocater에 따라 메모리 파편화 문제가 생길 수 있음
    Jemlloc 4.x 대 부터 파편화가 많이 해결되었는데 버전에 따라 다르게 동작할 수 있다.

메모리 파편화(단편화)

사용 가능한 메모리가 충분히 존재하지만 할당(사용)이 불가능한 상태
메모리의 공간이 작은 조각으로 나누어져있는데 보통 jemalloc이 할당하는 메모리 페이지의 크기가 필요한 조각보다 크기 때문

Replication - Fork

레디스는 replication을 쓰게 되면 필연적으로 fork를 한다
Fork를 하게 되면 read가 많은 서비스는 괜찮지만 write가 헤비한 서비스인 경우 메모리를 최대 2배까지 쓸 수 있다.
따라서 작은 메모리의 instance를 여러개 운영하는 것이 안전함

메모리 부족 Solution

  • 돈을 들이는 방식..ㅎㅎ 좀 더 메모리 많은 장비로 Migration
  • 있는 데이터 줄이기

Replication

  • Async Replication -> Replication Lag 발생 가능
  • statement Replication 과 유사한 형태 (쿼리로 replication 된다)
    -> 발생할 수 있는 문제는 now와 같이 쿼리 실행 시점에 따라 데이터가 달라지는 문제들
  • Master는 slave의 sync 명령에 따라 현재 메모리 상태 저장을 위해 fork 함 -> 이것 때문에 disk에 dump하고 memory도 많이 쓴다

데이터 분산

Application 레벨에서의 분산 - constant hashing

서버가 추가되거나 다운되어도 리밸런싱의 비용을 최소한으로 하는 방법 -> 딱 추가되거나 사라진 데이터 범위만 리밸런싱 일어나면 됨 (1/N만 변동)
알고리즘: 자신의 해시보다 큰 해시값 중 가장 작은 해시값을 갖고 있는 서버에 저장된다

샤딩

  • Range: 단순히 hash의 범위에 따라 데이터를 분리한다, 이럴 경우 상황에 따라 놀고 있는 서버가 생김
  • Indexed: 기존 갯수의 2의 제곱씩으로 서버를 증가시킨다, 데이터는 나머지에 따라 분산된다, 이럴 경우 새로 생긴 서버에 기존 서버 index에 해당하는 데이터의 절반씩 이동하면 된다 -> ex 모듈 0,1 이 있었다면 0,1,2,3으로 모듈이 늘어나고 기존 0에 있던 절반의 데이터는 2로, 1에 있던 절반의 데이터는 3으로 가게 된다
  • 모든 index 정보를 index 서버가 관리하기 때문에 index 서버가 다운되면 문제가 된다

Redis Cluster


primary가 죽으면 secondary가 primary로 승격

  • 장점
    * 자체적인 Primary, Secondary Failover (health check후 자동 승격)
    • Slot 단위의 데이터처리
  • 단점
    * 메모리 사용량이 더 많음
    • Migration 자체는 관리자가 시점 결정 필요
    • Library 구현 필요

0개의 댓글