Redis源码学习简记(一) sds原理与个人理解

2018/4/7

    从今天开始,记录学习Redis源码的一些笔记,写一些自己的理解与总结。主要以概念和自己觉得重要的东西为主。希望能在一个月内看完。

首先开篇先看redis中sds链表的实现。

数据结构

其数据结构异常简单

typedef char *sds;

就一个字符串指针。

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
//__attribute__ ((__packed__)) 保证了内存不对齐,即每次存储该结构时连续存储,保证后面api能够使用指针,直接访问想要的数据结构。

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};//长度最长为2的5次方 即32个字符  已经被放弃使用。
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[];
};//长度最长为2的8次  即256个字符  下面以次类推
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[];
};

其内存中的存储结构为

uint_*    lenuint_* allocunsigned char flagschar buf [ ]

连续的内存空间用于存储 *根据使用的位数来决定

unsigned char 固定为两个字节 八个bit。

那么拿到一个sds其实是指向的是buf的位置的指针。

由于拥有不同长度的字符串类型需要用flag进行判断,以下为所有type存储在flag的值。利用flag&SDS_TYPE_MASK即可得到type。

#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
#define SDS_TYPE_MASK 7

知道了数据结构后,来看看其重要的一些操作。个人认为最重要的操作其实就是两个,扩容和压缩。

sds扩容

废话不多说,直接上代码。

sds sdsMakeRoomFor(sds s, size_t addlen) { //s 与要增容的长度
    void *sh, *newsh;
    size_t avail = sdsavail(s);//allo  获取sds剩余空间大小
    size_t len, newlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK; //如前面所说的&7 则为类型
    int hdrlen;

    /* Return ASAP if there is enough space left. */
    if (avail >= addlen) return s;  //如果新增的长度,小于剩余空间,则直接返回

    len = sdslen(s);//获取现在长度
    sh = (char*)s-sdsHdrSize(oldtype);  //sh指向len 即指向了最前的位置
    newlen = (len+addlen); //增容后的长度
    if (newlen < SDS_MAX_PREALLOC)  //小于 1024*1024即1M时*2增加, 避免多次增加,因为没增加一次都需要realloc 遍历一遍。
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC; //大于1m 时新的长度增加1m

    type = sdsReqType(newlen);//更新type

    /* 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. */
    if (type == SDS_TYPE_5) type = SDS_TYPE_8; //淘汰了5bit的类型

    hdrlen = sdsHdrSize(type);//获取增容后的头长度
    if (oldtype==type) {
        newsh = s_realloc(sh, hdrlen+newlen+1);//type若没有变化,则表头结构都不需要重新初始化,直接realloc
        if (newsh == NULL) return NULL; //分配失败则返回NULL
        s = (char*)newsh+hdrlen; //新的开始 即newsh+头的长度
    } else {
        /* Since the header size changes, need to move the string forward,
         * and can't use realloc */
        newsh = s_malloc(hdrlen+newlen+1);//新分配长度
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);//拷贝表头后的所有数据
        s_free(sh);//释放原有空间  realloc 会自动做这一步
        s = (char*)newsh+hdrlen;//s调整指向buf数据段
        s[-1] = type;//设置flag
        sdssetlen(s, len);//设置占用的总长度
    }
    sdssetalloc(s, newlen);//设置分配的总空间
    return s;
}

压缩

sds sdsRemoveFreeSpace(sds s) {
    void *sh, *newsh;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;//一样取type
    int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
    size_t len = sdslen(s);//使用的长度
    sh = (char*)s-oldhdrlen;//指向len的指针   |len|alloc|flag|buf|
    /* Check what would be the minimum SDS header that is just good enough to
     * fit this string. */
    type = sdsReqType(len);//符合实际长度的新的type
    hdrlen = sdsHdrSize(type);//新的type

    /* If the type is the same, or at least a large enough type is still
     * required, we just realloc(), letting the allocator to do the copy
     * only if really needed. Otherwise if the change is huge, we manually
     * reallocate the string to use the different header type. */
    if (oldtype==type || type > SDS_TYPE_8) {//若两者一样则直接调用realloc  原type变化不大则只是realloc 将容量缩减,不变更type
        newsh = s_realloc(sh, oldhdrlen+len+1);//重新分配容量
        if (newsh == NULL) return NULL;
        s = (char*)newsh+oldhdrlen;//指向buf
    } else {
        newsh = s_malloc(hdrlen+len+1);//更换type 重新分配空间
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);//拷贝数据
        s_free(sh);
        s = (char*)newsh+hdrlen;//指向新的buf
        s[-1] = type;//设置type
        sdssetlen(s, len);//设置加上头加上数据的总长度
    }
    sdssetalloc(s, len);//设置分配的长度即使用的长度
}
除了两个重要的函数,sds.c还实现了一些字符串的操作函数,个人觉得不是很重要。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值