[Redis] Collection 자료구조들은 왜 만료시간 설정이 한번에 안 될까?

Hocaron·2025년 1월 5일
1

DB

목록 보기
16/16

Redis를 사용하다 보면 String 타입은 값 설정(SET)과 동시에 만료시간을 지정할 수 있는 반면, Collection 타입들은 값 설정(HSET, SADD 등)과 만료시간 설정(EXPIRE)을 따로 해야 한다는 것을 알 수 있다. 이러한 차이가 발생하는 이유를 Redis Expire 를 어떻게 관리할까?에서 살펴본 Redis의 내부 자료구조를 통해 살펴보자.

Redis의 기본 데이터 저장 구조

Redis의 데이터베이스는 redisDb 구조체로 표현되며, 두 개의 핵심 딕셔너리를 가지고 있다.

struct redisDb {
    dict *dict;     // 실제 키-값 데이터를 저장하는 딕셔너리
    dict *expires;  // 만료 시간 정보를 저장하는 별도의 딕셔너리
    // ... 다른 필드들
};

String 타입의 동작 방식

String은 Redis의 가장 기본적인 데이터 타입으로, 단순한 키-값 구조를 가진다.

SET key value [EX seconds]

SET 명령어를 실행하면,
1. redisDb->dict에 키-값 쌍이 단일 dictEntry로 저장된다.
2. EX 옵션이 있다면 동시에 redisDb->expires에 만료시간이 저장된다.

이러한 단순한 구조 덕분에 값 설정과 만료시간 설정을 atomic하게 수행할 수 있다.

Collection 타입의 복잡성

Collection 타입들은 내부적으로 더 복잡한 자료구조를 사용한다.

HSET hash field1 value1
EXPIRE hash 3600

Collection 타입의 명령어 실행하면,
1. redisDb->dict에 키-컬렉션 쌍이 저장되는데, 이때 값 부분이 복잡한 자료구조를 포인터로 가리킨다.
2. 해당 자료구조는 여러 요소들을 포함하고 있어서, 각 요소별로 다른 만료시간을 가질 수 없다.
3. 따라서 컬렉션 전체에 대한 만료시간은 EXPIRE 명령어로 따로 설정해야 한다.

각 Collection 타입별 내부 구조

  • Hash: ziplist 또는 hashtable 구조
  • Set: intset 또는 hashtable 구조
  • Sorted Set: skiplist와 hashtable 조합
  • List: quicklist(ziplist들의 연결리스트) 구조

Redis 자료구조와 명령어 처리 흐름

String 타입의 SET 명령어 처리

// SET key value EX seconds 명령어 처리
void setCommand(client *c) {
    robj *key = c->argv[1];
    robj *val = c->argv[2];
    
    // 1. dict에 키-값 저장
    setKey(c->db, key, val);
    
    // 2. 만료시간 옵션이 있다면 expires에 저장
    if (expire_found) {
        setExpire(c, c->db, key, expire);
    }
    
    // 3. 한 번의 명령어로 처리 완료
    addReply(c, shared.ok);
}

Hash 타입의 HSET 명령어 처리

// HSET hash field value 명령어 처리
void hsetCommand(client *c) {
    robj *o;
    
    // 1. 해시 객체 조회 또는 생성
    if ((o = hashTypeCreate(key)) == NULL) {
        if ((o = createHashObject()) == NULL) 
            return;
    }
    
    // 2. 해시 내부의 인코딩 타입 확인
    if (hashTypeGet(o) == REDIS_ENCODING_ZIPLIST) {
        // ziplist 인코딩일 경우의 처리
        if (hashTypeLength(o) >= hash_max_ziplist_entries)
            hashTypeConvert(o, REDIS_ENCODING_HT);
    }
    
    // 3. 해시에 필드-값 추가
    hashTypeSet(o, field, value);
    
    /* 여기서 만료시간도 함께 설정하려면:
     * - 인코딩 변환 이전에 설정? 이후에 설정?
     * - 해시 크기가 변경되어 인코딩이 바뀌면?
     * - 기존 만료시간이 있었다면?
     */
}
  1. redisDb->dict에서 해시 키 조회
  2. 해시가 존재하지 않는 경우
  • redisDb->dict에 새로운 키-값 쌍 추가
  1. 해시가 이미 존재하는 경우
  • 내부 자료구조(ziplist 또는 hashtable)에만 데이터를 추가

여기서 만료시간도 함께 설정하려면 고려되어야할 부분

  1. 매번 HSET 시마다 expires를 확인해야 함
  • 기존에는 키 값 조회만 수행했지만, expires 까지 확인이 추가로 필요로해 오버헤드가 발생할 수 있다.
  1. 자료구조 변환 과정에서 어떤 시점에 만료시간을 처리할 것인가?
  • 이전에 설정? 이후에 설정?할 것인지 복잡한 로직이 추가된다.
  1. 원자성 보장하는 것이 복잡해진다.
  • 자료구조 변환이 성공했더라도, 만료시간 설정에 실패하면 성공한 자료구조 변환까지 모두 롤백되어야한다.

Redis 7.4에서 추가된 Hash Field 단위의 만료시간 설정

Redis 7.4 버전에서는 Hash 자료구조에 대해 필드별 만료시간 설정이 가능한 HEXPIRE 명령어가 추가되었다. 이전 버전과의 차이점이 생겼길래 필드별 만료시간 설정이 가능해졌을까?

물론 필드가 아닌 키 설정에서 만료시간을 함께 설정할 수는 없다.

HEXPIRE 명령어 공식 문서

이전 버전의 제약사항

이전 Redis 버전에서는 키 단위로만 만료시간 설정이 가능하여, Hash의 내부 구조는 단순히 field-value 쌍만을 저장했다.

struct hash {
    dict *dict;  // field-value 쌍을 저장하는 해시 테이블
}

Redis 7.4의 변화

Redis 7.4에서는 Hash 자료구조의 내부 구현이 변경되어 필드별 만료시간 관리가 가능해졌다.

// 각 해시 필드의 메타데이터를 저장하는 구조체
struct hashFieldMeta {
    long long expiration;  // 만료 시간
    // 기타 메타데이터
};

// 변경된 해시 구조
struct hash {
    dict *data;       // field-value 쌍을 저장
    dict *metadata;   // field별 메타데이터(만료시간 등) 저장
}

Hash 자료구조 내부에 필드별 메타데이터를 저장하는 별도의 딕셔너리가 추가되었고, 각 필드의 만료시간을 독립적으로 관리할 수 있게 되었다.

하지만, 회사에서 이 기능을 사용하지 못할 것 같다

Redis 7.4부터 RSALv2, 서버 사이드 퍼블릭 라이선스 이중 라이센스가 적용되면서, 클라우드 서비스 제공자의 상업적 이용이 제한되었다. 회사에서는 AWS ElastiCache 를 사용하고 있는데 오픈소스 버전의 Redis를 기반으로 하기 때문에 HEXPIRE와 같은 신규 기능을 사용하기 어렵지 않을까 한다🫠

새로운 라이선스에 따라 Redis 오퍼링을 호스팅하는 클라우드 서비스 제공자는 더 이상 Redis의 소스 코드를 무료로 사용할 수 없습니다. 예를 들어, 클라우드 서비스 제공자는 Redis 코드의 유지 관리자인 Redis와 라이선스 조건에 동의한 후에만 Redis 7.4를 제공할 수 있습니다. 이러한 계약은 기존 통합 솔루션에 대한 지원을 뒷받침하고 향후 Redis 혁신에 대한 전체 액세스를 제공합니다.
fyi; https://redis.io/blog/redis-adopts-dual-source-available-licensing/

결론

  • String은 단순한 구조를 가지므로 값과 만료시간을 한번에 설정할 수 있다.
  • Collection은 내부 구조가 복잡하여 값 설정과 만료시간 설정을 분리하는 것이 구현과 유지보수 측면에서 더 효율적이라고 생각한 것 같다.

References

profile
기록을 통한 성장을

0개의 댓글