文章目录
Redis 的实现原则
单线程+多路IO复用
1、常用五大数据类型 - 底层结构解析
1.1 String 类型
Redis 实现字符串的数据结构是 SDS,其是二进制安全的,意味着 Redis 的 string 可以包含任何数据。比如 jpg 图片或者序列化的对象。
1.1.1 原子性
定义:原子操作是指不会被线程调度机制打断的操作。
分两种场景分析:
1、单线程环境下,能够在单条指令中完成的操作就叫原子操作,因为中断只能发生在指令之间。
☆单条指令应该是单条机器指令,那么 incrby key 的自增+1操作是解析成单条的机器指令?还是指单条的代码指令
2、多线程环境下,不能被其他线程打断的操作就叫原子操作。
Redis 单命令的原子性主要得益于 Redis 的单线程。
原子性的应用案例:
1、java 中的 i++ 是否是原子操作?
不是。
2、i = 0;两个线程分别对 i 进行 100 次 i++,不考虑 cpu 的调度策略下,i 的理论范围是多少?
2 ~ 200。
1.2 List 类型 - 单键多值
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
跳跃列表(skipList)、压缩列表(zipList)和快速列表(quicklist)
Redis 实现 List 的数据结构是快速链表 quickList。首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是压缩列表。
它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较多的时候才会改成 quicklist。
因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是 int 类型的数据,结构上还需要两个额外的指针 prev 和
next。
Redis 将链表和 ziplist 结合起来组成了 quicklist。 也就是将多个 ziplist 使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
1.3 Set 类型 - 集合
Redis set 对外提供的功能与 list 类似是一个列表的功能 ,特殊之处在于 set 是可以自动去重的,当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。
Redis 的 Set 是 string 类型的无序集合。它底层其实是一个 value 为 null 的 hash 表,所以添加,删除,查找的复杂度都是0(1)。
一个算法,随着数据的增加,执行时间的长短,如果是0(1),数据增加,查找数据的时间不变。
Redis 实现 Set 的数据结构是哈希表。Java 中 HashSet 的内部实现使用的是 HashMap,只不过所有的value都指向同一个对象。Redis 的 set 结构也是一样,它的内部也使用 hash 结构,所有的 value 都指向同一个内部值。
1.4 Hash 类型 - 哈希
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。类似 Java 里面的Map<String.Object>。用户ID为查找的 key,存储的 value 用户对象包含姓名,年龄,生日等信息,如果用普通的 key/value 结构来存储。
Hash 类型对应的数据结构是两种: ziplist (压缩列表),hashtable (哈希表)。当 field-value 长度较短且个数较少时,使用 ziplist, 否则使用hashtable.。
1.5 Zset 类型
SortedSet (zset)是 Redis 提供的一个非常特别的数据结构,一方面它等价于 Java 的数据结构 Map<String,Double>,可以给每一个元素 value 赋予一个权重 score,另一方面它又类似于 TreeSet,内部的元素会按照权重 score 进行排序,可以得到每个元素的名次,还可以通过 score 的范围来获取元素的列表。
zset底层使用了两个数据结构:
1、hash:hash的作用就是关联元素 value 和权重 score,保障元素 value 的唯一性,可以通过元素 value 找到相应的 score 值。
2、跳跃表:跳跃表的目的在于给元素 value 排序,根据 score 的范围获取元素列表。
1.5.1 跳跃表(跳表)
1、简介
有序集合在生活中比较常见,例如根据成绩对学生排名,根据得分对玩家排名等。对于有序集合的底层实现,可以用数组、平衡树、链表等。数组不便元素的插入、删除;平衡树或红黑树虽然效率高但结构复杂;链表查询需要遍历所有效率低。Redis 采用的是跳跃表。跳跃表效率堪比红黑树,实现远比红黑树简单。
2、实例
对比有序链表和跳跃表
(1)、有序链表
链表中查询的时间复杂度O(n)
(2)、跳跃表
检索步骤:
1、从第2层开始,1节点比51节点小,向后比较。
2、21节点比51节点小,继续向后比较,后面就是NULL了,所以从21节点向下到第1层
3、在第1层,41节点比51节点小,继续向后,61节点比51节点大,所以从41向下
4、在第0层,51节点为要查找的节点,节点被找到,共查找4次。
跳跃表中查询的时间复杂度O(logn)。
2、Redis 的发布和订阅
2.1.什么是发布和订阅
Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
Redis 客户端可以订阅任意数量的频道。
2.2.Redis的发布和订阅
1、客户端可以订阅频道如下图
2、当给这个频道发布消息后,消息就会发送给订阅的客户端
2.3.发布订阅命令行实现
1、打开一个客户端订阅channel1
SUBSCRIBE channel1
2、打开另一个客户端,给channel1发布消息hello
publish channel1 hello
返回的1是订阅者数量
3、打开第一个客户端可以看到发送的消息
注:发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息