Redis를 사용하다 보면 String 타입은 값 설정(SET)과 동시에 만료시간을 지정할 수 있는 반면, Collection 타입들은 값 설정(HSET, SADD 등)과 만료시간 설정(EXPIRE)을 따로 해야 한다는 것을 알 수 있다. 이러한 차이가 발생하는 이유를 Redis Expire 를 어떻게 관리할까?에서 살펴본 Redis의 내부 자료구조를 통해 살펴보자.
Redis의 데이터베이스는 redisDb
구조체로 표현되며, 두 개의 핵심 딕셔너리를 가지고 있다.
struct redisDb {
dict *dict; // 실제 키-값 데이터를 저장하는 딕셔너리
dict *expires; // 만료 시간 정보를 저장하는 별도의 딕셔너리
// ... 다른 필드들
};
String은 Redis의 가장 기본적인 데이터 타입으로, 단순한 키-값 구조를 가진다.
SET key value [EX seconds]
SET 명령어를 실행하면,
1. redisDb->dict
에 키-값 쌍이 단일 dictEntry
로 저장된다.
2. EX 옵션이 있다면 동시에 redisDb->expires
에 만료시간이 저장된다.
이러한 단순한 구조 덕분에 값 설정과 만료시간 설정을 atomic하게 수행할 수 있다.
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들의 연결리스트) 구조
// 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);
}
// 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);
/* 여기서 만료시간도 함께 설정하려면:
* - 인코딩 변환 이전에 설정? 이후에 설정?
* - 해시 크기가 변경되어 인코딩이 바뀌면?
* - 기존 만료시간이 있었다면?
*/
}
Redis 7.4 버전에서는 Hash 자료구조에 대해 필드별 만료시간 설정이 가능한 HEXPIRE
명령어가 추가되었다. 이전 버전과의 차이점이 생겼길래 필드별 만료시간 설정이 가능해졌을까?
물론 필드가 아닌 키 설정에서 만료시간을 함께 설정할 수는 없다.
이전 Redis 버전에서는 키 단위로만 만료시간 설정이 가능하여, Hash의 내부 구조는 단순히 field-value 쌍만을 저장했다.
struct hash {
dict *dict; // field-value 쌍을 저장하는 해시 테이블
}
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/