01
前言
从今天开始我们就要学习redis的源码了,想想还有点小激动呢。
前方高能预警,非战斗人员迅速撤离。
但是咱怎么能怂呢,撤离啥啊,说干就干,死磕源码,这代码也是人写的,他还能整出什么幺蛾子。
又一个但是来了,redis底层是用C语言写的,如果对C语言一窍不通,那还是算了,前方等待的是一座大山。墙裂推荐去了解一下C.emmmm,幸好我会C,哈哈哈,毕竟他是开启偶代码之路的小哥哥。
好啦,不扯淡了,说说正经事。
02
介绍
Redis没有直接使用C语言传统的字符串来表示(以空字符串结尾的字符数组),而是自己构造了一种名为简单动态字符串SDS。
之前看的String类型的数据结构底层就是用SDS实现的。
SDSHDR(SDS的表头结构如下):
举个栗子:
我们之前设置一个名为str1的字符串,值为redis,其实他在内存上的结构大致如下:
len为5,表示这个sds长度为5个字节。
free为2,表示这个sds还有2个字节未使用的空间。
buf为char[]的数组,分配了(len+1+free)个字节的长度,前len个字节保存redis这5个字符串,接下来1个字节保存了'\0',剩下的free个字节未使用。
03
优点
1.二进制安全。
因为传统的C语言字符串符合ASCII编码,而他的特点是遇零则止,所以当读一个字符串的时候,只要遇到'\0',就认为到达了末尾。这个问题就来了,如果保存的是图片或视频等二进制文件,就会被强行截断,那么数据就不完整了。
那现在不能通过遇零则止来判断是否这个字符串读完了,但是现在可以通过len与buf[]数组的长度比较,如果len+1等于buf的长度,就说明这个字符串读完了。
2.获取字符串长度的操作,其时间复杂度为O(1)。
原来传统的C字符串获得长度的做法是遍历字符串的长度,如果遇零就返回,其时间复杂度为O(n)。
而SDS表头的len成员就保存了字符串的长度,其时间复杂度为O(1)。
3.杜绝缓存区溢出。
因为SDS表头的free成员记录着buf字符数据中未使用的数量,所以,在进行append命令的时候,先判断free是否够用,如果够用,就直接添加字符,如果不够用,就先进行内存扩展,再进行添加字符串。
04
内存分配原则和惰性释放
前面说的内存会扩展,但是呢,不是随便扩展的。就像奥特曼要小怪兽,一个闪光就行,但是要是终极大boss,就要好好打了,注意策略,很有可能会场外求救呢。
如果SDS表头len成员小于1MB(1024X1024),就分配和len长度相同的未使用空间。
如果SDS表头len成员大于等于1MB(1024X1024),就分配1MB的未使用空间。
空说无凭,翠花,上代码。
这边说的是内存扩展,如果新的字符串比旧的字符串要短很多,那么他是不是要回收呢?
先看重置代码,很明显,程序并没有回收多出来的长度,而是使用free来将这些字符串记录起来,等到将来使用。
此上就是SDS的源码,如果要看他具体每个操作的步骤,就要看具体的文件了sds.c和sds.h。
emmmm,偶反正是不行了。
领取专属 10元无门槛券
私享最新 技术干货