Redis的使用场景
根据自己简历上的业务进行回答
缓存 穿透、击穿、雪崩、双写一致、持久化、数据过期、淘汰策略
分布式锁 setnx redisson
缓存穿透:查询一个不存在的数据,数据库查不到数据也不会直接写入缓存,就会导致每次请求都查询数据库,一般都是恶意攻击。
解决方案:1、缓存不存在的数据,这会消耗内存 2、使用布隆过滤器,redis中的一种数据结构 bitmap 位图结构,对它先进行预热(多次hash算法),当key不存在一定不存在,当key存在可能不存在,会存在一定的误判,误判率可以设置为5%。
缓存击穿:当缓存中的key刚好过期,恰好这时间对这个key有大量的并发请求过来,这些请求可能会瞬间把DB压垮。
解决方案:1、互斥锁,强一致性,性能差 2、对热点数据不设置过期时间
缓存雪崩:同一时段大量的缓存key同时失效或者Redis宕机,导致大量请求到数据库。
解决方案:1、给不同的Key的过期时间添加随机值 2、利用Redis集群提高服务的可用性
3、给缓存业务添加降级限流策略
双写一致(针对高并发)
解决方案:1、强一致性,可以采用redisson读写锁来保证数据的同步,在读的时候添加读锁,可以保证读读不互斥,读写互斥。当更新数据的时候,添加排它锁,读读和读写都互斥。 2、最终一致性,可以采用MQ中间件,更新数据之后,通知缓存删除
持久化
1、RDB:一个快照文件,bgsave的命令通过fork一个子进程把内存存储的数据写到磁盘上,采用copy on write 的策略不影响主线程的写操作,避免了子线程无意义的复制。
2、AOF: 追加文件,当redis操作写命令时,都会存储在这个文件中。
哪种方式恢复比较快?
RDB因为是二进制文件,保存的体积比较小,恢复速度比较快,但它有可能丢失数据。
AOF虽然恢复的速度慢一些,但是它丢数据的风险要小很多,在设置刷盘策略,可以设置每秒批量写入一次命令
Redis过期删除策略
惰性删除:设置该key过期时间后,不去管它,当需要该key时,会检查是否过期,如果过期就删掉它,反之返回该key
优点:对CPU友好,只有使用才检查key是否过期。
缺点:对内存不友好,过期的key一直没有使用,会一直存在内存中
定期删除:每隔一段时间,对key进行检查,删除里面过期的key(抽取一定数量的key进行检查,并删除其中的key) (SLOW模式和FAST模式)
优点:有效释放的过期key占用的内存
缺点: 难以确定删除操作执行的时长和频率
过期删除策略:惰性删除 + 定期删除两种策略配合使用
淘汰策略
数据的淘汰策略:当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。
Redis支持8种不同策略来选择要删除的key:
noeviction: 不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
allkeys-random:对全体key ,随机进行淘汰。
volatile-random:对设置了TTL的key ,随机进行淘汰。
allkeys-lru: 对全体key,基于LRU算法进行淘汰
volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰
allkeys-lfu: 对全体key,基于LFU算法进行淘汰
volatile-lfu:对设置了TTL的key,基于LFU算法进行淘汰
LRU (Least Recently Used) 最近最少使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
LFU(Least Frequently Used) 最少频率使用。会统计每个key的方问频率,值越小淘汰优先级越高。
分布式锁
使用场景:定时任务、抢单、幂等性场景
如何实现?
在redis中提供了一个命令setnx(SET if not exists)
由于redis是单线程的,用了命令之后,只有一个客户端对某一个key设置值,在没有过期或删除key的时候其它客户端是不能设置这个key的。
如何控制Redis实现分布式锁有效时长?
采用框架redisson实现的。
在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当业务执行完成之后需要使用释放锁就可以了。
还有一个好处就是,在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,它会自选不断尝试获取锁,如果客户1释放之后,客户2就可以马上持有锁,性能也得到了提升。
redisson实现的分布式锁是可重入的吗?
是可以重入的。这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用的hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数。
redisson实现的分布式锁能解决主从一致性的问题吗?
不能解决,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了,如果业务中非要保证数据的强一致性,建议采用zookeeper实现的分布式锁。
Redis集群有哪些方案?
主从复制
master 写操作,slave 读操作,读写分离。
主从数据同步原理
主从全量同步
1.从节点请求主节点同步数据(replication id、 offset)
2.主节点判断是否是第一次请求,是第一次就与从节点同步版本信息 (replication id和offset)
3.主节点执行bgsave,生成rdb文件后,发送给从节点去执行
4.在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)
5.把生成之后的命令日志文件发送给从节点进行同步
主从增量同步
1.从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值
2.主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步
哨兵模式
保证Redis的高并发高可用 ,实现主从集群的自动故障恢复(监控、自动故障恢复、通知)
你们使用redis是单点还是集群,哪种集群?
主从(1主1从)+哨兵就可以了。单节点不超过10G内存,如果Redis内存不足则可以给不同服务分配独立的Redis主从节点。
redis集群脑裂,该怎么解决呢?
集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在老的主节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这时再从新master同步数据,就会导致数据丢失。
解决:我们可以修改redis的配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求就可以避免大量的数据丢失。
分片集群
有什么作用?
集群中有多个master,每个master保存不同数据
每个master都可以有多个slave节点
master之间通过ping监测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
Redis分片集群中数据是怎么存储和读取的?
Redis分片集群引入了哈希槽的概念,Redis 集群有16384个哈希槽
将16384个插槽分配到不同的实例
读写数据:根据key的有效部分计算哈希值,对16384取余(有效部分,如果key前面有大括号,大括号的内容就是有效部分,如果没有,则以key本身做为有效部分)余数做为插槽,寻找插槽所在的实例
Redis是单线程的,但是为什么还那么快?
Redis是纯内存操作,执行速度非常快
采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
使用I/0多路复用模型,非阴塞IO
能解释一下I/O多路复用模型?
1.I/0多路复用
是指利用单个线程来同时监听多个Socket,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的/0多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
2.Redis网络模型
就是使用I/O多路复用结合事件的处理器来应对多个Socket请求
连接应答处理器
命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件
命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程