Redis 基础数据结构

179 阅读4分钟

Redis有5种基础数据结构,分别为 string (字符串)、list (列表〉、hash (字典)、set (集合)和 zset (有序集合)。这 种基本数据结构的熟练使用,是 Redis 的相关知识中最基础、最重要的部分,也是在 Redis 面试题中被问到最多的知识点。

1. string(字符串)

Redis 的字符串是动态字符串,是可以修改的字符串,内部结构的实现类似于Java ArrayList ,采用预分配冗余空间的方式来减少内存的频繁分配,如图所示,内部为当前字符串分配的实际空间 capacity 一般要高于实际字符串长度 len 。当字符串长度小于 lMB 肘,扩容都是加倍现有的空间 。如果字符串长度超过 lMB ,扩容时一次只会多扩1MB 的空间 。需要注意的是字符串最大长度为 512MB

image.png

2. list(列表)

Redis 的列表相当于 Java 语言里面的 LinkedList 注意它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n) ,这点让人非常意外。如图所示 表中的每个元素都使用双向指针顺序,串起来可以同时支持前向后向遍历。 当列表弹出了最后一个元素之后,该数据结构被自动删除,内存被回收。

image.png

如果再深入一点,你会发现 Redis 底层存储的不是个简单的 linkedlist,而是称之为“快速链表”(quicklist)的一个结构。

在存储元素较少的时候,会使用一块连续的内存存储,这个结构是ziplist(压缩列表),它将所有的元素彼此紧挨着一起存储,分配的是一块连续的内存。当数据量大的时候才会改成quicklist。Redis将链表和ziplist起来组成了quicklist,也就是将多个 ziplist 使用双向指针串起来使用。如图所示,quicklist 满足了快速的插入删除性能,又不会出现太大的空间冗余。

image.png

3.hash(字典)

Redis 的字典相 当于 Java 语言里面的HashMap,它是无序字典,内部存储了很多键值对。实现结构上与Java的HashMap是一样的,都是“数组+链表”二维结构。如图所示,第一维hash 的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。

image.png

因为Java的HashMap在字典很大时rehash是个耗时的操作,需要一次性全部rehash。Redis为了追求性能,不能堵塞服务,所以采用了渐进rehash策略。

渐进式rehash会在rehash的同时保留新旧两个hash结构如图所示,查询是会同时查两个hash结构然后在后续的定时任务以及hash操作指令中,循序渐进地将 hash的内容 点点地迁移到新hash结构中。当搬迁完成了,就会使用新的hash结构取而代之。

image.png

实现原理: redis字典中有两个数组,还有一个字段rehashidx用来控制rehash(默认是-1)

何时发生扩容:
image.png 什么是扩容:

image.png

并且rehashidx从-1修改为0 此时,当前字典就进入了rehash的状态 之后对字典的增删改查操作都会进行一次单步的rehash

  • 单步的rehash会从当前的rehashidx开始,把数组1号的元素迁移到数组2号,迁移完一个索引后,rehashidx会加1
  • 处于rehash中的添加操作,都会把新添加的元素直接添加到数组2号中

查询也会进行同样的单步rehash操作,一直反复直到数组1号元素的个数为0,代表rehash完成 此时,就需要把数组1号的指针替换为数组2号,再把数组2号的指针重新置为NULL,最后把rehashidx置为-1。(这样,就完成了redis字典的rehash)

image.png

4.set(集合)

Redis的集合相当于Java语言里面的HashSet,它内部的键值对是无序的、唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值 NULL

5.zset(有序列表)

zset可能是Redis提供的最有特色的数据结构,它也是在面试中面试官最爱问的数据结构。如图所示,它类似于 JavaSortedSet和HashMap的结合体,一方面它是个set,保证了内部 value 的唯一性,另方面它可以给每个 value赋予一个 score ,代表这个value的排序权重。它的内部实现用的是一种叫作“跳跃列表”的数据结构。

image.png