您的位置:首页 > 数据库 > Redis

redis数据类型设计和实现(之一)字符串类型

2016-05-17 18:19 597 查看
字符串类型

最近一直在研究redis的数据类型的底层实现,大神推荐的书《redis设计与实现》。从今天起一点一点分解本书所讲解的知识并同步博客来记录学习过程以及当做将来的复习资料,同时也希望和有相同兴趣的同学一起学习共同进步。由于个人水平有限,还望看过朋友不吝赐教指正文中理解不足或是错误的地方,本人不胜感激。
今天先从redis最简单的数据类型说起--字符串类型,好了废话不多说。

一、[b]SDS(简单动态字符串)[/b]

下图添加了一组键值对其中键名为"test" 值为"redis"。



存储成功后,我们看到“test”和“redis”都是一个字符串,那么问题就来了,redis是怎样存储键值对的呢?

实际上,在redis的存储中,上述的键“test”和值“redis”都是使用的redis的字符串类型进行存储。但redis的字符串类型底层实现并没有直接使用C语言中的字符串类型,而使用的是自己构建的一套名为简单动态字符串抽象类型,简称为:SDS。

也就是说:键"test"是使用一个SDS类型进行存储;值“redis”也是使用的一个SDS进行存储。

二、SDS的组成

SDS由3个字段组组成分别是:len、free、buf,伪代码表示,



len:int类型,用来记录字段buf数组已经使用的字节的数量,也就是SDS的字符串的实际长度。

free:int类型,用来记录字段buf数组未使用的的字节的数量。

buf:字节数组,用来保存字符串。

三、SDS与C字符串类型区别和优点

A.储存格式不同。传统中,C字符串类型使用长度为N+1的字符数组来保存长度为N的字符串,而且字符数组的最后一个元素总是一个空字符'\0'(这种字符串的存储方式在很多方面限制了C字符串类型在redis中广泛的使用)。虽然在SDS中字符串存储在属性字段buf的字节数组中字符串结尾处也是用一个空字符串'\0'来表示,但是C中'\0'表示字符串的结束结尾没有任何可用空间,而SDS中'\0'只是字符串伪结尾不表示字符串的结束,空字符'\0'后面还可能有可用的空间。如果有可用空间,属性字段free记录可用空间的长度。优点:SDS能够兼容一些C字符串函数,不用SDS再重新写新的API函数。

B.获取字符串长度方式。C字符串类型获取字符串长度则要从字符串的开始位置计算直到count到结尾元素'\0',复杂度为O(N);SDS获取字符串的长度只需要读取len属性字段值即可,复杂度为O(1)。优点:做出对比就可以知道两种字符串获取字符串长度的效率是不一样的,SDS明显优于C字符串。


C字符串类型计算长度


SDS直接读取len获取长度

C.追加/缩短字符串方法不同。C字符串类型在append新字符串时则要重新进行内存重分配,扩展字符串类型空间并分配恰好的空间来存储总字符串。SDS在append新字符串是首先需要判断buf属性字段的剩余空间是否足够,如果足够,那就将新字符串放入剩余空间,否则也进行内存重分配扩展buf属性字段空间并分配大于总符字符长度的空间(总字符长度:原有字符串长度+append字符创长度,即修改后的字符串长度,这种方式叫做空间预分配)。同样的道理缩短字符串,C字符串也要进行内存重分配将多余的字节返还;SDS则不需要内存重分配,而是将缩短后多余的字节保留在buf属性字段中并在free属性字段中做长度的记录,以便SDS进行下一次append时进行使用。优点:1.防止缓存溢出。C字符串在增长字符串长度的时候,如果忘记内存重分配,则会导致新的字符串值覆盖原有的字符串出现的缓存溢出;SDS则不会出现这种情况。首先,SDS在做增长字符串的时候会判断buf属性字段的空间是充足,如果不足,则扩展空间,充足直接存放。这些操作都是redis的API自动完成,不需要人工进行干预。2.减少内存重分配次数。C字段的变动肯定引起内存重分配,N次变动必须执行N次重分配;SDS则只需要最多N次的重分配。

D.二进制安全。C字符串类型的结尾'\0'元素,限制了C存储二进制数据的安全性。如果如果某种二进制数据中有'\0'空字符,则C会认为这是字符串的结束,从而导致出现数据的破坏。SDS则没有这种限制,因为SDS读取数据的结尾是靠len字段大小确定的。二进制数据放进来是什么样,出去的时候还是什么样的,SDS就当不认识空字符'\0',当然就不会破坏数据。优点更明显了:SDS存储二进制数据更安全。

四、SDS的空间预分配策略

空间预分配这种方式是为了优化SDS字符串增长的操作。SDS的API在增长字符串判断需要增加空间时,程序不仅会分配修改所需要的空间,还会分配一些额外的未使用的空间,额外的未使用的空间的大小是根据以下公式确定的:

1.当完成修改后SDS空间小于1M是,即len的数量小于1M,程序会分配额和len数量同样大的未使用的空间。比如:一个SDS原来有10个字节的字符串,要在增加5个字节长度的字符串,SDS需要进行扩展,扩展后的SDS空间的大小为:15+15+1=31个字节。已使用空间大小15个字节小于1M,再加上分配的未使用的15个字节的空间,再加上1个字节的结尾空字符'\0'占用空间。

2.当修改完成后SDS空间大于等于1M,即len的数量大于等于1M,程序只分配1M的未使用空间。此举在于节省内存空间,因为如果存在大量的超过1M的SDS,都为其分配相等的未使用空间,内存浪费是相当严重的。

总结:redis使用了自己构建的SDS类型来存字符串,SDS相较于C字符串优势在于:高效率,更安全,功能更强大。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: