>데이터 베이스 >Redis >redis 연구 노트-문자열 원리

redis 연구 노트-문자열 원리

Golang菜鸟
Golang菜鸟앞으로
2023-08-08 16:19:281610검색

String은 Redis의 가장 기본적인 데이터 유형입니다. 모든 키가 문자열 유형일 뿐만 아니라 다른 여러 데이터 유형으로 구성된 요소도 문자열입니다. 문자열의 길이는 512M를 초과할 수 없습니다.

우선 512M를 초과할 수 없다고 누가 규정했나요? 아니면 왜 512M을 초과하지 않습니까?

// 源码定义(检查字符串长度)
static int checkStringLength(redisClient *c, long long size) {
    if (size > 512*1024*1024) {
        addReplyError(c,"string exceeds maximum allowed size (512MB)");
        return REDIS_ERR;
    }
    return REDIS_OK;
}

소스 코드 확인으로 수정되었으며 512M을 초과할 수 없습니다.

redis 문자열 구조를 살펴보겠습니다:

struct sdshdr{
    // 记录 buf 数组中已使用字节的数量
    // 等于 SDS 所保存字符串的长度
    int len;
    // 记录 buf 数组中未使用字节的数量
    int free;
    // 字节数组,用于保存字符串
    char buf[];
}

int가 32비트이므로 최대 4G 문자열을 지원할 수 있어야 함을 바로 알 수 있지만 실제 상황은 그렇지 않습니다. .

512M을 초과할 수 없는 이유를 알아보기 위해 공식적인 답변을 찾았습니다.

redis 연구 노트-문자열 원리

그런 다음 제가 읽은 Redis 정보가 최신이 아니라는 것을 발견했습니다!

redis 연구 노트-문자열 원리

보세요, 다른 사람도 속았습니다. 이 토론에서 논의된 버전은 모두 3.2 이전 버전입니다.

话不多说,继续学习 redis5.0 版本的资料。不过之前学习了的也没事,我们可以一起来看下 redis 的字符串是怎么优化的。

用如下结构来存储长度小于32的短字符串:

struct __attribute__((__packed__)) sdshdr5 {
        unsigned char flags; /* 低3位存储类型,高5位存储长度*/
        char buf[]; /* 柔性数组,存放实际内容*/
}

sdshdr5 结构中,flags占1个字节,其低3位(bit)表示type,高5位(bit)表示长度,能表示的长度区间为0~31(25-1), flags后面就是字符串的内容。

而对于长度大于31的字符串,这个结构就不够用了,所以对于不同长度的字符串,有不同的处理方式:

#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4

struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

可以看到,这4种结构的成员变量类似,唯一的区别是len和alloc的类型不同。

结构体中4个字段的具体含义分别如下:

1)len:表示buf中已占用字节数。

2)alloc:表示buf中已分配字节数,不同于free,记录的是为buf分配的总长度。

3)flags:标识当前结构体的类型,低3位用作标识位,高5位预留。

4) buf: 유연한 배열, 실제로 문자열을 저장하는 데이터 공간입니다.

문자열 생성 과정:

Redis는 sdsnewlen 함수를 통해 SDS를 생성합니다. 함수에서는 문자열의 길이에 따라 적절한 유형이 선택됩니다. 해당 통계 값을 초기화한 후 문자열의 내용에 대한 포인터가 반환되고 문자열의 길이에 따라 다른 유형이 선택됩니다. .

sdshdr5 유형의 경우 빈 문자열을 생성하면 sdshdr8로 캐스팅됩니다. 이유는 빈 문자열을 생성한 후 해당 내용이 자주 업데이트되어 확장이 발생할 수 있기 때문에 생성 시 바로 sdshdr8로 생성되기 때문일 수 있습니다.

문자열 접합:

sdscatsds는 상위 레이어에 노출되는 메소드이며, 최종적으로 sdscatlen을 호출합니다. SDS의 확장이 포함될 수 있으므로 sdscatlen에서 sdsMakeRoomFor가 호출되어 접합된 문자열 s의 용량을 확인합니다. 확장이 필요하지 않으면 s가 직접 반환되고, 확장된 새 문자열 s가 반환됩니다. 함수의 len 및 curlen과 같은 길이 값에는 종료자가 포함되어 있지 않습니다. 스플라이싱 시 memcpy를 사용하여 두 문자열을 함께 스플라이싱하고 해당 길이를 지정하므로 이 프로세스는 바이너리 보안을 보장합니다. 끝에 종결자를 추가해야 합니다.

문자열 확장

  1. sds에서 사용할 수 있는 남은 여유 길이가 새 콘텐츠 추가 길이보다 길면 확장하지 않고 유연한 배열 버퍼의 끝에 직접 추가하세요.

  2. SD에 남은 무료 길이가 새 콘텐츠 추가 길이보다 작거나 같은 경우 사례별로 논의하겠습니다. 추가 후 총 길이가 len+인 경우 addlen1MB인 경우 새 길이에 1MB를 추가하여 용량이 확장됩니다.

  3. 마지막으로 새로운 길이에 따라 저장 유형을 다시 선택하고 공간을 할당합니다. 여기서 유형을 변경할 필요가 없으면 realloc을 통해 유연한 배열을 확장하면 됩니다. 그렇지 않으면 메모리를 다시 열고 원래 문자열의 buf 콘텐츠를 새 위치로 이동해야 합니다.

문자열은 이 정도입니다.

버전 5.0에서는 512M의 문자열 제한이 없습니다. 문자열 처리 방법은 유형에 따라 다르므로 더 많은 메모리가 절약됩니다.

위 내용은 redis 연구 노트-문자열 원리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Golang菜鸟에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제