当前位置: 首页 > 科技观察

Redis套路随笔:前面写的是字符串

时间:2023-03-12 02:13:25 科技观察

Mavericks之前发布了StereotypedEssay背诵系列。很多朋友问我能不能有一篇千篇一律的Essay,把面试题讲透,于是这个系列就这样诞生了。第一期我们就来说说Redis。stringRedis底层是用C语言实现的。所以很多朋友想当然地认为Redis字符串的实现方式和C语言字符串是一样的。但实际上,Redis本身定义了一套字符串实现,称为SDS(simpledynamicstring)。很多同学在面试的时候,面试官都会淡淡的学一个词,说说Redis的SDS。所有人都愣住了,久久无法回答。结果搞了半天,面试官居然问到了Redis的字符串。先回答一个问题:为什么Redis不直接使用C语言的字符串来实现呢?这当然是因为这种数据结构存在先天缺陷。主要有以下缺点1:O(n)复杂度获取长度我们知道C语言是如何判断一个字符串结束的,当然是通过标志'\0'。C语言Str因此,我们要得到字符串的长度,需要从头开始遍历,直到到达\0,时间复杂度就变成了O(n)。缺点二:没有更好的扩展机制。对于C语言来说,如果要创建一个字符串数组,就必须事先确定好字符串的长度。如果字符串需要经常修改,最好说修改前后长度一样。如果不一致,那么程序级就需要重新申请一块新的内存,将字符一个一个复制到新的地方。缺点三:无法处理特殊字符。参考范例《Redis源码剖析与实战》如果我们要存储字符串"redis\0"char*a="redis\0";到原来的C语言,编译器看到\0,认为还是一个字符结束的标志呢,打印的话,只打印redis。所以特别是二进制数据,这种奇怪的情况很多,所以C语言的字符数组不能处理存储二进制字符的需求。为了解决C语言中字符数组的不足,redis提出了一种新的方法。让我们看一下3.0及更早版本的实现。structsdshdr{unsignedintlen;unsignedintfree;charbuf[];}来解释这些字段。len:使用的数组字符串的长度free:数组未使用的字符串的长度buf:存储字符串在以后的版本中,Redis改进了SDS,但大体思路还是一样structsdshdr{unsignedintlen;unsignedintalloc;unsignedcharflags;charbuf[];}让我们解释一下字段。len:使用的数组字符串的长度alloc:数组分配的长度flags:表示SDS类型buf:存储字符串对于SDS类型,我也多说几句。在新版本的redis中,有4种SDS类型(sdshrd5没用过)。sdshrd8sdshrd16sdshrd32sdshrd64的区别仅在于len和alloc。对于sdshrd8,定义是structsdshdr8{uint8_tlen;uint8_talloc;unsignedcharflags;charbuf[];}等等,sdshrd16是structsdshdr16{uint16_tlen;uint16_talloc;unsignedcharflags;charbuf[];}那为什么新版本的Redis有那么多结构?一构法身,一法通达诸法,难道还不够吗?当然,从实现的角度来说,确实是这样。如果只用sdshrd64,肯定够用了。但是从小气的角度来说呢?如果我们的机器很好,内存很小,想省一点就省一点,这样做是有好处的。有什么好处?当然,uint8_t、uint16_t、uint32_t、uint64_t占用的空间是不一样的。对于小字符串,使用小头sdshdr8,这样len和alloc占用的字段也可以节省一点,就是这样。所以可以看出,SDS本质上就是一个C语言的字符数组,再加上一些其他标识属性的结构体。朋友们下次遇到面试官问SDS的时候,不要慌!最后再说两句关于SDS扩充的:对于添加的字符串,如果原来剩余的空间够用,直接返回;如果空间不够,重新申请两倍最小需要的Length空间,然后一一赋值。最后总结一下:Redis提出了动态字符串的数据结构,改善了C语言字符数组的不足。动态字符串有以下优点:获取字符串长度的时间复杂度为O(n)->O(1),减少了字符串扩展带来的数据传输次数。可以存储更复杂的二进制数据参考《Redis源码剖析与实战》https://blog.csdn.net/weixin_39744512/article/details/111170924https://blog.csdn.net/wolf2s/article/details/107945242