文章目录
- Spring 整合redis(细致)
-
- 0. redis基本的架构
- 1. 数据结构(面试重点)
- 2. 过期策略(针对过期的数据)
- 3. 淘汰策略(针对“内存不足”时的措施)
- 4. 缓存异常
- 5. *分布式锁(并发访问的正确性)
- 6. redis事务管理
- 7. redis的持久化机制(OKK)
- 8.redis的集群方案(视频学习)
- 9.redis的主从复制原理(数据一致性)
- 10.Redis 除了做缓存,还能做什么?(业务场景)
- 11.redis的IO模型(多看多理解)
- *我的项目--论坛项目:缓存+高级数据类型
- SpringCache整合redis的学习
- Spring整合Elasticsearch(商城项目)
Spring 整合redis(细致)
0. redis基本的架构
1. 数据结构(面试重点)
基本数据类型
底层的数据结构
1. SDS动态字符串 && 源码解读
动态字符串的扩展与申请内存空间(sdshdr32最大为512MB,可是sdshdr64是干嘛的)
动态字符串的特征与优势(查询长度快速+不会发生缓冲区溢出+动态扩容)
2. IntSet(整数数组)
其实这个和SDS很像,只不过数组存放的是int整数(但是不同的是:这里元素的存储是有序的,按照二分搜索进行判断,同时是不会出现重复的)
编码的升级过程:注意只能升级不能进行降级(2–>4,4–>8的两种情况),一旦有一个新加入的元素编码的等级更高,此时整个数组都需要升级(为保证有序性,按照倒序往后更新原来每个数的位置)
redis使用二分查找来确保intset数组的有序性与不重复(居然用的不是hash)
确保数据唯一的判断方式的源码
3. Dict(字典结构/hashmap结构)
为了实现渐进式的rehash操作,采用两个hash表结构来进行工作。
实际使用的dict结构 {两个hash表:dictht(由桶+链表实现,每个链表上是dictEntry节点)}
新元素插入在冲突链表的队首位置(因为这样方便移动计算)
rehash触发的条件(已保存节点数/哈希表的桶的数量==used/size)
对比一下java中的hashmap扩容的情况,java中的负载因子更小只有0.75(此外当链表长度超过8&&数组长度小于64也会触发扩容)
渐进式rehash操作
一般使用的是dict结构中dictht[0]哈希表1(利用rehashidx来表示是否处于rehash的阶段,如果等于-1说明rehash结束,此外这个rehashidx也会作为hash表1数据迁移的数组位置的index指针记录—rehash的初始化值为0,每次迁移完一个位置就会进行++自增操作)
rehash渐进式实现(没有采用子进程来完成扩容,而是采用了主线程进行缓慢的渐进式扩容)
注意新增的操作只会在hash表2进行,但是查删改会同时在两个表并行操作(保证了表1的只减不增)
4. ZipList(压缩列表)
连锁更新问题
使用“记录前一个节点的长度”这种结构可以同时推断出前一个后一个节点的起始的地址位置(注意:引发连锁更新空间的分配—但是这种情况比较极端类似“多米诺牌效应”—针对有多个251-253字节之间的entry排列&&有一个entry发生长度变化)
5. QuickList(双端链表—节点是ziplist)
quicklist的节点quickListNode节点内部都会指向一个压缩列表的结构(控制了压缩列表的长度,既解决了寻址的问题,有解决了连锁更新的问题)
注意:虽然quicklist减小了连锁更新的长度,但是还是会出现连锁更新的问题,这是无法避免的
6. SkipList(跳表—zset的底层结构–有score)
跳跃表首先必须按照顺序存储的链表结构,同时每个节点的指针层数是随机生成的,不同层级的指针对应的下一个节点的跨越维度是不同的(将查询的复杂度平均降到了log(N))
首先按照节点的score分数排序,之后(针对相同的score)按照ele元素值的字典排序----限制了查询也是这种顺序(先按score,再按ele)
注意是多级的索引指针,不是多级的链表,每个节点含有zskiplistNode结构的level数组【层级随机生成,(0,1)随机数小于0.25层级就加1,直到遇到的随机数大于0.25停止)------高低层之间的节点数量尽量维持1:2的关系】
对于同一个节点,不同层级的索引,对应的下一个节点和相应的跨度是不一样的
RedisObject的封装(恰好封装为5种基本的数据类型)
总结一下:string采用int(整数)和SDS的动态字符串的结构,List结构采用的链表+压缩列表的结构(前一个节点的长长度+本节点的内容及长度),set结构采用的intset(数据较少)/dict结构(数据较多),hash结构压缩列表(数据较少)/dict结构(数据较多时),zset结构采用压缩列表/ skipList+dict(这两个同时需要,一个按照分数排序,一个按照键值对存储键与分数的关系)
RedisObject组成:type对象类型(5种),encoding底层编码方式,lru表示对象最后一次访问的时间,recount对象引用计数器,指针(指向实际的数据空间,数据据结构就是那5种)
string–string
编码方式:针对字符串是否是数字,是否字符串长度小于或大于32字节(这个32按照具体的redis的版本来规定),可以分为3种编码方式。
默认是使用raw的编码方式,字符串长度最大为512MB
string–list(双向链表)值
使用quicklist实现:本质是双向链表+ziplist
实现队列和栈的不同场景(在队列的场景下,可以实现消息队列的功能,但是不能实现多个消费者消费同一条消息,即不能实现消费者组的功能-----除非使用redis的stream)
string–hash(string,string)值
数据较多时,使用dict结构实现
数据较少时,使用ziplist结构实现
string–set(无序集合)值
正常情况下,使用dict实现;只有元素比较少而且存储的是整数,才会采用Intset结构实现!!!!
string-- zset(有序集合,含权重)值 [有序指针对权重的排序]—跳表
同时使用两种的数据存储结构—skipList(按分数排序查询)+dict(用于键值对的保存与去重),占用你内存很高,但是支持快速查询和分数排名的操作(但是数据比较少时,直接使用压缩列表)
注意:ele不能重复(相当于key),score当然可以重复(相当于value)
可以看出zset是由跳表和dict两种数据结构构成的
不需要排序时—直接使用dict结构:key–val(分数)
需要排序时—直接使用跳表结构
元素较少且每个元素的长度比较少时,采用压缩列表实现
BigKey的问题
高级数据类型(主要是应用场景)
HyperLogLog(统计不重复数据的个数–不精确)
注意m/sigma的那个分式项就是调和平均数(m就是16384,14个bit位对应的十进制数),其中的kmax就是统计的hash值的从第15位开始往左的连续0的个数的最大值(历次增加数的最大值,其实最大为50,所以需要最多6bit来统计这个count),其中的A修正常数可能是Redis的员工自己做了无数次的实验搞出来的一个修正常数。
为什么选择这样的调和级数
Bitmap(bit数组读取,bit设置)
s实际上字符数组本质还是保存的位数组二进制的数据
注意bitmap的底层数据结构就是sds,本质是一个二进制的数组,统计数组位置上的1的个数(设置对应位置的index为0或1,这个index或者偏移量必须是整数)
键与bitmap数组偏移量位置映射关系的设计
将键(实际一般我们实际业务的中就是"数值类型",不是字符串—需要自己设计映射的关系)定位为bit数组相关的位置并置该位置为1,之后利用工具统计相应的整个数组的1的总数(注意可能需要新加入的key对应的offset大于了原始的数组的长度,此时需要对数组进行扩容)
牛客网的项目的键就是用户的id,直接id就是相应的偏移量
根据bitmap的offset(比如字符串的形式,需要先进行hash映射成为数值型的偏移量)如何定位在这个bit数组的位置呢??(可以使用hash算法,或者使用多层的hash算法解决hash碰撞)
这个总结的很好(偏移量:1.直接利用“数值的表面值”;2。对这个数值重新做一层hash的映射再来定位为数值的偏移量)
布隆过滤器使用的是多个hash函数,需要将多个hash函数对应的位置置为1
如何统计redis中bitcount数组中的1的个数(查表法+SWAR算法)
查表法(使用少量位数的情况):
SWAR算法(针对32位–4字节的情况进行判断):
知乎大神讲解,有空看一下
跑了一下
redis的统计维数组的bit个数采用的是查表法(不足28字节,每个字节按照查表法,即8比特共256种可能)和SWAR算法(每4个字节进行处理–共读取7组)相结合的方法
long long redisPopcount(void *s, long count) {
long long bits = 0;
unsigned char *p = s;
uint32_t *p4;
// 为查表法准备的表
static const unsigned char bitsinbyte[256] = {
0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,