-
[redis] redis는 데이터 타입을 어떻게 처리하나 (redisObject, SDS)공부 이야기/REDIS 2024. 6. 4. 18:21
Redis에 저장된 모든 데이터는 redisObject로 통일된 관리
redisObject는 Redis가 데이터의 타입, 인코딩 방식, 참조 카운트 및 기타 메타데이터를 관리하기 위해 사용하는 내부 구조체
이를 통해 Redis는 다양한 데이터 타입을 효율적으로 다루고, 메모리 관리를 최적화
1) redisObject의 구성 요소
`redisObject`는 다음과 같은 주요 필드로 구성
typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; void *ptr; } robj;
1. type: 객체의 데이터 타입. Redis는 여러 가지 데이터 타입을 지원하며, 주요 타입은 다음과 같습니다:
- `REDIS_STRING`: 문자열 객체
- `REDIS_LIST`: 리스트 객체
- `REDIS_SET`: 집합 객체
- `REDIS_ZSET`: 정렬된 집합 객체
- `REDIS_HASH`: 해시 객체
2. encoding: 객체의 인코딩 방식. Redis는 데이터 저장 공간과 접근 속도를 최적화하기 위해 다양한 인코딩 방식을 사용
- `RAW`: 일반적인 메모리 구조
- `INT`: 정수 저장
- `HT`: 해시 테이블
- `ZIPMAP`: 압축된 해시 테이블
- `LINKEDLIST`: 연결 리스트
- `ZIPLIST`: 압축된 리스트
- `INTSET`: 정수 집합
- `SKIPLIST`: 스킵 리스트
3. lru: 객체의 마지막 사용 시간. 이 필드는 LRU(Least Recently Used) 알고리즘을 구현하기 위해 사용. Redis의 메모리 관리 정책 중 일부는 LRU를 기반으로 하므로, 이 필드는 객체의 사용 빈도를 추적
4. refcount: 객체의 참조 카운트.이 필드는 객체가 몇 개의 다른 객체나 클라이언트에 의해 참조되고 있는지를 추적. 참조 카운트가 0이 되면 객체는 메모리에서 해제
5. ptr: 실제 데이터를 가리키는 포인터. 데이터의 실제 값이나 구조체는 `ptr` 필드에 의해 참조2) redisObject의 역할
1. 메모리 관리: `redisObject`는 메모리 관리의 핵심적인 역할을 합니다. 참조 카운트를 통해 객체가 더 이상 사용되지 않으면 메모리에서 해제할 수 있습니다.2. 데이터 접근: `redisObject`는 다양한 데이터 타입과 인코딩 방식을 지원하여 효율적인 데이터 접근을 가능하게 합니다.
3. LRU 구현: LRU 필드를 통해 객체의 사용 빈도를 추적하고, 메모리 관리 정책에 따라 오래 사용되지 않은 객체를 제거할 수 있습니다.
3) SDS (Simple Dynamic String)
문자열의 경우 내부적으로 SDS (Simple Dynamic String) 를 사용하여 효율적으로 처리
redisObject의 *ptr이 SDS를 가리킴
SDS는 C 언어의 기본 문자열 타입인 char *를 대체하기 위해 설계되었으며, 여러 가지 장점을 제공. SDS는 동적 문자열 관리, 메모리 효율성, 안전성 등에서 C 문자열보다 우수함. (Redis는 C언어 기반)
struct sdshdr { int len; // 문자열의 길이 int free; // 할당된 버퍼 중 사용되지 않은 공간의 길이 char buf[]; // 실제 문자열 데이터를 저장하는 버퍼 };
- len: 현재 문자열의 길이, 이는 strlen 함수를 호출하지 않고도 문자열의 길이를 빠르게 알 수 있게 함
- free: 현재 할당된 버퍼 중 사용되지 않은 공간의 길이. 이를 통해 문자열을 확장할 때 메모리 재할당을 최소화
- buf: 실제 문자열 데이터를 저장하는 버퍼. 맨 마지막엔 Null(1byte)로 끝남. 이 버퍼는 NULL로 종료되는 C 문자열과 호환
- free는 최초에 SET 키 값 명령어를 수행했을 때는 0이지만 SET APPEND 명령어를 수행했을 때 len 만큼 할당한다.
- debug SDSLEN 키 명령어로 수행 과정을 확인할 수 있다.
4) SDS의 특징
1. 동적 확장: SDS는 문자열의 길이가 변경될 때 자동으로 버퍼 크기를 조정합니다. 문자열을 추가하거나 삭제할 때, 필요에 따라 메모리를 할당하거나 해제합니다.
2. 메모리 효율성: free 필드를 통해 미리 할당된 여유 공간을 관리함으로써, 빈번한 메모리 재할당을 피할 수 있습니다. 이는 성능을 향상시키고 메모리 단편화를 줄입니다.
3. 안전성: len 필드를 사용하여 문자열의 길이를 추적하기 때문에, C 문자열에서 흔히 발생하는 버퍼 오버플로우 문제를 방지할 수 있습니다.
4. 바이너리 안전성: SDS는 NULL 문자를 포함할 수 있는 바이너리 데이터를 저장할 수 있습니다. 이는 바이너리 데이터를 처리할 때 유용합니다.
5. 호환성: buf 필드는 NULL로 종료되는 C 문자열과 호환되므로, 기존 C 문자열 함수와 함께 사용할 수 있습니다.
5) EMBSTR(Embedded String)
Redis의 `EMBSTR`(Embedded String)은 작은 크기의 문자열을 효율적으로 저장하기 위한 특수한 인코딩 방식으로 메모리 할당 및 관리 비용을 줄이고, 성능을 최적화하기 위해 사용. 이 인코딩 방식은 작은 문자열(44byte 이하)을 저장하고 검색하는 데 특히 유리
6) `EMBSTR`의 특징1. 단일 메모리 할당: `EMBSTR` 인코딩 방식은 `redisObject` 구조체와 `SDS`(Simple Dynamic String) 문자열 데이터를 하나의 메모리 블록에 CPU 캐시 영역에 함께 연속으로 저장. 이는 메모리 할당 및 해제 시 한 번만 수행할 수 있기 때문에 GET, SET 오버헤드를 줄임
2. 작은 문자열에 특화: `EMBSTR` 인코딩은 일반적으로 길이가 44바이트 이하인 문자열에 사용됩니다. 문자열의 길이가 이 제한을 초과하면 Redis는 `RAW` 인코딩을 사용하여 별도로 메모리를 할당
3. 읽기 성능 최적화: `EMBSTR` 방식은 데이터 접근 시 캐시 효율성을 높이며, 메모리 단편화를 줄임. 이는 작은 문자열에 대해 더 빠른 읽기 성능을 제공
4. 불변성: `EMBSTR`로 인코딩된 문자열은 불변(immutable). 문자열을 수정하려고 하면 Redis는 새로운 메모리 블록을 할당하고, 기존 데이터를 `RAW` 인코딩 방식으로 복사.
7) `EMBSTR`과 `RAW`의 차이점- EMBSTR: 작은 문자열(보통 44바이트 이하)에 사용되며, `redisObject`와 `SDS`가 하나의 메모리 블록에 저장. 메모리 할당 및 해제 시 오버헤드가 적고, 캐시 효율성이 높음
- RAW: 더 큰 문자열에 사용되며, `redisObject`와 `SDS`가 별도의 메모리 블록에 저장됩니다. 문자열 수정 시 더 적은 오버헤드가 발생
- EMBSTR은 Immutable 객체이기 때문에 수정이 불가능. 메모리를 재 할당해서 값을 복사하는 과정이 발생하기 때문에 수정 오버헤드가 RAW에 비해 한 번 더 발생함.
- 클라이언트(개발자)는 sds 객체를 직접 선언해서 low level function을 통해 좀 더 세밀한 데이터 제어를 할 수 있음
참고 : http://redisgate.kr/redis/configuration/internal_string.php
Redis STRINGS Data Structure
redisgate.kr
'공부 이야기 > REDIS' 카테고리의 다른 글
[Redis] 데이터 타입 - Stream (0) 2024.06.06 [Redis] Keys * vs Scan (0) 2024.06.05 [redis] 데이터 타입 - HLL (0) 2024.06.04 [redis] 데이터 타입 - Strings (0) 2024.06.04 redis-cli info 명령어로 redis 서버 정보 확인하기 (0) 2024.03.22