晓磊:各位朋友大家好:首次见面 先自我介绍一下,我是晓磊哥,一名工作十年的后端程序员,目前就职于鹅厂。
大路:大家好,我是一名半路出家的野生程序员。可以叫我大路。目前在一家互联网医疗创业公司做全栈。
晓磊:我们做这件事的初衷是,之前很多书,都没有坚持看完。我认为技术书籍区别于其他书籍的点在于:读技术书籍的时候需要不断的思考,遇到问题又不能直接向作者请教,又没有相对集中的地方进行讨论。所以想以一种讨论的方式想和大家一起养成坚持读书的习惯。
大路:那么今天我们读的第一本书是什么呢?
晓磊:今天我们来聊聊《Redis设计与实现》这本书。提到Redis相信很多码农都很熟悉。
Redis是一个key-value存储系统,和Memcache类似,它支持存储的value类型相对更多,包括string、list、set、zset和hash 以及订阅/发布等功能。但这本书并不是介绍怎么使用Redis的,而是对Redis的实现原理进行了介绍,力图展示其核心数据结构以及关键的算法思想,并让我们能够快速、有效的了解Redis的内部构造以及其运作机制。
作者在这里吹了个牛逼,如果只是对Redis实现原理感兴趣,但是又不想深入研究Redis源代码,那么读这本书就够了。
大路:真的是这样吗?
晓磊:关于这个观点其实我不是特别赞同,就拿我举例子,有很多地方光看书就不是很容易理解。
大路:好,废话不多说,直奔主题。redis能存储这么多的value类型, 那它内置了哪些基础的数据结构?
晓磊:这里redis封装了一些数据结构,如:SDS(简单动态字符串), 链表 ,跳跃表,整数集合,对象等。
简单动态字符串SDS的结构:
代码语言:javascript复制struct sdshdr {
// 字节数组,用于保存字符串
char buf[];
//buf数组中已使用字节的数量,等于SDS所保存字符串的长度
int len;
// buf数组中未使用字节的数量, 是0则表示这个SDS分配的空间都被用完了
int free;
}
大路:那len free岂不是就等于buf的length吗?
晓磊:是的~
大路:感觉这个sds和C字符串的功能差不多都是存字符串的嘛。为什么要多此一举封装一个结构体呢?
晓磊:这是因为,如果想在C中获取一个字符串的长度,需要遍历,O(n) 但sds获取字符串长度复杂度 O(1) 。而且,在修改字符串时 ,如果新字符串的长度小于已经申请内存的大小,不需要重新申请内存。这样会减少内存重新分配的次数,避免频繁申请内存。
大路:哇,很讲究嘛。那这么设计还有什么优点?
晓磊:嗯,sds定义了一系列API,对SDS的操作都是通过API来操作的。 比如buf由小变大,sds会自动判断buf大小是否足够,如果不够重新申请内存。这样会避免内存溢出。
大路:也就是说REdis帮我们做了很多管理内存的工作,那它具体是怎么分配和管理内存的呢?
内存分配策略
- 如果大小<1M,则会大方的给我们一份double的内存。
- 如果大小>1M,如5M,则多分配1M即,5 1M。
大路:内存有分配就会有回收,那比如说,当需要的内存由大变小时(如:原大小64M 现在只存2KB),sds会变成4kb大小的内存呢?还是维持原有大小64M呢?
晓磊:这是一个很好的问题。你不妨猜猜看?
大路:我想应该是会变小吧,因为这样可以节省内存空间。
晓磊:嗯,我原来也是这么想的,但其实,咱俩都猜错了。答案是维持原有大小,只是会改变free的值。这样做的好处是,如果内存又变化了,它就不用再去申请了。
大路:哦,这样啊。晓磊哥我还有最后一个问题。就是redis sds字符串最大可以存储多大的字符串?
晓磊:这个问题很有意思。我们可以下期再说。那么,这一期的上读书会就到此为止了,我们下期再见!
大路:下期见~