본문 바로가기
개인공부/Web API 게임 서버 공부

배경지식 - Redis

by 하고싶은건많은놈 2023. 4. 20.

Redis

Remote Dictionary Storage의 약자로 모든 데이터를 메모리에 저장하고 조회하는 인메모리 데이터 저장소

오픈 소스로 개발되었으며 메모리 기반 key-value 구조를 가지는 데이터베이스 관리 시스템

  • string / bitmap / hash / list / set / sorted set 등 다양한 자료구조를 지원하기 때문에 데이터를 간단하게 저장 / 조회 / 수정할 수 있음
  • 메모리에 데이터를 저장하기 때문에 매우 빠른 Read/Write 속도를 가짐
  • Master-slave 구조로 여러 노드에 데이터를 분산하여 저장하고, 클러스터링을 지원함으로써 높은 가용성과 확장성을 제공
  • 필요에 따라 디스크에 데이터를 저장하여 영속화시킬 수 있음
    • 레디스는 정기적으로 Snapshot을 저장하여 데이터베이스의 내용을 디스크에 씀
    • 레디스의 재기동시 Snapshot을 불러와 데이터를 복원
    • 단, Snapshot 저장시 명령어 수행이 제한되어 BGSAVE 옵션으로 백그라운드에서 스냅샷을 저장할 수 있으나, 자식 프로세스가 생성되기 때문에 메모리 사용량이 두배가 될 수 있음
  • 싱글 쓰레드로 동작하기 때문에 요청시 주의가 필요
  • Pub/Sub 기능을 활용하여 채널(Channel)과 구독(Subscribe)을 바탕으로 한 메시지 기반의 통신을 효과적으로 구현할 수 있음

자료형에 따른 Redis 기본 명령어

String

SET : key에 value를 저장

SET username "hello"

 

GET : key에 해당하는 value를 가져옴

GET username

 

DEL : key를 삭제

DEL username

 

 

List

  • 양방향 linked list 형태의 자료형

 

LPUSH / RPUSH : 리스트의 왼쪽(앞) / 오른쪽(뒤)에 데이터 추가

LPUSH fruits "apple" "banana" "cherry"
RPUSH fruits "orange" "pineapple"

 

LPOP / RPOP : 리스트의 앞 / 뒤에서 데이터 획득 후 삭제

LPOP fruits
RPOP fruits

 

LINDEX : 리스트의 특정 인덱스에 해당하는 데이터 조회

LINDEX fruits 2

 

LRANGE : 리스트의 특정 범위에 해당하는 데이터 조회

LRANGE fruits 0 2

 

LLEN : 리스트의 길이(= 요소의 개수) 조회

LLEN fruits

 

 

Set

  • 중복을 허용하지 않는 고유한 값을 저장하는 자료형
  • 집합이기 때문에 교집합 / 합집합 / 차집합 등을 매우 빠르게 구할 수 있음

 

 

SADD : Set에 데이터 추가

SADD my_set "apple" "banana"

 

SMEMBERS : Set의 모든 멤버 조회

SMEMBERS my_set

 

SISMEMBER : Set에 특정 멤버가 속해있는지 확인

SISMEMBER my_set "banana"

 

SREM : Set에서 특정 멤버 제거

SREM my_set "apple"

 

SCARD : Set의 크기(= 멤버의 개수) 조회

SCARD my_set

 

SUNION / SINTER / SDIFF : Set의 합집합 / 교집합 / 차집합 구하기

SUNION set1 set2 set3
SINTER set1 set2 set3
SDIFF set1 set2

 

 

Sorted Set

  • 값과 가중치(score)로 이루어진 정렬된 자료형
  • 값은 중복을 허용하지 않지만 가중치는 중복이 가능함
  • 가중치를 기준으로 오름차순 정렬됨

 

ZADD : Sorted Set에 멤버와 스코어 추가

ZADD my_sorted_set 90 "John" 85 "Alice" 92 "Bob"

 

ZRANGE / ZREVRANGE : Sorted Set의 특정 범위 내 멤버 조회(오름차순 / 내림차순)

ZRANGE my_sorted_set 0 2
ZREVRANGE my_sorted_set 0 2

 

ZREM : Sorted Set에서 특정 멤버 제거

ZREM my_sorted_set "Alice"

 

ZSCORE : Sorted Set에서 특정 멤버의 스코어 조회

ZSCORE my_sorted_set "Bob"

 

ZRANK : Sorted Set에서 특정 멤버의 순위 조회

ZRANK my_sorted_set "John"

 

ZCARD : Sorted Set의 크기(=멤버의 개수) 조회

ZCARD my_sorted_set

 

ZUNIONSTORE / ZINTERSTORE : Sorted Set의 합집합 / 교집합을 다른 Sorted Set에 저장

ZUNIONSTORE destination numkeys key1 key2 ...
ZINTERSTORE destination numkeys key1 key2 ...

 

 

Hash

  • 필드-값 쌍(Field-Value Pair)의 집합으로 이루어진 자료형
  • 각 필드와 그에 대응하는 값이 함께 저장됨
  • 암호화와는 관계없음

 

HSET : Hash에 필드-값 쌍을 추가 또는 수정

HSET user:1 name "John"

 

HMSET : Hash에 여러 개의 필드-값 쌍을 한번에 추가 또는 수정

HMSET user:1 name "John" age 30 email "john@example.com"

 

HGET : Hash에서 특정 필드의 값을 조회

HGET user:1 name

 

HMGET : Hash에서 여러 개의 필드에 대한 값을 조회

HMGET user:1 name age

 

HDEL : Hash에서 특정 필드를 삭제

HDEL user:1 email

 

HKEYS : Hash의 모든 필드들의 이름 조회

HKEYS user:1

 

HVALS : Hash의 모든 필드들의 값을 조회

HVALS user:1

 

HGETALL : Hash의 모든 필드-값 쌍을 조회

HGETALL user:1

CloudStrucutres 라이브러리를 사용한 Redis 활용 예시

CloudStructures는 StackExchange.Redis에 기능을 추가하고 사용하기 편하게 랩핑한 라이브러리

 

public static class RedisServer
{
    public static RedisConnection Connection { get; }
    public static RedisServer()
    {
        var config = new RedisConfig("name", "connectionString");
        Connection = new RedisConnection(config);
    }
}

RedisConnection 객체는 반드시 static으로 선언되어야함

RedisConfig의 두번째 매개변수인 connectionString에는 Redis 서버의 호스트 이름 / 포트 번호 / 액세스 키 등의 정보를 기입

 

var key = "test-key";
var defaultExpiry = TimeSpan.FromDays(1);
var redis = new RedisString<T>(RedisServer.Connection, key, defaultExpiry)

RedisString<T> 클래스를 사용하여 Redis 서버에서 string 데이터를 저장하고 관리하기 위한 redis 객체를 생성

T는 데이터의 형식을 나타내며, RedisString 외에도 사용하고자 하는 데이터의 구성에 따라 RedisList / RedisSet / RedisHashSet 등을 사용할 수 있음

 

await redis.SetAsync(data);

위와 같이 생성해둔 객체의 명령어들을 사용하여 데이터를 관리할 수 있음

 


CloudStrucutres 라이브러리 문법

String

var v = new RedisString<int>(RedisConnection, "test_key", null);

 

  • v.SetAsync() : SET에 해당
  • v.GetAsync() : GET에 해당
  • v.DeleteAsync() : DEL에 해당

 

  • v.Increment(Decrement)Async(x) : 기존 값에서 x만큼 증가(감소)
  • v.IncrementLimitByMax(Min)Async(x, y) : 기존 값에서 x만큼 늘리지만 y보다 크지는(작지는) 않음
  • v.GetWithExpiryAsync() : 키의 유효 기간 획득
  • v.GetAndDeleteAsync() : 값 획득 후 삭제
  • v.SetAsync(x, null, When.NotExists) : 키가 존재하지 않을 때만 값 저장

 

List

var v = new RedisList<int>(RedisConnection, key, defaultExpiry);

 

  • v.Left(Right)PushAsync() : LPUSH / RPUSH에 해당
  • v.Left(RIght)PopAsync() : LPOP / RPOP에 해당
  • v.GetByIndexAsync() : LINDEX에 해당
  • v.RangeAsync() : LRANGE에 해당
  • v.LenghtAsync() : LLEN에 해당

 

Set

var v = new RedisSet<int>(RedisConnection, key, defaultExpiry);

 

  • v.AddAsync() : SADD에 해당
  • v.MemberAsync() : SMEMBERS에 해당
  • v.ContainsAsync() : SISMEMBER에 해당
  • v.RemoveAsync() : SREM에 해당
  • v.LengthAsync() : SCARD에 해당
  • v.CombineAsync() : SUNION / SINTER / SDIFF에 해당

 

Sorted Set

var v = new RedisSortedSet<int>(RedisConnection, key, defaultExpiry);

 

  • v.AddAsync() : ZADD에 해당
  • v.RangeByRankAsync() : ZRANGE / ZREVRANGE에 해당
  • v.RemoveAsync() : ZREM에 해당
  • v.ScoreAsync() : ZSCORE에 해당
  • v.RankAsync() : ZRANK에 해당
  • v.LengthAsync() : ZCARD에 해당
  • v.CombineAndStoreAsync() : ZUNIONSTORE / ZINTERSTORE에 해당

 

Hash

var v = new RedisDictionary<int, bool>(RedisConnection, key, defaultExpiry);

 

  • v.SetAsync() : HSET에 해당
  • v.GetAsync() : HGET에 해당
  • v.DeleteAsync() : HDEL에 해당
  • v.KeysAsync() : HKEYS에 해당
  • v.ValuesAsync() : HVALS에 해당
  • v.GetAllAsync() : HGETALL에 해당
  • HMSET과 HMGET에 해당하는 메서드는 발견하지 못함...

 


Redis를 사용한 Lock

서버가 분산되어있을 경우 같은 유저에 대해 동시에 접근하는 경우가 발생할 수 있음

이를 Redis에서 원자성을 보장하는 명령어인 SETNX를 사용하여 해결 가능

  • 유저 접근시 SETNX로 해당 유저에 대한 키값을 생성
  • SETNX는 키의 존재 여부를 확인하여 존재하지 않을 때만 해당 키와 값을 저장
  • 따라서 이미 키가 존재한다면 같은 유저에 대해 중복 요청이 들어온 것이므로 요청을 처리하지 않아야 함
try
{
    var redis = new RedisString<AuthUser>(_redisConn, key, NxKeyTimeSpan());

    if (await redis.SetAsync(new AuthUser
    {
        // emtpy value
    }, NxKeyTimeSpan(), StackExchange.Redis.When.NotExists) == false)
    {
        return false;
    }
}
catch
{
    return false;
}

return true;

CloudStructures 라이브러리를 활용하여 해당 기능을 구현할 수 있음

  • Redis 객체를 생성, 이 때 key는 유저에 따라 달라지는 값
  • SetAsync() 메서드로 해당 키를 설정
    • 키에 대한 밸류는 필요하지 않으므로 비워둠
    • 두번째 매개변수로 NxKeyTimeSpan()을 지정한 것과 같이 키가 만료되는 시간도 설정 가능
    • 세번째 매개변수로 지정한 StackExchange.Redis.When.NotExists로 SETNX를 구현
  • 키가 존재할경우 false, 아닐 경우 true를 반환하기 때문에 이 값을 통해 중복된 접근인지 아닌지 판별할 수 있음

'개인공부 > Web API 게임 서버 공부' 카테고리의 다른 글

배경지식 - Web 서버 구조  (0) 2023.04.23
배경지식 - ZLogger  (0) 2023.04.20
배경지식 - ORM  (1) 2023.04.20
배경지식 - C#  (0) 2023.04.20
배경지식 - ASP.NET  (0) 2023.04.19

댓글