redis为什么支持高并发
- 基于内存,读写速度非常快
- 采用单线程,减少上下文切换时间
- 采用IO多路复用和非阻塞IO
redis数据结构
string
- 描述:相当于ArrayList,预分配冗余空间减少内存频繁分配
- 用途:缓存
- 扩容方式:1M以内直接翻倍,大于1M,每次+1M
hash
- 描述:相当于HashMap,每个节点保存键值对
- 用途:类似于关系型数据库,节省反序列化时间
- 扩容方式:渐进式rehash(同时保留新旧节点)
list
- 描述:相当于LinkedList,实现原理为双向链表,插入删除快,但是索引定位慢
- 用途:热销榜,feed流
set
- 描述:相当于HashSet
- 用途:拥有差并交集功能,实现共同好友,可能认识的人等功能
sorted set
- 描述:类似于Set和HashMap的结合体,给每个value赋予一个score代表排序权重,内部使用HashMap和跳跃表,HashMap中放的是成员到score的映射,跳跃表中存放的所有的成员,使用跳跃表结构可以获得比较高的查询效率,并且在实现上比较简单。sorted set中最后一个value被移除后,数据结构自动删除,内存被回收
- 用途:排行榜
- 查询事件复杂度O(2log(n)) = O(log(n))
redis持久化方式
RDB
指定时间对数据进行快照,类似于MySQL的dump备份文件
AOF
记录每次对服务器的写操作,服务器默认使用aof文件内指令进行数据恢复,类似于MySQL的binlog
RDB与AOF混合使用
AOF文件中存有RDB二进制数据,合二为一
redis主从模式
![image.png](https://img-blog.csdnimg.cn/img_convert/86cdd673eee507cca59c7fb7cb7438d1.png#clientId=u4c181150-4bbb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9050eb04&margin=[object Object]&name=image.png&originHeight=812&originWidth=1114&originalType=url&ratio=1&rotation=0&showTitle=false&size=173601&status=done&style=none&taskId=ub8f68ad7-86bc-48cb-8919-cc0d521d6ac&title=)
以上被称为全量复制,当然还存在增量复制,通过offset从缓存队列中获取指令,如果不在master缓冲队列中,则进行全量复制
主从复制风暴
一主多从架构会引发主从复制风暴,解决办法:树状架构
redis哨兵架构
sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点
定时任务
- 每1秒每个Sentinel对其他Sentinel和Redis节点执行PING操作,即心跳检测
- 每2秒每个Sentinel通过Master节点和channel交换信息(Publish/subscrible)
- 每10秒每个Sentinel会对Master和Slave执行INFO命令,目的有两个
1) 发现Slave节点
2) 确认主从关系
主观下线
即SDOWN,指单个Sentinel实例对服务器做出的下线判断
客观下线
即ODOWN,值对各服务器做出的判断,当认定为客观下线时才会开始故障迁移
仲裁
配置文件quorum选项,一般设置为Sentinel个数的二分之一加1,大于此值则认定为客观下线
缺点:
- 主从切换需要时间(比集群多),会丢失所有期间数据
- 没有解决主节点写压力
- 动态扩容困难复杂
redis集群模式
概述
Redis Cluster将所有数据划为16384个slots(槽位),每个节点负责其中一部分槽位
槽位定位算法
便于理解:HASH_SLOT = CRC16(key) mod 16384
代码实现:HASH_SLOT = CRC16(key) & 16383
跳转重定位
redirect到正确的节点上操作
redis阶段通信机制
redis采用gossip协议进行通信
gossip协议的优点在于元数据的更新比较松散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;缺点在于元数据更新有延时可能会导致集群的一些操作会有一些滞后
集群选举原理
- salve发现自己的master变为FAIL
- 将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST信息
- 其他节点收到该信息,只有master响应,判断请求者合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack
- 尝试failover的salve收集master返回的FAILOVER_AUTH_ACK
- salve在收到超过半数master的ack之后成为新Master(这里解释了为什么至少需要3个主节点,如果只有两个,当其中一个挂了,只剩一个主节点是不能选举成功的)
- slave广播pong消息通知其他集群节点
从节点延时计算公式:
- DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
SLAVE_RANK表示此slave已经从master复制数据的总量rank,rank越小代表已复制的数据越新。(尽量使数据多的从节点优先成为master)
集群脑裂问题
脑裂指同时产生了两个master节点,在后续恢复到主从模式时,会丢失其中一个新写入的数据
解决方案:
//写数据成功最少同步slave数量
//只有当超过该值的从节点也写入了数据(模仿大于半数机制),才会返回成功
min‐replicas‐to‐write 1
Redis集群为什么至少三个且推荐为奇数
- 大于半数的master节点同意才能选取成功
- 节省机器,4个节点和3个节点可用性上相同(4个和3个一样,只允许挂一个节点)
原生redis锁的问题
- 执行超时
问题:a线程执行过程中超时了,b线程会将他的锁释放掉
解决:加锁时以UUID+threadId为value,释放锁时判断value值是否为当前线程
- 解锁原子性问题
问题:解锁时判断+释放锁不是原子操作,可能会出现多线程问题
解决:采用lua脚本或直接适应redisson