redis源码阅读笔记(1)——sds

Redis SDS详解
本文深入剖析Redis中的简单动态字符串(SDS)实现原理,探讨其相较于C字符串的优势,并通过代码实践揭示SDS的设计精髓。
最近突然一时兴起,开始看redis的源代码实现。《redis源码阅读笔记》系列将记录下我阅读redis源码的一些笔记和心得。

第一篇我们从最简单的sds(Simple Dynamic String,简单动态字符串)开始啦。
网上已经有一些很好的代码解析的文章了,所以我们可以参考一下

1.高层视角解读
[url=http://www.redisbook.com/en/latest/preview/sds/implementation.html]SDS 的定义[/url]
[url=http://www.redisbook.com/en/latest/preview/sds/different_between_sds_and_c_string.html]SDS 与 C 字符串的区别[/url]

比起C字符串,SDS具有以下优点:
[list]
[*]常数复杂度获取字符串长度。
[*]杜绝缓冲区溢出。
[*]减少修改字符串长度时所需的内存重分配次数。
[*]二进制安全。
[*]兼容部分C字符串函数。
[/list]

2.底层视角解读

接下来就可以阅读源码了(sds.h,sds.c两个文件)。
然后结合这篇文章看,
[url=http://blog.nosqlfan.com/html/2853.html]Redis String类型实现原理[/url]

3.疑难点解惑

Q:为何代码中是int,解释中说long型。原文:“巧合的是两个long型的长度加起来正好是struct sdshdr的长度。”
A:好吧,其实C语言里,32位系统上,int和long是一样的,这两个都是32位,而long long型才是64位的。

sizeoftest.c运行结果

sizeof(int) = 4
sizeof(long) = 4
sizeof(long long) = 8
sizeof(size_t) = 4
sizeof(char *) = 4


Q:size_t什么意思
A:size_t 类型定义在cstddef头文件中,它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。
看一下在stddef.h中的定义

#define __SIZE_TYPE__ long unsigned int


在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的,这样利用该类型可以增强程序的可移植性。换句话说,一个指针可以被安全地放进为size_t类型。

C函数库中strlen的定义

size_t strlen (const char *s)


这样定义意思是返回的数肯定是大于等于0的,所以一般都用size_t这个类型。所以还是习惯,大师都这么用,所以就这么写。

Q:sdslen函数的定义里inline什么意思
A:内联函数用来建议编译器将指定的函数代码插入并取代每一处调用该函数的地方,从而节省了调用函数带来的额外开销。类似于写程序时将同样的代码复制粘贴到多处。好处是减少执行时间,坏处就是增加了重复代码占用的空间。

Q:static什么意思
A:static意味着该函数只在这个文件中可见,还意味着可以在其他文件中定义同名函数。

Q:为什么static inline一起用
A:这个我查了几篇文章,解释的比较复杂,看不大懂。简单的说,这个成了C99里的一个习惯了吧,大家都这么写,你就这么写吧,而且static inline函数一般都是定义在头文件(h)中。这样包含这个h文件的c文件都将拥有这个函数定义。

Q:数据空间的char buf[]什么意思,能改成char* buf吗?
A:有区别。这个比较复杂了,char buf[]是flexible array member,是一个编程技巧,[url]http://c-faq.com/struct/structhack.html[/url]
不是一句话能讲明白的。请耐心看完下面的解释。

c99之 柔性数组成员 flexible array member
请参考这篇文章,我不重复复制了。
[url=http://blog.youkuaiyun.com/zhangboyj/article/details/6232168]c99之 柔性数组成员[/url]

我做了一个试验,将原来的代码片段改用char* buf的形式重写了。
比较一下和原来的不同
sds1test.c

memcpy(sh->buf, str1, strlen(str1));
sh->buf[initlen] = '\0';


sds2test.c

strcpy(sh + 1, str1);
sh->buf = sh + 1;


sds1test.c运行结果

sizeof(struct sdshdr) = 8
strlen(str2) = 5
address of sh = 537335688
address of str2 = 537335696
str2 - sh = 8
sdslen = 5


sds2test.c运行结果

sizeof(struct sdshdr) = 12
strlen(str2) = 5
address of sh = 537335688
address of str2 = 537335700
str2 - sh = 12
sdslen = 5


可以看到改成char* buf的形式后,struct体占用空间多了4个字节,而且给buf的赋值要用sh->buf = sh + 1这种古怪的形式,不方便。
这个技巧用的真是令人拍案叫绝哪!

[img]http://dl2.iteye.com/upload/attachment/0098/8065/fdb954da-0fad-30a7-8d05-1c5ad61e257b.jpg[/img]
上图左边是原来redis的实现,struct体只占用8个字节,右边是改成char* buf的形式后,struct体占用12个字节。
到此,相信读者对“巧合的是两个long型的长度加起来正好是struct sdshdr的长度。” 这句话就可以理解了。

最后看一下这个网页的解释,可以看到这是一个经常用到的技巧。
[url=http://c-faq.com/struct/structhack.html]comp.lang.c FAQ list · Question 2.6[/url]

附件是我的测试代码,可以下载下来运行,细细体会flexible array member的精妙。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值