1.1 常见的缓存策略有哪些?
Cache-Aside: 可以理解为缓存辅助。具体逻辑是:先去查缓存,缓存命中则返回;不命中则去查询数据库,同时更新缓存,最后返回。这种方式会存在数据不一致的情况。
Read/Write Through: 读写操作直接操作缓存,然后缓存同步的去更新数据库。
Write-Behind: 写后同步,先更新缓存,然后异步去更新数据库。
1.2 缓存穿透、缓存击穿和缓存雪崩
1.2.1 缓存穿透(Cache Penetration)
(1)什么叫缓存穿透
正常情况下,访问数据先去缓存中,如果缓存中没有,再去DB,然后维护缓存,下次同一个key过来时,就可以直接从缓存中读取,从而减小了DB的压力。但是,如果访问了一个不存在的key的数据,缓存中也没有,DB中也没有,后续如果一直访问就达不到减少DB压力的目的,数据访问过程一直是缓存-->DB。这就是缓存穿透。
(2)出现的场景
1>原先数据可能存在的,但是由于某种原因,数据从缓存和DB中删除了
2>恶意的攻击,大量访问不存在的业务数据,企图造成程序崩溃
(3)如何解决
1>缓存一个空值,同时要设置一个过期时间且过期时间不宜过长,这样可以防止大量数据访问到达DB层
2>使用BloomFilter,比如用一个bitmap来实现一个白名单,先判断数据key是否在白名单中,不在白名单中,则直接返回空值
1.2.2 缓存击穿(Cache Breakdown)
(1)什么是缓存击穿
正常时,数据访问能够走缓存,突然某个时刻,缓存失效了,数据访问又到达DB层。对于一般的数据,这种情况是合理的,但是如果失效的是一个热点key数据,那么DB的压力是很大的。这种情况就是数据击穿的缓存,重新访问DB。总结起来就是,热点key数据失效了,缓存没拦住大量的流量。
(2)出现的场景
应用程序中,热点数据过期了
(3)如何解决
根据具体场景来判断
1>如果业务运行热点数据永不过期,就设置热点数据永不过期
2>如果不允许设置永不过期,就设置一个互斥锁,不要让多个线程同时去访问DB, 一个线程去DB获取数据,更新缓存,其他线程等待,缓存更新完成后,其他线程直接从缓存中获取。
1.2.3 缓存雪崩(Cacha Avalance)
(1)什么是缓存雪崩
基本跟缓存击穿是一样的,区别在于,缓存雪崩是指大量热点key同时失效。
(2)出现的场景
大量热点key设置了同样的过期时间,导致同一时间集体失效,造成DB压力陡增
(3)如何解决
1>是否可以设置永不过期?
2>不能,则在缓存数据预热时,分散的设置过期时间,让它们不在同一时间失效。
1.3 Redis的数据结构有哪些?
string, hash, list, set, zset
1.4 Redis中list的相关操作
lpush、rpush、lpop、rpop
1.5 Redis使用的注意点
(1)要估计要内存使用量
(2)不设置大key, 这个大key指的是缓存的单个数据太大
(3)缓存一般都建议设置过期时间,热点数据根据业务场景可以设置永不过期
1.6 Redis的持久化方式
1.6.1 RDB
(1)说明
RDB是快照的模式,按照配置的策略,持久化当前的数据,比如配置每隔1h,保持快照一次。
(2)触发方式
1>手动触发: 通过save和bgsave命令来触发。其中save命令会造成Redis服务器阻塞,不能及时响应客户端请求。
这个命令基本上不能使用。bgsave方式是通过一个fork子进程来持久化数据,在创建子进程时有一定的阻塞,但是相比于save命令已经优化很多。
2>自动触发: 自动触发的都是bgsave命令,自动触发的场景一般是配置文件中,配置了一定的策略,每隔多长时间执行一次;此外,在主从复制模式下,从节点全量复制主节点数据时,也会主动bgsave一次,然后把RDB文件发送给从节点;还有一种场景,就是服务器shutdown时,如果没有开启AOF持久化模式,服务器也会主动执行一次bgsave
(2)优缺点
1>优点:特别适合全量数据的备份,RDB文件使用压缩算法,占用磁盘空间小,可以设置一定时长来备份数据。
服务器宕机时,从RDB文件恢复的速度要快于从AOF恢复
2>缺点:有一定的时间差,会造成一部分数据的丢失,不能做到近乎实时的响应,此外由于历史原因,RDB文件存在一些版本兼容问题。
1.6.2 AOF
(1)说明
AOF是写后日志,每次执行完命令,都先追加到AOF的缓存空间中,然后异步执行刷盘。比如按照推荐的策略,每个1s刷盘一次。
(2)优缺点
1>优点:通过配置刷盘fsync策略,可以实现秒级的数据持久化。
2>缺点:每次追加命令到AOF文件中,会导致AOF文件越来越大,这时AOF文件会自动重写来压缩文件。但是重写也是需要fork子进程的,也会导致主线程阻塞
1.7 常见的淘汰策略
(1)LRU(Least Recently Used), 时间维度,最近时间未使用
(2)LFU(Least Frequency Used), 使用次数维度, 最近最不常用
(3)FIFO(First In First Out), 存储维度,先进先出
1.8 Redis部署的模式
1.8.1 单机模式
这种非常好理解,就是在启动一个Redis服务实例,数据的读写都在这个实例上,如果宕机则失去缓存作用。稳定性和可靠性比较差。
1.8.2 主从复制模式
这种是读写分离模式,可以是一主一从,也可以是一主多从。主节点负责读写,从节点就负责读,不能负责写,因为主从复制模式,数据的流向是单向的,始终从主节点流向从节点,如果从节点也能写,那会造成数据主从不一致。主从数据同步直接存在一定的延时,对于数据一致性要求较高的业务系统,最好不要采用这种模式,建议采用集群模式。主从数据同步又分为全量复制和部分复制,全量复制的实现是基于RDB文件的,部分复制是依赖复制缓存区的偏移量offset。
1.8.3 主从哨兵模式
这个部署模式是针对主从模式出现问题时的优化。当主节点发生故障时,需要做三件事:1>在从节点中选出新的主节点 2>将其他从节点归属于新的主节点下 3>通知客户端。
那么主从的哨兵就是自动完成上述功能,发现故障 - 故障转移 - 通知故障
Redis的哨兵就是Redis实例的特殊启动,加上sentinel参数。逻辑上分为Redis数据节点和Sentinel集群。
1.8.3 集群模式
(1)Redis集群的分区规则
Redis集群的分区规则采用一致性Hash虚拟槽算法,将整个Redis集群划分为0 - 16383个槽slot,每个节点分别负责一定数据量的槽,这样就能将数据的存储离散开来。
(2)集群搭建
Redis集群最小配置需要6个节点,3主3从,主节点负责读写和维护集群数据,从节点按照主从复制的规则同步主节点数据。节点准备好后,需要通过握手命令,建立通信联系,形成集群。
(3)节点通信
Redis节点间使用Gossip流言协议来进行通信,通过不同节点之间的数据交换,从而达到整个集群数据更新的目的。
(4)集群扩展性
1>扩容
准备新节点 --- 加入集群 --- 迁移槽和数据
2>缩容
下线迁移槽和数据 --- 集群忘记节点 --- 关闭节点
(5)请求路由
执行Redis命令时,先判断key属于哪个槽,如果是本机,则执行命令;否则,重定向到目标槽的节点。
这部分一般由客户端帮忙解决请求路由问题。
(6)故障转移
1>ping 命令不通,则标记为主管下线状态(pfail),然后传播
2>其他节点拿到消息后后会维护在本节点的主观下线报告中
3>集群中超过半数以上的节点都认为其下线,则进入客观下线状态
4>开始处理故障
5>从下线节点的从节点中选择投票选择一个从节点晋升为主节点继续工作,集群内每个主节点都拿有一张选票,
当选票大于N/2 + 1时生效。如果没有选举成功则进入下次选择,直到成功
6>得到选票的从节点晋升为主节点,负责原先的槽,并广播pong消息告知集群中其他主节点,节点替换成功。
1.9 Redis的选举算法和流程
1.9.1 选举算法
基于Raft的算法,具体逻辑待研究
1.9.2 选举流程
(1)更新配置纪元,加1
(2)每个配置纪元下,每个主节点都可以进行一次投票
(3)从节点在发现主节点下线后,根据复制的偏移量大小来发起选举过程。保证偏移量大的,也就是延迟小的从节点优先发起选举过程, 注意是广播发送给集群类的所有主节点
(4)当主节点收到从节点发送的选举AUTH_REQUEST报文后,会给节点回复一个AUTH_ACK报文,这就表示当前主节点在当前纪元下,将自己的选票投给了这个从节点
(5)如果这个从节点收到了N/2 + 1的选票,则认为选举成功,自己晋升为主节点,复制原先主节点的槽,并广播通知集群内的其他主节点
(6)如果没有收到足够的选票,那么重新进行(1)开始的流程
1.10 Redis的一些常见优化操作
(1)不要使用bigKey
这个一般理解为value值不宜过大,比如好几M之类的,会造成网络阻塞,CPU占用率过大等问题
(2)可以使用批命令,MSET类似的
(3)多个不同数据类型的命令可以使用Pipeline,来减少网络开销
(4)部署的时候,如果不把Redis当作纯粹的存储来看,只作为降低DB压力的缓存来使用的化,就不要开启持久化了
1.11 Redis的主从复制原理
(1)主从节点会保存复制偏移量的值来确认当前主从节点数据是否一致
复制偏移量是根据执行命令的字节数来累加的
(2)在第一次主从节点建立连接后,从节点向主节点发送psync命令,复制偏移量的值是-1,主节点认为是第一次同步数据,则通过bgsave命令生成一份RDB文件发送给从节点,从节点解析RDB文件做一次全量复制
(3)后续如果主从断连再次连接的同步都是根据复制偏移量和运行Id (runId) 做的部分同步。主节点向从节点同步命令时,同时也会写入复制缓存冲中,后续部分复制时,如果偏移量相差较小,在复制缓冲池中都能找到,则直接发送,如果找不到,则再进行一次全量复制
(4)主从节点之间通过心跳检测来维系关系,发现是否复制偏移量有偏差, 如果有偏差则从复制缓冲池中获取命令重新发送给从节点,这个一点来解决部分网络命令丢失问题。
主要实现依赖三个重要参数,节点运行id(runId), 复制缓冲池(replication backlog), 复制积压缓冲区(replication offset)
1.12 Redis的线程模型
Redis线程模型是基于Reactor模式的文件事件处理器。
(1)文件事件处理器使用I/O多路复用(multiplexing)程序来同时监听多个套接字,并根据套接字当前的执行的任务来关联不同的事件处理器
(2)当被监听的套接字准备好执行时,与之相关联的事件处理器就会执行相应的逻辑
(3)如果文件事件并发的出现,则会将其放入一个队列中,每次处理完一个文件事件后,则从其中取下一个
1.13 本地缓存和集中式缓存的比较,简述一下各自的优缺点
本地缓存,存储在本地读写,数据快,但是如果是多机器部署会出现不一致的情况。此外,本地缓存没法大量写数据,受限于本进程内存。
集中式缓存,可以大量写,多个进程间数据是一致的,可靠高,性能要低于本机缓存,有网络开销等