Redis 数据类型和各自的使用场景
常见的有五种数据类型:String字符串,List列表,Hash哈希,Set集合、Zset有序集合。
1、String类型
String适用场景:一般用于缓存字符串对象
它底层的数据结构是 SDS简单动态字符串。
1、SDS 不仅可以保存文本数据,而且能保存图片、视频、压缩文件这样的二进制数据。
2、SDS 获取字符串长度的效率更高,用 len 属性记录了字符串长度,时间复杂度大约是 O(1)。而C 语言的字符串没有len, O(n);
3、Redis 的 SDS API 是安全的,拼接字符串不会造成缓冲区溢出,因为拼接之前会先检查,如果空间不够就自动扩容。
2、List类型
List场景:一般用于任务队列
底层数据结构由 quicklist 快表实现。
Quicklist :「双链表 + 压缩列表」组合,一个 quicklist 就是一个链表,而链表中的每个元素是一个压缩列表。
压缩列表:是一块连续的内存空间,有点像数组。但是它有一个记录前一个元素长度的字段,主要是帮助快速定位到列表中的下一个元素,从而提高遍历效率。但是有【连锁更新】的问题。
- 前一个节点长度 < 254 字节,prevlen 需要用 1 字节的空间来保存这个长度值
- 前一个节点长度 > 254 字节,prevlen 需要用 5 字节的空间来保存这个长度值
- 本来都在250~253 之间,因为前面插入了新的导致最前面大于254,后面都跟着变了
解决办法:控制每个链表节点中的压缩列表的元素个数,来规避连锁更新的问题。因为压缩列表元素越少,连锁更新带来的影响就越小。
3、Hash类型
Hash场景:购物车等。
键:cart:123 、字段:product:456、值:2 (用户123的购物车里有456产品,购物车里456产品的数量是2)
在 Redis 7.0 中,由 listpack 数据结构来实现。
listpack 是在压缩列表的基础上改造而来的,它没有压缩列表中记录前一个节点长度的字段了,只记录当前节点的长度,所以加入新元素的时候,不会影响其他节点的长度字段的变化,从而避免了压缩列表的连锁更新问题。
4、Set类型
Set 场景:一些聚合计算场景,比如点赞、共同关注等。
Set 类型的底层数据结构是由哈希表或整数集合实现的:
如果集合中的元素都是整数且元素个数小于默认的 512 个,Redis 会使用整数集合作为 Set 类型的底层数据结构;
如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。
5、Zset类
Zset场景:排序场景,比如Rank排行榜、姓名排序等。
Zset 类型的底层数据结构是由 listpack 数据结构来实现。
Redis 后续版本又支持四种数据类型,它们的应用场景如下:
BitMap:
二值状态统计的场景,比如签到、判断用户登陆状态等;
HyperLogLog:
海量数据基数统计的场景,比如百万级网页访问量计数等;
GEO:.
存储地理位置信息的场景,比如滴滴叫车;
Stream:
消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。
跳表
跳表其实是一种多层的有序链表,跳表在原有的有序链表基础上结合二分查找的思想增加了多级索引,通过索引来实现快速查询。
我们知道对于一个单链表来说,即使它有序,查找的时间复杂度还是O(n)。我们可以做一些优化,每两个结点提取一个"索引"到上一级,上一级有到下一级的指针。那如果链表数据是1-10,正常查找是O(n),有了索引之后就可以2、4、6、8这样来查找,甚至说数据多的话可以建立三层四层索引,以此来提升工作效率。
跳表的查询、插入、删除时间复杂度都是O(logn),空间复杂度是 O(n)