1.redis缓存:
-
缓存穿透:查询一个不存在的数据,mysql查询不要也不会直接写入缓存导致每次请求都去访问数据库
解决方法:1.缓存空数据,查询返回的数据为空,仍然把这个空结果缓存进数据库,简单,消耗内存,容易导致数据不一致性
2.布隆过滤器:缓存预热时也要预热布隆过滤器,先去查询布隆过滤器的key,如果key不存在就直接方法,
内存占用少,没有多余key,但存在误判
-
缓存击穿: 给某一个key设置了过期数据,当key过期的时候,恰好这个时间点有大量并发请求过来,这些并发请求有可能压塌数据库
解决方法:
使用互斥锁:当缓存失效时,不立即去load db,先使用如 Redis 的
setnx 去设置一个互斥锁,当操作成功返回时再进行 load db的操作并回设缓
存,否则重试get缓存的方法。保证了数据的一致性但分布式锁容易导致死锁问题
key逻辑过期:在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
当查询的时候,从redis取出数据后判断时间是否过期如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新。高可用性,性能比较高,但是数据同步这块做不到强一致。
创建定时任务对一个热点key设置一个定时任务,在key快要过期时,定时任务自动刷新key
-
缓存雪崩:是指同一时间内大量的缓存key同时失效或者redis噶了,导致大量请求到底数据库,造成压力
解决方法:给不同的设置各种的过期时间,设置一个定时任务,定时循环刷新热点key主动打散热点key
2.双写一致性:
-
双写一致性:当修改数据库的数据时也要同时更新缓存里面的数据,缓存和数据库里面的数据保持一致性
-
读操作:缓存命中,直接返回,返回未命中查询数据库,写入缓存,设置超时时间
-
允许延时一致业务:使用MQ消息中间件,更新数据后,通知缓存删除
-
对于强一致性业务:采用redisson提供的读写锁
共享锁即读锁readlock:加锁以后,其他线程可以操作读数据
排它锁:独占锁即writelock,加锁以后,阻塞其他线程进行读写操作
3.延迟双删:
-
延迟双删(Delayed Double Deletion)是一个在读写高并发场景下,确保缓存与数据库数据一致性的一种策略。它的核心思想是通过两次删除缓存操作以及适当的延迟,来避免缓存和数据库间的数据不一致问题
-
更新数据库:首先,更新数据库中的数据。这是为了确保数据的准确性。
-
删除缓存:在更新数据库后,立即删除相关的缓存。这一步是为了防止旧数据继续存在于缓存中。
-
延迟一段时间:等待一段时间(通常是比业务操作预计完成的时间稍长一些),目的是确保所有可能的缓存重建请求已经结束。
-
再次删除缓存:再次删除缓存,以确保在第一步删除缓存和更新数据库之间可能产生的缓存重建已被清除。
4.redis数据的持久化:
-
RDB:定时对整个内存做快照,数据并不完整,2次备份之间会丢失,对于ROB来讲会进行文件压缩,空间占用较小,但回复数据很快,但数据恢复优先级较低。因为数据完整性不如AOF,占用大量的cpu和内存消耗,适用场景:可以容忍数分钟的数据丢失,追求更快的启动速度
-
AOF:记录每一次的执行命令,数据先对完整,主要取决于刷盘策略,因为记录命令,所以1文件体积先对较大恢复速度较慢,当数据完整性更高,恢复优先级较高, 系统资源占用较低,主要是磁盘io资源,但AOF重写时会占用大量的cpu和内存资源对数据的安全性较高时使用
5.数据过期策略:
-
redis对数据设置数据的有效时间,数据过期以后,就需要把数据从内存中删除,可以按照不同的规则进行删除,这个规则就是数据过期策略
-
惰性删除:设置过期时间后,不许要对他自动删除,只会在需要这个key时,会检查是否过期,如果过期了就自动删除,否则就返回这个key,但对内存占用较大,如果这个key过期了但一直没有使用就会占用内存
-
定期删除:每隔一段时间就会对一些key进行检查,删除里面过期的key,最终检测所有的key,可以通过限制删除操作执行的时长和频率来减少删除操作对cpu的影响,定期删除也有效释放过期key占用的内存,缺点是难以限制删除操作执行的时长和频率
定期删除有两种模式:
-
SLOW:是定时任务,执行频率默认为10hz,每次不超过25ms,可以通过redis.conf的hz选项进行修改
-
FAST:执行频率不固定,但2次间隔不低于2ms,2次耗时不超过1ms
-
6.数据淘汰策略:
-
当缓存内存不足时,redis就会按照某种规则将内存中的数据删除,这种规则就是数据淘汰策略
-
noevction: redis默认策略,不淘汰任何key,但内存不足时不允许写入任何数据
-
volatile-ttl: 对设置了ttl(过期时间)的key,比较key剩余的ttl值,ttl越小越容易被淘汰
-
allkeys-random: 对全体key随机进行淘汰
-
volatile-random: 对设置了ttl的key进行随机淘汰
-
allkeys-lru: 对全体key,基于LRU算法进行淘汰
-
volatile-lfu:对设置了ttl的key基于LRU算法进行淘汰
-
allkeys-lfu:对全体key,基于LFU算法进行淘汰
-
volatile-lfu:对全体设置ttl的key,基于LFU算法进行淘汰
-
LRU:最近最少使用,用当前时间减去最后访问时间,值越大淘汰优先级越高
-
LFU:最少频率最低,统计每个key的访问频率,值越小,淘汰优先级越高
7.分布式锁:
-
redis实现分布式锁主要利用setnx,即set if not exists,因为redis是单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或删除key的时候是其他客户端是不能设置这个key的,同时redis底层是lua脚本,保证原子性
-
在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当业务执行完成之后需要使用释放锁就可以了,正常看门狗锁刷新时间为过期时间/3,默认为10
-
redis的分布式锁可以重入:这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用的hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数
-
redisson这个锁解决不了主重一致性,可以引入红锁,但红锁性能太低了。redisson提倡高可用,这时候建议使用zookeeper实现的分布式锁
8.redis集群方案:
-
主从复制:单个redis的并发能力有上线,要提高redis的并发能力,就需要搭建主从集群(一主多从),实现读写分离
一般情况下:都由主节点master进行写的操作,多个从节点slave进行读操作,需要主从数据同步
-
主从数据同步原理:
-
主从全量同步: 从节点请求主节点同步数据(replication id ,offset)
主节点判断是否为第一次请求,如果为第一次就与从节点同步版本号
主节点会执行bgsave,生成rdb文件,发送给我从节点进行数据同步,
同时在rdb期间也会接收其他命令并写入一个日志文件
然后把生成的日志文件发给从节点进行数据同步
-
增量同步:从节点请求同步主节点数据,如果不是第一次请求就获取从节点的offset,
主节点同步命令从日志中获取offset值之后的日志,发送给从节点进行数据同步
-
9.哨兵模式:
-
redis提供了哨兵机制来实现主从集群的自动故障恢复能力
-
监控:会不断的检测master和slave是否会正常工作
-
自动故障升级:如果master故障后,哨兵会自动将一个slave升级为master,当实例故障恢复后也以新的master为主
-
通知;哨兵充当redis客户端的服务发现来源,当集群发生故障时,会将最新的消息推送给redis客户端
-
选举规则:哨兵配置文件中的优先级,复制偏移量,被选中的次数(被选中次数),连接状态和响应时间
-
-
脑裂:
-
由于redis master节点和redis salve节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到master,所以通过选举的方式提升了一个salve为master,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在old master那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会 将old master降为salve,这时再从新master同步数据,这会导致old master中的大量数据丢失
解决方法:
-
第一可以设置最少的salve节点个数,比如设置至少要有一个从节点才能同步数据,
-
第二个可以设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失
-
10.分片集群:
-
分片集群作用:
-
集群中有多个master,每个master保存不同的数据
-
每个master有多个slave节点
-
master之间通过ping检测彼此健康
-
客户端可以访问集群中的任一节点,最终请求都会被转发到正确的节点
-
-
redis分片集群中数据怎么存储和读取的
-
redis分片集群引入hash槽的概念,redis集群中有16384个哈希槽
-
将163384分配给不同的实例
-
每个请求过来会对key有效部分进行取模,就通过key的哈希值对16384进行取模,余数作为插槽,寻找插槽所在的实例
-
10.redis是单线程为什么这么快:
-
redis是纯内存操作,执行速度非常快
-
采用单线程操作,避免了不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
-
使用I/O多路复用模型,非阻塞io,基本上采用epoll
-
redis是纯内存操作,执行速度非常快,他的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型
-
I/O多路复用;利用单线程去监听多个socket,并在莫个socket可读,可写时,得到通知,从而避免了无效等待充分利用了CPU资源
-
几种监听模式:
-
select:自会通知用户进程有scoket就绪了,但不确定到低是哪个socket,需要用户遍历所有scoket来确定
-
poll: 同select同理
-
epoll :在通知用户进程就绪的同时,把已经就绪的scoket写入内存中
-
-
-
redis6.0引用了多线程,但本质是还是单线程操作,只不过命令解析,和写出数据使用了多线程,但命令执行还是单线程,找不到多线程解决了网络问题,即命令回复处理器和命令应答处理器采用了多线程