第44题: Redis 的String 是怎么实现的?为什么不直接用c的字符串?
Redis 没有直接使用C 语言的字符串表示,而是自己构建一种简单动态字符串(SDS: simple dynamic string)。
Redis 中的String 都是SDS,例如我们执行这样一条命令:
redis> SET blog angela
那么Redis 在内存中创建了一个键值对,
-
键:键是一个字符串对象,对应底层实现是一个保存字符串“blog” 的SDS; -
值:值也是一个字符串对象,对应底层实现是一个保存字符串是“angela”的SDS。
如果执行
redis> RPUSH blog "angela" "de" "blog"
那么Redis 将在内存中创建一个键值对,键还是一个SDS,存放“blog”,值是一个列表,存放三个SDS,“angela”、“de”、“blog”
SDS如何实现的呢?
首先SDS被定义为这样一个结构体:
struct sdshdr {
char buf[];//字节数组,用于保存字符串
int len;//记录buf数组中已使用字节的数量
int free;//记录buf数组中未使用字节的数量
}
举个例子:
现有执行redis> SET blog angela
命令后,value值在redis 中存放如下图所示,buf存放字节数组(真实数据),len表示存放字符串的长度(不包括结束符号’’),free表示剩余空间,可以看到还剩5个字节。
SDS遵循了C语言字符串以空字符结尾的惯例,保存空字符的1个字节空间不在SDS 的len属性里面。这样做有个好处是我们直接可以复用C 提供的库函数,比如打印字符串,直接用C 提供的printf("%s", s->buf)
;
那又是为什么不直接用C语言提供的字符串呢?
在C语言中,表示一个“angela” 的字符串如下图所示,结束也是通过空字符表示:
C语言的字符串和SDS的区别如下:
-
长度获取效率:C语言如果需要获取字符串长度,需要从头开始遍历,时间复杂度O(n),SDS提供len属性,访问时间复杂度O(1), Redis的strlen 获取字符串长度函数性能毫无压力;
-
杜绝溢出:C字符串不记录自身长度的另一个问题是容易造成缓冲区溢出,比如要做字符串拼接,
char *strcat(char *dest, const char *src)
, 讲src 字符串拼到desc字符串,如果desc忘记提前扩容,就会溢出,而恰好desc后面正好有数据,则会被覆盖; -
减少内存重分配:对于Redis这种内存数据库,很多场景会频繁更新value的值,那如果采用C字符串,value长度有变化,就需要频繁分配内存,SDS做了内存的预分配和惰性释放,能有效减少内存分配次数,内存的重分配代价还是很高昂的,对于Redis这种对性能要求非常高的缓存产品,这种性能损耗是不能接受的;
-
二进制安全:C字符串必须符合某种编码(比如ASCII),除了空字符结尾,字符串中间是不能包含空字符的,否则会被误以为是字符串结尾。这就限定了C字符串不能用来存储类型图片、音频、视频、压缩文件这种二进制书数据,但是SDS可以,SDS设计的API都是二进制安全的。
对Redis感兴趣的可以加我微信guofu-angela, 把你拉到Redis群,一起探讨缓存的问题。
今天文章有点短,最近发现有个蛋疼的事情,有好几个读者私我,让我帮忙看面试进度,他大爷的,内推的想不起来找我,快拿到offer了找我看进度。
开个玩笑,希望玩家们都能拿到心仪的offer,你们可以在公众号后台许愿。
所以还是要强调内推记得找我,我会认真写面试推荐。
另外在招22届毕业实习生,有22届毕业的同学帮忙推荐给我,把我这篇文章或者微信(guofu-angela)发给他/她。
大家如果不想漏看,可以星标一下公众号。
原文始发于微信公众号(安琪拉的博客):面试题解-Redis的String是如何实现的?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/25241.html