728x90
반응형
728x90
반응형
반응형

 

Redis의 Sorted Set은 특정 요소들의 집합으로, 각 요소는 고유한 값(value)과 정수나 부동소수점 형식의 점수(score)를 함께 가집니다. 이 점수에 따라 요소들이 자동으로 정렬됩니다.

key -> [(member1, score1), (member2, score2), (member3, score3), ...]

특징:

  1. 자동 정렬: 요소들이 점수에 따라 오름차순으로 자동 정렬됩니다.
  2. 빠른 조회: 특정 범위에 있는 요소들을 빠르게 조회할 수 있습니다.
    • 범위 조회 지원: 점수나 멤버 인덱스를 기반으로 부분 집합을 효율적으로 검색 가능합니다.
  3. 순위 계산: 요소들의 순위(rank)를 쉽게 계산할 수 있습니다.
  4. 중복 불가: 멤버 값은 중복될 수 없습니다. (단, 점수는 중복 가능)
  5. O(log(N)) 복잡도: 추가, 삭제, 조회 연산의 시간 복잡도는 O(log(N))입니다.

주요 명령어:

  • ZADD: Sorted Set에 요소를 추가합니다.
  • ZRANGE: 지정한 범위의 요소를 가져옵니다.
  • ZREM: 요소를 삭제합니다.
  • ZSCORE: 요소의 점수를 확인합니다.
  • ZRANK: 요소의 순위를 확인합니다.
//ZADD key score member [score member ...] 추가
ZADD le/aderboard 100 alice
ZADD leaderboard 200 bob 150 charlie

//ZSCORE key member 조회
ZSCORE leaderboard alice  # 결과: 100

//ZRANGE key start stop [WITHSCORES] 인덱스기반(시작; 0) 범위 조회 [점수도 같이 반환]
ZRANGE leaderboard 0 -1 WITHSCORES  # 전체 조회

//ZRANGEBYSCORE key min max [WITHSCORES] 점수 기반 범위 조회
ZRANGEBYSCORE leaderboard 100 200 WITHSCORES

//ZRANK key member 0시작 순위 반환(오름차순)
ZRANK leaderboard bob  # 결과: 2

//ZREVRANK key member 내림차순 순위 반환
ZREVRANK leaderboard bob  # 결과: 0

//ZREM key member [member ...] 특정 맴버 삭제
ZREM leaderboard alice

활용 사례

(1) 리더보드

게임에서 점수에 따라 순위를 관리할 때 유용합니다.

  • 점수를 score로, 사용자 이름이나 ID를 member로 저장.
  • 순위 조회, 점수 범위 내 사용자 검색 등이 가능.

(2) 태스크 스케줄링

  • 점수를 타임스탬프로 사용하여 작업을 스케줄링.
  • 특정 시간 범위의 작업을 조회하거나 삭제 가능.

(3) 우선순위 큐

  • 점수를 우선순위로 사용하여 작업을 관리.

주의점

  • 메모리 사용량: 점수와 멤버를 함께 저장하므로 메모리 사용량이 단순 Set보다 큽니다.
  • 점수 정밀도: 점수는 부동소수점(Floating Point)이므로 정밀도에 주의해야 합니다.

 

TTL 설정

Redis의 Sorted Set 자체에는 직접적인 TTL(Time To Live) 설정이 지원되지 않습니다. Redis는 Key-Value 기반으로 동작하므로, TTL은 키(key) 단위로 설정됩니다. 따라서 zset 안의 각 맴버에게 만료시간을 설정하는 것이 아닌 Sorted Set 전체에 대해 TTL을 설정해야 합니다.

  • TTL은 키 전체에 적용됩니다. Sorted Set의 개별 멤버에 TTL을 설정할 수는 없습니다.
    • 개별 멤버 TTL 관리가 꼭 필요하다면, 점수를 TTL처럼 사용하거나 별도 키로 TTL 관리하는 방법이 가장 실용적입니다.
  • TTL 설정 후, 키이 삭제되면 Sorted Set에 저장된 모든 멤버도 함께 삭제됩니다.
  • TTL은 주로 일시적인 데이터에 사용됩니다. 예를 들어, 일일 리더보드나 임시 작업 큐 등에 적합합니다.
  • 키가 이미 만료된 상태에서 접근하면, Redis는 키를 자동으로 삭제하고, 해당 키에 대한 작업은 무시됩니다.

레디스로 구현하려면..

sorted set으로 랭킹 저장하고 각 맴버별로 hash만들어서 ttl 설정한 후 노티피케이션이나 주기적으로 확인하여 set에서 삭제하는 로직 작성하여 수동으로 관리해야함..

# Sorted Set에 멤버 추가
ZADD myset 100 user1
ZADD myset 200 user2

# user1의 TTL이 필요하면 별도 키 생성 후 TTL 적용
SET user1_temp_value some_value
EXPIRE user1_temp_value 3600  # user1_temp_value 키에 TTL 1시간 설정

 

728x90
반응형

'개발 > cache' 카테고리의 다른 글

분산락 - redis 사용  (0) 2024.11.08
[redis] 기초  (0) 2023.02.08
반응형

분산락 (Distributed Lock)

분산락은 분산 시스템에서 여러 인스턴스가 동시에 동일한 리소스를 수정하거나 접근할 때, 경쟁 조건(race condition)을 방지하기 위해 사용하는 메커니즘입니다. 주로 여러 서버에서 동시에 동일한 데이터나 자원에 접근할 때, 하나의 서버만이 리소스를 수정하거나 작업을 진행할 수 있도록 동기화(synchronization)합니다.

분산락은 시스템의 일관성을 보장하며, 여러 서비스가 동시에 동일한 작업을 수행하지 않도록 하여 데이터의 무결성을 유지합니다.

분산락 구현 방법

분산락을 구현하는 방법은 여러 가지가 있으며, 각각의 방식은 특정 상황에 맞춰 사용할 수 있습니다. 주요 분산락 구현 방법은 다음과 같습니다:

  1. 데이터베이스 기반 락 (Database Locking)
    • 비관락 쓰기락 베타락
      • 구현 방식: 데이터베이스에서 특정 레코드를 업데이트하거나 SELECT FOR UPDATE와 같은 쿼리를 사용하여 락을 걸고, 이를 통해 락을 구현합니다. 
      • 장점: 간단하게 구현할 수 있으며, 대부분의 관계형 데이터베이스가 지원합니다.
      • 단점: 성능 문제, 락 경합, 교착 상태(deadlock) 등이 발생할 수 있습니다.
    • 네임드락 (Named Lock)
      • 구현 방식: 네임드락은 일반적으로 데이터베이스가 제공하는 GET_LOCK(), RELEASE_LOCK() 등의 함수를 사용하여 이름이 지정된 락을 설정합니다. 이 락은 데이터베이스의 특정 리소스가 아니라, 지정된 이름을 가진 락을 사용하여 락을 설정합니다. 트랙젝션 단위가 아닌 세션 단위의 락
  2. Redis 기반 락 (Redis Lock)
    • 구현 방식: Redis의 SETNX (SET if Not Exists)와 EXPIRE 명령어를 사용하여 락을 구현합니다. 이 방식은 Redis 서버를 통해 분산 환경에서 빠르고 효율적으로 락을 관리할 수 있습니다.
    • 장점: 빠르고, TTL(시간 만료)을 지원하여 자동으로 락을 해제할 수 있습니다. 분산 환경에 적합합니다.
    • 단점: Redis 서버가 다운될 경우 락이 풀리지 않는 문제가 발생할 수 있습니다.
  3. Zookeeper 기반 락 (Zookeeper Lock)
    • 구현 방식: Zookeeper를 활용하여 분산 락을 구현합니다. Zookeeper의 Ephemeral Node와 Watchers 기능을 사용하여 락을 관리합니다.
    • 장점: 강력한 일관성과 고가용성을 제공합니다. 락이 해제되면 자동으로 다른 노드가 락을 획득할 수 있습니다.
    • 단점: 설정이 복잡하고, 성능 이슈가 발생할 수 있습니다.
  4. Consul 기반 락 (Consul Lock)
    • 구현 방식: Consul은 Session과 Key/Value Store를 사용하여 분산 락을 구현할 수 있습니다. 락은 세션을 기반으로 하며, 세션이 만료되면 락이 자동으로 해제됩니다.
    • 장점: Consul의 분산 시스템과 고가용성을 활용할 수 있습니다.
    • 단점: Consul을 설정하고 관리하는 복잡성이 있습니다.
  1.  

Redis 기반 분산락 구현 예시

Redis를 사용하여 분산락을 구현하는 방법을 소개합니다. Redis의 SETNX 명령어와 EXPIRE 옵션을 활용하여 락을 구현할 수 있습니다.

  1. 분산 락을 획득하고, 획득되지 않으면 예외를 던지는 방식 (Throwing)
  2. 스핀락 방식 (Spinlock)

1. Redis 분산락 구현(예외를 던지는 방식)

Redis에서 분산락을 구현할 때 사용할 수 있는 핵심 명령어는 SETNX와 EXPIRE입니다.

  • SETNX: Key가 존재하지 않으면 값을 설정하고, 존재하면 아무 작업도 하지 않습니다. 락을 구현하는 데 유용합니다.
  • EXPIRE: Key에 대한 TTL(시간 제한)을 설정하여 일정 시간 후 락을 자동으로 해제할 수 있습니다.
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class RedisDistributedLock {

    private final RedisTemplate<String, String> redisTemplate;
    private static final String LOCK_PREFIX = "lock:";

    public RedisDistributedLock(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 락을 얻고, 성공하면 block을 실행, 실패하면 예외를 던지는 방식
    public <T> T lockAndRun(String key, long timeout, TimeUnit timeUnit, Runnable block) {
        String lockKey = LOCK_PREFIX + key;
        boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", timeout, timeUnit);

        if (lockAcquired) {
            try {
                block.run();
            } finally {
                releaseLock(lockKey);
            }
        } else {
            throw new RuntimeException("Unable to acquire lock for key: " + key);
        }

        return null;
    }

    // 락 해제
    private void releaseLock(String key) {
        redisTemplate.delete(key);
    }
}
  • setIfAbsent: Redis의 SETNX 명령어를 사용하여 락을 획득하려고 시도합니다. 락이 이미 있을 경우 false를 반환하고, 없으면 true를 반환하여 락을 획득합니다.
  • timeout과 timeUnit: 락을 획득할 수 있는 시간 제한을 설정합니다. 이 시간이 지나면 자동으로 락이 풀립니다.
  • 락을 성공적으로 획득하면 block.run()이 실행되고, 작업이 끝난 후 락을 해제합니다.
  • 락을 획득하지 못하면 예외를 던집니다.

장점:

  • 직관적: 락을 획득하지 못했을 때 바로 예외를 던지므로 호출하는 쪽에서 락 실패에 대한 처리를 명확히 할 수 있습니다.
  • 성공적인 락 획득 후 작업 실행 보장: 락을 획득한 후 작업이 실행되므로, 동시에 여러 프로세스에서 동일한 리소스를 수정하지 않게 됩니다.

단점:

  • 락 획득 실패시 예외 처리 필요: 락을 획득하지 못했을 경우 예외가 발생하므로 호출자 측에서 이를 처리해야 합니다. 예외가 자주 발생할 경우 성능에 영향을 줄 수 있습니다.
  • 락 획득 실패에 대한 대처가 복잡할 수 있음: 예외를 던지면 호출자가 반드시 예외를 처리해야 하므로, 이 부분에서 코드가 복잡해질 수 있습니다.

2. Redis 분산 락 구현 (스핀락 방식)

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class RedisSpinlock {

    private final RedisTemplate<String, String> redisTemplate;
    private static final String LOCK_PREFIX = "lock:";

    public RedisSpinlock(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 스핀락을 이용한 락 획득 및 실행
    public <T> T spinLockAndRun(String key, long timeout, TimeUnit timeUnit, Runnable block) {
        String lockKey = LOCK_PREFIX + key;

        long start = System.currentTimeMillis();
        boolean lockAcquired = false;

        while (System.currentTimeMillis() - start < timeUnit.toMillis(timeout)) {
            lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", timeout, timeUnit);
            if (lockAcquired) {
                try {
                    block.run();
                    break;  // 성공적으로 작업을 마쳤으면 루프 종료
                } finally {
                    releaseLock(lockKey);
                }
            }

            try {
                // 락을 획득하지 못하면 일정 시간 대기 후 재시도 (스핀락)
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        if (!lockAcquired) {
            throw new RuntimeException("Unable to acquire lock for key: " + key);
        }

        return null;
    }

    // 락 해제
    private void releaseLock(String key) {
        redisTemplate.delete(key);
    }
}

설명:

  • 스핀락(Spinlock): 락을 획득하지 못하면 일정 시간 동안 계속해서 락을 시도하며 기다립니다. setIfAbsent로 락을 시도하고, 락을 획득하지 못하면 sleep을 이용해 일정 시간 대기한 후 다시 시도합니다.
  • timeout과 timeUnit: 락을 시도할 최대 시간을 설정하고, 락을 획득한 후 해당 시간 동안 작업을 실행합니다.
  • 락을 성공적으로 획득한 후 작업을 실행하고, 완료 후 락을 해제합니다.

장점:

  • 낮은 대기 시간: 예외를 던지지 않고 계속해서 락을 시도하므로 예외 처리보다 더 부드러운 흐름을 유지할 수 있습니다.
  • 재시도 방식: 락을 획득하지 못하면 일정 시간 대기 후 재시도하므로, 락 경합이 많을 때 유용할 수 있습니다.

단점:

  • CPU 자원 낭비: 락을 시도하며 대기하는 동안 CPU를 계속 소모하게 되어, 시스템 부하가 증가할 수 있습니다.
  • 무한 루프 문제: 락을 계속해서 시도하지만, 특정 상황에서는 결국 락을 얻지 못하고 무한 루프에 빠질 수 있습니다. 이를 해결하려면 재시도 횟수를 제한해야 합니다.
  • 성능 저하: 락이 자주 경합되면, 스핀락을 반복하면서 성능 저하가 발생할 수 있습니다.
728x90
반응형

'개발 > cache' 카테고리의 다른 글

[redis] sorted set  (1) 2024.12.16
[redis] 기초  (0) 2023.02.08
반응형

레디스

  • nosql
    • not only sql
  • 비정형화된 데이터 사용 -> 유연성, 확장성, 실시간 응답
  • 레디스는 key-value형태이고 
    • 그래프, 다큐먼트, 콜롬배이스 형태도 있음
  • in memeory 데이터베이스 -> 모든 데이터가 메모리 위에 있어 디스크까지 가서 받아오지 않아도 돼서 빠름
  • 단순성 - 다양한 자료구조 제공(10가지 이상), 커맨드 처리, 이벤트 루프로 동작(싱글스레드; 한 번에 하나의 작업만 함)
  • 클라의 처리가 빠르고 단순
  • 확장성/고가용성
    • 클러스터 -> 샤딩을 위한 기술
    • 여러개 서버에 나눠서 저장

MSA에서 레디스?

  • 데이터 저장소(권장하지 않음) 쓸 수는 있다(10가지의 자료구조 제공; 개발의 편리성) -> 데이터의 영구저장을 할 수는 있다.(파일)
  • 캐시: 최소한의 리소스로 막대한 처리; 자체 HA 기능 제공
  • 메시지 브로커: pub/sub, event queue, stream

redis 자료구조

  • string(모든 종류의 문자열 저장 가능; 이진 데이터 포함)
    • set/get으로
      • set hello world -> key: hello, value: world
      • get hello -> world
    • 이미지, html 도 가능
    • 최대 512MB
    • string -> string
    • 키도 최대 512MB
    • command
      • incr(1씩 증가) / incrby(지정한 수만큼 증가; 락 안 잡고도 증가시킬 수; mysql은 락을 잡음)
      • getset: 조회하고 결과 나오고 치환
      • 옵션: xx/nx
        • xx: 키가 있을 때만 저장(없으면 안 해; 없으면 nil 반환)
        • nx: 키가 없을 때만 저장(없으면 해; 있으면 nil반환)
  • list 순서를 가지는 문자열의 목록
    • 리스트 안에 인덱스 있음 reverse index도 있음; 추가되면 인덱스는 알아서 재정리됨  
      • command
        • rpush rpop lpush lpop(return 있음)
          • pop은 조회하고 삭제
        • ltrim 원하는 인덱스가 아니면 다 지움(void)
        • linsert(어디 앞에 넣는다) / lset(지정한 인덱스에 치환) 중간에 넣는 작업
          • 중간에 넣을 때는 O(n)이라 안 빠름
  • hash
    • hset(데이터 넣고 1반환) 
    • field-value 쌍을 가진 아이템의 집합; 데이터 페어를 무제한으로 늘릴 수 있음
    • rdbms의 테이블 데이터와 가장 유사 -> 변환 가능
    • hrandfield 랜덤 / 양수를 주면 겹치지 않게; 음수는 겹쳐도 되게
  • set
    • sadd
    • 중복 안됨
    • 정렬되지 않은 문자열
    • srandmember 랜덤
    • 객체 간 관계 계산(교/차/합/원소개수)
      • sunion 합
      • sinter 교
      • sdiffer 차
  • sortedSet 
    • 스코어 값으로 정렬되는 고유한 문자열의 집합(정렬되어 있음; 매번 정렬할 필요 없음)
    • 모든 아이템은 value-score 쌍
    • 같은 스코어라면 사전순 정렬
    • value는 중복되지 않음
    • 인덱스로 접근 가능(O(logn))
    • -인덱스 사용 가능 나부터 -1
    • command
      • zadd 키 스코어 밸류
      • 옵션
      • xx/nx/lt/gt / withscores(밸류랑 스코어랑 같이 조회)/ rev(역순)/byscore
      • zrange 인덱스 기반으로 데이터 조회
      • zrandmember 랜덤 뽑기
      • bylex(사전순)
        • zrange mySortedSet (b (f bylex
          • (: 포함하지 않고 
          • (b 초과
          • [: 포함하고
  • bitmap
    • 확장된 string형태의 자료구조
    • 2^32개의 비트를 가질 수
    • 저장공간을 줄일 수
    • setbit, getbit, bitcount(1 개수 카운트)
  • hyperloglog
    • 집합의 카디널리티를 추정하는 자료구조
    • 대용량(로그)의 개수 카운팅; 중복제거
    • 12KB 고정용량(엄청 큰 데이터도), 1% 미만의 오차
    • pfadd, pfcount, pfmerge
  • geospatial
    • 위도, 경도 쌍; 지구가 완벽한 '구'라는 전제; 오차가 약간 있음
    • 내부적으로는 sortedSet
    • 특정 위치를 기준으로 검색할 수
    • 반경 몇 미터 안 검색 가능
    • 사각형으로도 조회가능
  • stream
    • 아파치 카프카에서 영향
    • append only 한 key-value형식의 자료구조
    • 메시지 브로커에 사용

key를 관리하는 법

  • 자동으로 생성되고 삭제됨
  • 키가 존재하지 않을 때 아이템을 넣으면 삽입 전 빈 자료구조 생성
  • 모든 아이템을 삭제하면 키도 삭제됨(스트림 예외)
  • 키가 존재하지 않을 때
    • 키를 삭제하는 명령을 하거나;
    • 키에 저장된 아이템을 삭제하거나;
    • 자료구조 크기를 조회하는 read only 커맨드를 수행하면
      • -> 키가 없지만 있는 척; 있고 아이템이 없는 것처럼 동작
  • command
    • 조회
    • keys 명령어는 위험; 다른 요청 blocking 됨;
      • scan 커맨드로 대체가능(여러 번 끊어서 데이터 가져옴)
      • 다음에 조회할 커서를 알려줌 그걸로 연쇄적으로 조회 가능
    • sort, exists, rename/renamenx, copy, type
    • 삭제 flushall, del, unlink
    • 만료시간 expire, expireat, expiretime, ttl

리더보드

  • sorted set: 중복을 허용하지 않고 정렬이 되는 자료구조
  • 데이터입력: ZADD daily-score:220817 200 player:286
  • 랭킹 합산; ZUNIONSTORE weekly-score:2208-3 3 daily-score:220815 daily-score:220816 daily-score:220817 (integer) 4 
  • 최대 다섯 개 까지만 저장 하려면 
    • 검색 기록이 다섯개 이하인 경우 → 삭제 안 함 
    • 검색 기록이 다섯 개보다 많을 경우 → 인덱스 0 삭제
    • 항상 인덱스 -6을 삭제한다면? 오버헤드 감소! 
    • ZREMRANGEBYRANK 사용

캐시로의 레디스

  • 레디스는 빨라, 응답속도를 줄일 수, 자체적인 ha 제공

캐싱 전략

  • 읽기 전략: look aside(lazy loading)
    • 우선 캐시에 데이터가 있는지 확인; 없으면(cache miss) 디비에서 읽고 캐시에 저장
      • 초반에 느릴 수 있음
      • 미리(오픈전) 데이터를 넣어주는 작업을 하기도 하는데 이를 cache warming
    • 캐시가 없더라도 장애로 이어지지 않음
    • 반대로 디비에 몰리면 장애가 날 수도 있음
    • 원본과 동일하도록 유지
      • 디비 업데이트 칠 때 캐시에도 업데이트해야(write through)
  • 쓰기 전략(데이터 일관성)
    • write through(중요하면 동기방식; 별로 안 중요하면 비동기)
      • 항상: redis -> db 업데이트
      • 매번 2개 저장
      • 쓰기 시간이 듦
      • 다시 사용하지 않아도 될 데이터가 이중으로 저장될 수도
      • expire time 설정 추천
    • cache invalidation
      • 디비에 저장할 때 캐시를 삭제
      • 찾을 때 디비에서 가져오고 후에 캐시 업데이트
      • 불일치는 발생하지 않음
    • write behind(나중에 디비에 써; 핫해서 계속 디비 넣기 부하 걸리니; 일정 주기로 동기화)
      • 쓰기가 빈번하다면 
      • 디비에 대량의 write면 디스크 IO -> 성능저하로 이어지기 때문에
      • 캐시에 먼저 업데이트하고
      • 비동기적으로 디비에 업데이트
      • 실시간으로 엄청 정확하지 않아도 될 경우
      • 단, 캐시 날아가면,, 끝

 

레디스를 캐시로 사용할 때 주의할 점

  • 자주 사용하는 데이터 일부만 임시로 저장해야(적은 양)
  • 적절한 메모리를 유지할 수 있도록 적절히 삭제하고 들어오도록 유지해야 함
  • TTL(time to live) 사용은 필수(캐시 유지시간)
    • 메모리 꽉 차면 레디스 자체의 max memory policy에 의해 가장 오래된 키부터 삭제
      • 쓰기를 막는 설정이 기본이긴 한데 이게 더 장애 유발 가능성 그래서 오래된 키부터 삭제하는 정책
      • default는 저장 불가 -> 장애로
      • 자주 사용하지 않는 거 삭제하는 방식으로 설정 변경 필요
  • TTL을 너무 작게 하면 cache stampede 현상
    • cache miss가 날 경우 조회 시간차로 (db) duplicate read, (redis) duplicate write 발생할 수도
  • 캐시로 사용할 경우
    • 영구저장소가 아닌 임시라서 캐시의 백업정책 필수는 아님; 비추
    • 캐시가 없는 경우 서비스장애로 이어진다면 ha구성 필요

메시지 브로커로서의 레디스

메세징 큐

  • 프로듀서 -> -> 컨수머
  • 데이터 푸시
  • 데이터 영속성: 읽으면 큐에서 데이터 삭제
  • 컨수머 추가되면 그 후에 추가된 메시지만 확인가능
  • 1:1

이벤트 스트림

  • 퍼블리셔 -> -> 섭스크리버(구독자)
  • 당겨감
  • 데이터 영속성: 저장소별 설정에 따라 특정기간 동안 저장
  • 구독자 추가되면 이전부터도 확인가능
  • 다:다; 1:다

레디스에서는

  • pub/sub
    • 최소한의 메시지 기능
    • 클러스터에서도 사용가능
    • 한번 채널에 전파되면 사라져; 일회성
    • 메시지가 잘 갔는지 모름(보관 안 함)
    • end-end에서는 별로
    • notification 딱 보내고 끝이면 괜찮음
    • command
      • subscribe / publish
  • list를 이용한 메시지 큐
    • list.exists
    • rpushx 이미 캐싱되어 있는(존재하는) 경우에만 입력(불필요한 리소스 줄일 수)
    • event loop 
      • 폴링: 이벤트 큐에서 확인 -> 핸들러
      • 즉각적인 확인 X, 주기별로 체크
      • 리스트에서 blocking을 추가하면 큐확인을 기다릴 필요 없음
      • brpop / blpop -> 있음 반환; 없으면 새로 올 때까지 기다렸다가 반환
      • circular queue: rpoplpush
  • stream 자료구조
    • 연속적으로 계속 들어오는 데이터 처리
    • 써도 사라지지 않고 남아있음
    • 카프카처럼 사용가능
    • 로그를 저장하기 적절
      • append only; 중간에 저장하지 않음
    • range 검색가능, tail 가능, 그룹핑 가능
    • xadd 
      • command에 "*" : 시간 millisec을 timestamp로 갖겠다,

데이터 영구적으로 저장하는 법

  • 재시작하면 데이터 날아감
  • 서버에 파일로 저장함
  • AOF: 커맨드 자체 저장; 과정을
    • 파일이 커지는 걸 막기 위해 aof는 bgrewriteaof로 파일을 다시 써서 압축
  • RDB: 데이터의 최종 스냅샷 저장(메모리 자체); 결과만
    • bgsave 커맨드로 새로운 스냅샷 저장
  • 이때 백그라운드 프로세스를 생성하기 때문에 메모리 초과로 인한 장애 발생 가능성 매우 큼
  • 비추

레디스 HA(high availability)

고가용성, 안정성 = 사용시간/전체시간

  • 복제: 마스터 -> 복제본 실시간 복사
  • 자동 failover: 마스터 노드에 발생한 장애를 감지해서 연결을 복제본 노드로 리디렉션 하도록; 수동으로 안 해도 됨

replication

  • master-replica
  • 마스터는 하나; 다중 마스터 불가(지원 안 함)
  • 마스터 노드는 rdb파일을 생성하여 리플리카 노드에 전달
    • 만드는 중에 메모리 증가
  • 리플리카에서 replicaof 실행
  • 쓰기는 마스터에서만
  • 장애 시 자동 failover 안돼서 잘 안 씀(커넥션 정보 수동으로 설정 변경해 줘야)

sentinel

  • ifnot: 복제 구성 시 장애 발생하면 직접 레디스 엔드포인트 변경 필요
    • replica of no one(복제본으로 활동하지 않도록) 설정하고 설정 변경 후 재시작
  • 데이터를 저장하지 않고 모니터링만 하는 프로세스(최소 3대 필요; 과반수가 필요; 홀수 구성)
  • 같이 설치됨; 추가로 다운로드할 필요 없음
  • 정상적으로 동작하기 위해선 최소 세 대의 센티널 프로세스가  필요
  • 마스터 파악; 마스터로 직접 연결 -> 변경되면 클라한테 알려줌
  • 마스터가 정상적으로 동작하는지 판단할 때 쿼럼(과반수) 이상일 때 페일오버 
  • 새로운 마스터가 되면 그동안의 데이터를 모두 날린 후 새로운 데이터를 복제받음
  • 다운된 마스터가 살아나면 새로운 마스터에 복제본으로 연결됨

cluster

  • full mesh 구조; 서로서로 감시(최소 3대, replica까지 치면 6대 필요)
  • 스캐일 업; 수직 확장: 스펙업
  • 스캐일 아웃; 수평 확장: 개수 증가
  • 일반적으로 업을 먼저 하고 아웃을 해
    • 키가 차있으면 먼저 메모리를 올려 스캐일 업
    • 메모리 문제가 아니라 처리량 문제면 스캐일 아웃 -> 처리량이 많아짐; 병렬도 가능; 마스터도 많아질 수
  • 데이터 샤딩
    • 개발(애플리케이션)에서 신경 쓰지 않아도 됨
      • 클러스터 아무 곳으로 던지면 알아서 리디렉션
    • 클라이언트 -> 클러스터 맵에 키랑 어디에 있는지 매핑 정보 들고 있음
      • 1 키 - 1 마스터
    • full mesh구조
      • 모든 마스터, 리플리카 노드는 가십 프로토콜을 사용해 서로서로 통신
      • 최소 세 개의 마스터 노드 필요
    • 샤딩 방법
      • 모든 키는 해시 슬롯에 연결됨(최대 16384개; mod 16384)
      • 해시 슬롯을 마스터 노드가 나눠가짐(나눠서 저장됨)
      • 키가 어떤 마스터에 저장되어 있는지 해시 함수로 알 수 있음(저장; 조회 모두 사용)
    • 마스터 노드 추가/삭제
      • 카프카는 데이터 이동 시 해당 데이터 사용 불가한데(전체 블라킹)
      • 레디스는 키를 새 마스터 옮기더라도 하나씩 옮기기 때문에 다운타임이 안 길어
      • 간편한 편.. 

레디스 도입 시

  • 어떤 용도?(persistence 기능 사용 유무)
    • 캐시
      • 저장 기능을 사용하지 않도록(원본이 디비에 있으면)
      • 캐시가 죽어도 애플리케이션에 영향가지 않도록(look-aside , write-through)
    • 디비
      • sentinel 사용
      • 애플리케이션 죽으면 영향 갈 수 있음
      • aof 사용하는 것이 안전
        • 주기적으로 rewrite 되도록 파라미터 조절 필요

레디스 구성을 할 때 미래를 생각하여 데이터를.. 짜야한다.

싱글 했다가 중간에 cluster로 바꿨는데 미리 염두에 두고 작업해야.. 나중에 cluster 옮겼는데 일부 명령어 안되고 잘 안될 수도 있다.

 

메모리 관리

  • used_memory: 논리적으로 Redis가 사용하는 메모리
  • used_memory_rss: OS가 Redis에 할당하기 위해 사용한 물리적 메모리 양
  • 삭제되는 키가 많으면 fragmentation 증가
  • 특정 시점에 피크를 찍고 다시 삭제되는 경우
  • TTL로 인한 eviction이 많이 발생하는 경우
  • 파편화 된 데이터 정리 해주는 명령어
    • CONFIG SET activedefrag yes

 

del 명령어보다 unlink

728x90
반응형

'개발 > cache' 카테고리의 다른 글

[redis] sorted set  (1) 2024.12.16
분산락 - redis 사용  (0) 2024.11.08

+ Recent posts