-
缓存穿透?如何解决?
缓存穿透是指查询一个一定不存在的数据,如果从存储层查不到数据,则不写入缓存,这就导致这个不存在的数据每次都要去数据库中查询,可能导致数据库挂掉。
可以通过布隆过滤器来解决。布隆过滤器用于检索一个元素是否在集合中。使用redisson实现。底层是一个大数组。存放0/1,来一个key进行3次哈希计算。通过找到的索引将数组内容由0改为1.但是存在误判。
-
缓存击穿
缓存击穿是指对于设置了过期时间的key,在某个时间点其过期,但突然对这个key有大量的并发请求,这些请求发现key过期后一般都从后端数据库加载数据,大量请求可能会导致数据库被压垮。
可以使用互斥锁(分布式锁)或者key逻辑过期来解决。互斥锁能够保证强一致性,但性能不好,同时可能出现死锁。而逻辑过期能够提供高可用性,性能比较高。但不能保证数据强一致性。
-
缓存雪崩
缓存雪崩是指缓存时采用了相同的过期时间,导致缓存在某一刻同时失效,请求全部转移到数据库上,导致数据库压力过重雪崩。与缓存击穿的区别是:雪崩是很多key,击穿是某一个key缓存。
可以在原有的失效时间上增加一个随机值,避免出现集体失效。 -
maysql与redis进行同步(双写一致性)
二者需要保证高度一致:使用读写锁实现强一致性。使用redisson实现的读写锁。读的时候添加共享锁,保证读读不互斥,读写互斥。添加数据的时候添加排他锁。读写读读都互斥。因为排他锁底层使用了setnx,保证同时只能有一个线程操作。
延时双删:写操作的时候,先把缓存数据删除,然后更新数据库,然后再延时删除缓存中的数据,这个延时并不好确定,在延时过程中可能会出现脏数据,并不能保证强一致性。
二者不需要保持高度一致:数据同步可以有一定的延时。采用阿里的canal组件实现数据同步。不需要更改业务代码,步数canal服务后,会伪装成一个mysql的从节点。当数据更新的时候,canal会读取binlog的数据,然后更新缓存。 -
redis的数据持久化
通过RDB或者AOF来做的。
-
redis的数据过期策略
惰性删除:在设置了该key过期时间后,不去管它,当需要该key的时候,我们再检查其是否过期,如果过期就删掉,反之则直接返回该key。
定期删除:每隔一段时间,就对一些key进行检查,删除里面过期的key。其中一种是slow模式,默认10hz,每次不超过25ms,可以修改redis.conf中hz来调整。fast模式执行频率不固定,两次间隔不低于2ms,耗时不超过1ms。
redis过期删除策略:惰性删除+定期删除两种配合使用。 -
redis 数据淘汰策略
当redis中内存不够用的时候,此时再向其中加入新的key,redis会按照某种规则将内存中的数据删除,这种数据删除规则被称为内存的淘汰策略。
noeviction:不淘汰任何的key,内存满的时候不允许写入新的key(默认策略)
volatile-ttl:对设置了ttl的key,比较key剩余的ttl值,ttl越小越淘汰(过期时间)
allkeys-random:对所有的key进行随机删除
volatile-lru:对设置了ttl的key,基于lru算法进行删除
LRU:最近最少使用,用当前时间减去最后一次访问时间,值越大淘汰优先级越高(删除不经常用的)
LFU:最少频率使用,统计每一个key的使用频率,频率越小淘汰优先级越高(删除使用次数少的)
注意:
一般情况下都是采用allkeys-lru,把最常用的留在内存中(有明显冷热数据之分)
没有冷热数据之分,所有的key用的都差不多,则可以采用allkeys-random
如果有置顶需求,则使用volatile-lru, 同时置顶数据不设置ttl
存在高频数据,则可以使用allkeys-lfu/volatile-lfu
当redis中内存用完之后,由于默认是noeviction,则会无法加入新的key,之后报错。
- redis分布式锁
分布式锁使用场景:集群情况下抢单、定时任务、幂等性场景
redis通过使用命令setnx来实现分布式锁,其通过redisson实现分布式锁,底层是setnx和lua脚本(保证原子性)
redisson实现分布式锁如何合理控制锁的有效时长?
在redisson的分布式锁中,提供了一个watchdog(看门狗),一个线程获取锁成功后,看门狗会给持有锁的线程续期(默认是10秒续期一次)
redission这个锁,可以重入吗?
可以重入,多个锁重入需要判断是否是当前线程,在redis中进行存储的时候使用了hash结构,来存储线程信息和重入的次数
redission锁能解决主从数据一致吗?
不可以,但是可以使用redission中的红锁来解决,但这样性能太低。如果业务中一定要保证数据的强一致性,可以采用zookeeper实现的分布式锁。
- redis集群方案
主从复制、哨兵模式、分片集群
redis的主从同步:
单节点的redis的并发能力是有上限的,要进一步提高redis的并发能力,就需要搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据
主从同步数据的流程:
全量同步:
从节点请求主节点同步数据(replication id、offset)
主节点判断是否为第一次请求,是的话就与从节点同步版本信息(replication id、offset)
主节点执行bgsave,生成rdb文件后,发送给从节点去执行
在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)
把生成之后的命令日志文件发送给从节点进行同步
增量同步:
从节点请求主节点同步数据,主节点判断不是第一次请求,获取从节点的offset值
主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步
怎样保证redis的高并发高可用?
哨兵模式:实现主从集群的自动故障恢复(监控、自动故障恢复、通知)
你们使用的redis是单点还是集群,哪种集群?
主从(1主1从)+哨兵。单节点不超过10G内存,如果redis内存不足则可以给不同服务分配独立的redis主从节点
redis集群脑裂怎么解决?
集群脑裂是由于主节点和从节点的sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主节点,这样就存在了两个主节点,就像大脑分裂了一样。这样会导致客户端还在老节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这是再从新的主节点同步数据,就会导致数据丢失。
我们可以修改redis的配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失。
- redis的分片集群有什么作用?
集群中有多个master,每个master保存不同数据
每个master都可以有多个slave节点
master之间通过ping检测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
redis分片集群中数据是怎么存储和读取的?
redis分片集群引入了哈希槽的概念,redis集群有16384个哈希槽
将16384个插槽分配到不同的实例
读写数据:根据key的有效部分计算hash值,对16384取余(有效部分:如果key前面有大括号,大括号内容就是有效部分,如果没有,则key本身就是有效部分)余数做为插槽,寻找插槽的实例
-
redis是单线程的,为什么还那么快?
redis是纯内存操作,执行速度很快。采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题。使用I/O多路复用模型,非阻塞IO -
解释下I/O多路复用模型
redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型主要就是实现了高效的网络请求
I/O多路复用是指利用单个线程来同时监听多个socket,并在某个socket可读可写时得到通知,从而避免无效的等待,充分利用CPU资源。
redis网络模型就是使用的I/O多路复用结合事件的处理器来应对多个socket请求。
连接应答处理器
命令回复处理器(redis6之后使用了多线程)
命令请求处理器(redis6之后,命令转换使用了多线程,命令执行的时候是单线程)