ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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[];  // 실제 문자열 데이터를 저장하는 버퍼
    };
    1. len: 현재 문자열의 길이, 이는 strlen 함수를 호출하지 않고도 문자열의 길이를 빠르게 알 수 있게 함
    2. free: 현재 할당된 버퍼 중 사용되지 않은 공간의 길이. 이를 통해 문자열을 확장할 때 메모리 재할당을 최소화
    3. 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

     

Designed by Tistory.