Redis底层剖析

Redis提供了五种数据结构:字符串、链表、集合、哈希表、有序集合这五种结构。
说一下简单实现


1. 字符串类型 string
存储:set	获取:get	删除:del
2. 哈希类型 hash:map格式
存储:hset key field value//获取指定的field对应的值
获取:hget key field 
		 hgetall key
删除:hdel key field
3. 列表类型 list:	linkedlist格式
可以添加一个元素到列表的头部(左边)或尾部(右边)
添加:lpush key value:将元素加入列表左边
	 rpush key value:将元素加入列表右边
获取:lrange key start end:范围获取(0 -1)获取所有
删除:lpop key:从列表的左边删除一个元素 
4. 集合类型	set(不允许重复、不保证顺序)
存储:sadd key value
获取:smembers key:获取所有元素
删除:srem key value:删除某个元素
5. 有序集合类型 sortedset(不允许重复、保证顺序)
存储:zadd key score value:按照分数进行排序
获取:zrange key start end[withscores]获取分数
删除:zrem key value 

进入正题:
一、简单动态字符串
Redis底层使用C语言编写,所以大多数人都会以为Redis的字符串结构就是直接使用了C语言的字符串,其实不然,Redis自定义了一个简单动态字符串结构,作为Redis中字符串的实现。动态字符串结里定义了几个属性:len(int),free(int),buf(char[ ]),其中len用来表示数组中已使用字节的数量,free用来表示数组中未使用字节的数量,buf数组用来保存字符串。动态字符串遵循C语言字符串中以空字符’\0’结尾的特征,这样的好处是可以重用一部分C语言自带字符串函数。
动态字符串的好处:

  1. 可以常数复杂度获取字符串长度
    因为简单动态字符串保存了len属性,我们可以通过len属性直接获取字符串长度,而C语言的字符串则需要遍历字符串数组才能获取字符串长度
  2. 防止缓冲区溢出
    C语言字符串的另一个问题是容易造成缓冲区溢出。假如我们对字符串str执行strcat字符串拼接操作,如果str没有分配足够的内存,就会产生缓冲区溢出问题。如果str和str2在内存中相邻,当我们为str执行strcat之前没有为str分配足够的空间,那么strcat执行后,str的数据将会溢出到str2的空间中,导致str2保存的内容被意外更改。而简单动态字符串杜绝了这一问题,当我们需要对简单动态字符串进行修改时,api会检查空间是否满足修改所需的需求,如果不满足,api会自动将字符串扩容到需要的大小,再执行修改操作。
  3. 减少修改字符串导致内存重分配的次数
    对于C语言字符串来说,每次增长或缩短字符串,程序都需要对这个字符串数据进行内存重分配,因为内存重分配相对来说较复杂且耗时,所以Redis通过未使用空间free解除了每次修改字符串都会内存重分配的问题。简单动态字符串实现了空间预分配和惰性空间释放的策略。
    空间预分配:当对一个字符串进行修改需要扩展空间的时候,程序不仅为字符串分配修改需要的空间,还会分配额外的未使用空间,使用free记录未使用空间。通过空间预分配策略,可以减少连续执行字符串增长所需的内存重分配次数。
    惰性空间释放:当一个字符串需要缩短空间的时候,使用free记录需要缩短的空间,等待将来使用,而不是直接执行内存重分配,需要缩短的空间记录在free里面。动态字符串提供了相应的api,让我们在需要时,真正释放未使用的空间。
  4. 二进制安全
    C语言字符串由于都以’\0’结尾,所以我们不能在C语言字符串中不能存储任何包含空字符的数据,否则会被误认为字符串的结尾了,这些限制使得不能保存图片,音频等数据,而Redis由于使用len记录数据长度,而不是使用空字符判断字符串是否结束,所以简单动态字符串可以存储包含空字符的数据。

二、链表
Redis里的链表并没有特别需要说明的地方,和其他语言中的链表类似,定义了链表节点listNode结构,是一个双向链表,同时使用list来持有链表,list的结构包含head,tail,len属性,还有一些方法,如复制、释放、对比函数。

三、字典
字典又称为符号表,是一种保存键值对的抽象数据结构,相当于Java的HashMap结构。字典在Redis中的应用相当广泛,比如Redis中的所有键值对就是使用字典来保存的。Redis里的字典实现原理和HashMap基本一致,都使用了链式结构来解决键冲突的问题,即当有两个键hash到统一结点时,使用链来连接两个节点。不过Redis中的字典有一个地方和HashMap有很大区别,即rehash的时候使用了渐进式扩展的方式扩展字典结构。rehahs:如果字典保存的数据len超过了字典数组的size,即哈希表保存的键值对数量太多或者太少时,哈希表会进行相应的扩展或者收缩。Redis的字典保存了两个哈希表,ht[0]和ht[1],大多数时候ht[1]执行null,在rehash的时候ht[1]会指向一个两倍于ht[0]的空字典数组,将ht[0]里的数据全部移动到ht[1]里,移动完后将ht[0]执行ht[1],释放ht[0]的空间,执行null,将ht[0]变为ht[1]。

四、跳跃表
跳跃表是一种有序数据结构,主要是用来解决有序链表的快速查找问题。跳跃表可以支持平均O(logN),最坏O(N)复杂的节点查找。在大部分情况下,跳跃表的效率可以和平衡术相比,并且跳跃表实现简单,所以不少程序使用跳跃表代替平衡树。假如我们有一个有序链表1->3->5->7->9->11->13->15,虽然我们知道是有序的,但是限于链表的实现原理,我们不能使用二分查找方法来快速查找元素。跳跃表就是用来解决这个问题的。跳跃表会在刚才的1-15个结点的基础上再添加一层结点,比如包含3-7-11-15作为第二层链表,7->15作为第三层链表,这样我们就可以从最上层结点查找,找得到直接返回,找不到的话也能确定下下一层的方位,再下一层的方位上查询,基于这一思想实现跳跃表的快速查找。

五、压缩列表
压缩列表是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的实现。压缩列表的主要作用是为了节约内存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值