Redis -底层(1)深入浅出SDS

Redis底层是靠C语言实现的,想看源码实现直接看src下的代码即可

.h文件和.c文件作用
xx.h文件 .h中一般放的是同名.c文件中定义的变量、数组、函数的声明,需要让.c外部使用的声明。
xx.c文件 .c文件一般放的是变量、数组、函数的具体定义

核心就是server.h,redisDb以及redisObject就是在server.h中。

typedef struct redisDb {
    dict *dict;                 /* k-v结构就存在这里边 */
    dict *expires;              /* 过期时间*/
    dict *blocking_keys;        /* 阻塞队列的一些处理*/
    dict *ready_keys;           /* 对应的client连接 */
    dict *watched_keys;         /* 事物处理 */
    int id;                     /* 索引ID */
    long long avg_ttl;          /* Average TTL, just for stats */
    unsigned long expires_cursor; /* Cursor of the active expire cycle. */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
typedef struct redisObject {
    /* 4bits 表示对象的类型,包括5种基本类型 */
    unsigned type:4;
    /* 4bits  表示值对象的内部编码 */
    unsigned encoding:4;
    /* 24bits  记录对象被命令访问的最后一次时间,和内存回收有很大关系 */
    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). */
    /* 4bytes = 32bits 记录被引用的次数 */
    int refcount;
    /* 8bytes,64-bit system  指向真正内容的指针 */
    void *ptr;
} robj;

学过C的都晓得,C语言存储字符串,是 char data[] 数组形式。
eg: char data[]=“guojia\0”;
\0表示结束
如果有一个字符串guo\0jia,正常结果应该是guojia,但是由于有换行符,就有可能变为guo.这就有可能导致数据不准确,所以redis自定义了字符串:

SDS :simple dynamic string

一、特点:

  • 1、二进制安全的数据结构
  • 2、提供了内存预分配,避免了频繁的内存分配
  • 3、兼容C语言函数库

二、扩容方式:

二倍扩容(空间换时间)

sds:
free:0 空余空间为0
len:6 使用空间为6
char buf[]=“huohuo” ->“huohuo123”


修改后的结果为:huohuo123,需使用9个空间

len:6
addlen:3
(len+addlen)*2=18个字节


free:9
len:9
char buf[]=“huohuo123”


由于剩余空间还为9,再次修改,空间够用不会扩容,空间不够将会再次2倍方式扩容。
什么场景下不会采用二倍扩容方式?
当len长度达到1M的时候,也就是1024*1024的长度
扩容源码位于sds.h中

sds sdscatlen(sds s, const void *t, size_t len) {
    /* 原字符串长度 */
    size_t curlen = sdslen(s);  
    /* 按需调整空间,如果 capacity 不够容纳追加的内容,就会重新分配字节数组并复制原字符串的内容到新数组中 */
    s = sdsMakeRoomFor(s,len);
    /* 内存不足 */
    if (s == NULL) return NULL; 
    /* 追加目标字符串的内容到字节数组中 */
    memcpy(s+curlen, t, len); 
    /* 设置追加后的长度值 */
    sdssetlen(s, curlen+len); 
    /* 让字符串以\0 结尾,便于调试打印 */
    s[curlen+len] = '\0'; 
    return s;
}
sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    size_t avail = sdsavail(s);
    size_t len, newlen;
    /* 首先计算出原SDS还剩多少可分配空间 */
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;

    /* Return ASAP if there is enough space left. */
    /* 已经够用的情况下直接返回 */
    if (avail >= addlen) return s;

    len = sdslen(s);

    sh = (char*)s-sdsHdrSize(oldtype);
    /*扩容分配策略 */
    newlen = (len+addlen);

    assert(newlen > len);   /* Catch size_t overflow */
    /* 如果新长度小于最大预分配长度则分配扩容为2倍 */
    /* 如果新长度大于最大预分配长度则仅追加SDS_MAX_PREALLOC长度 */
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;

    type = sdsReqType(newlen);

    /* Don't use type 5: the user is appending to the string and type 5 is
     * not able to remember empty space, so sdsMakeRoomFor() must be called
     * at every appending operation. */
    /* 由于SDS_TYPE_5没有记录剩余空间(用多少分配多少),所以是不合适用来进行追加的 */
    /* 为了防止下次追加出现这种情况,所以直接分配SDS_TYPE_8类型 */
    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

    hdrlen = sdsHdrSize(type);
    assert(hdrlen + newlen + 1 > len);  /* Catch size_t overflow */
    if (oldtype==type) {
        /* 类型没变化则直接使用原起始地址重新分配下内存即可 */
        newsh = s_realloc(sh, hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+hdrlen;
    } else {
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
        /* 头部类型有变化则重新开辟一块内存并将原先整个SDS拷贝一份过去 */
        newsh = s_malloc(hdrlen+newlen+1);
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
        /* 旧的已经没用了 */
        s_free(sh);
        s = (char*)newsh+hdrlen;
        /* 配置新类型 */
        s[-1] = type;
        sdssetlen(s, len);
    }
    /* 设置新对分配对总长度 */
    sdssetalloc(s, newlen);
    return s;
}

SDS_MAX_PREALLOC的容量大小定义在sds.h文件中,默认是 1024 * 1024,也就是1MB。
在这里插入图片描述

三、

三、reidsDb有多少DB?

0-15,一共16个redisDb

四、C底层关系图解

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值