本文为极客时间《Redis 核心技术与实战》的学习笔记。
为什么要用Redis?
从整个互联网的业务场景来看,读操作远远大于写操作是最普遍的情况。使用Redis作为缓存可以很好的解决这种业务场景,代价是:增加了代码量,引入了数据一致性问题。想要得到一些,总是要失去一些。与Redis带来的性能上的巨大提升,这些问题显得就没那么重要了。
使用Redis时主要有两个思路:
这两者的区别在于,对于数据的持久性要求不同。通常情况下,我们会选择缓存来使用,这也是绝大多数业务场景的需要。
Redis的数据结构
基础数据结构
简单的讲,Redis 的数据结构有五个常用类型:String(字符串)、List(列表)、Hash(哈希)、Set(集合)和 Sorted Set(有序集合)。
在具体的使用场景中:
底层数据结构
除了String的底层是简单字符串SDS结构,其他类型底层都有两种数据结构支持。Redis的配置列表中有一块配置信息是专门用来设置具体使用哪一个数据结构。比如:对于Hash而言,其底层会选择哈希表和压缩链表这两种数据结构。在配置文件中:
- hash-max-ziplist-entries:表示用压缩列表保存时哈希集合中的最大元素个数。
- hash-max-ziplist-value:表示用压缩列表保存时哈希集合中单个元素的最大长度。
对于一个Key,一旦超过其中一个阈值,Redis就会从压缩链表切换到哈希表,并且这个过程不可逆。一旦切到了哈希表,后续不会再切换回来。
问题:为什么Redis会有这种操作?
Redis中常见的底层结构够:SDS,双向链表,哈希表,压缩链表,整数数组,跳表。其中SDS,压缩链表和跳表,需要展开一下。
SDS
SDS全称:简单动态字符串(Simple Dynamic String,SDS),是String类型的底层结构。C语言原生的字符串是非常难用的,Redis使用SDS代替了原生的字符串。相比较而言,它具有以下几个优势:
SDS的整体数据结构比较简单,包括:
采用类似思想的还有:GO的切片。
问题延伸,为什么String类型容易浪费内存?
这个问题需要从三个角度来回答:
详细而言:
第一点,我们上面已经讲过了。第二点参考下图:
第三点,假如我们申请的内存为N个字节,那么jemalloc会分配 A(A大于N,小于N的2的幂次数) 个字节。
另外,SDS会分成三种分配方式:
压缩链表
压缩链表的结构如下:
- zlbytes,表示整个压缩链表占用的字节数。
- zltail,指向压缩链表中最后一个压缩节点的偏移量。
- zllen,表示压缩链表中包含的节点数量。
- zlend,表示压缩链表的结束。
- entry,用来存储数据的结构。
它的特点,换句话说,它是如何压缩数据降低内存开销的。
跳表
跳表是少有的一个结构,最常用的场景就是redis的有序集合。它的原理如下:
跳表的核心思想就是:加一层。通过加索引的方式,优化查询效率。整个过程就像在多级索引上跳来跳去,最后定位到元素。当数据量很大时,整个复杂度就O(logN)
需要注意的一个问题:
为什么不采用树或者红黑树,或者像MySQL一样采用B树? 这个问题本质上问的是跳表和其他数据结构的差别。
这里有大佬的经典回答:news.ycombinator.com/item?id=117…
有两个核心问题:
扩展结构
BitMap
Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态。你可以把 Bitmap 看作是一个 bit 数组。
Bitmap 常用于二值状态的业务场景,比如:用户是否登录,商品是否存在。
HyperLogLog
HyperLogLog 是一种用于统计基数的数据集合类型,它的最大优势就在于,当集合元素数量非常多时,它计算基数所需的空间总是固定的,而且还很小。日常用的比较少,了解即可。
GEO
GEO 是用于存储和处理地理位置信息的数据结构,跟地理位置相关的业务会用到这个结构。
Streams
Streams 是 Redis 专门为消息队列设计的数据类型,可以解决常见的消息队列问题。但是,我们有专门提供消息队列的组件:kafka,RocketMQ 等。
这里有一个常见问题:如何使用redis实现一个消息队列?实现一个延时消息队列?
全局哈希
存储结构
Redis的会使用一个全局哈希表来存储所有的Key,就像我们早期讲的GO的Map一样,它采用的也是拉链法。
渐进式Rehash
就像我们之前聊过的,Map的装载的数量增加时,性能会下降。为了提高性能,会对Map进行扩容,Redis采用了渐进式Rehash的方式进行扩容:
常用耗时情况
小工具
测算数据占用的内存空间:Redis容量预估-极数云舟