redis学习笔记
问题一:项目中缓存是如何使用的?缓存如果使用不当会造成什么结果?
1.用缓存,主要有俩用途,高性能 和高并发,一般的中小型项目考虑 高并发
2.常见的缓存问题有以下三个:
-
缓存与数据库数据不一致
-
缓存雪崩
-
缓存穿透
-
缓存并发竞争
问题二:redis和memacached 有什么区别?Redis的线程模型是什么,为什么单线程的Redis比多线程的memacached效率要高得多?
redis 和 memacached的区别?
-
Redis支持的服务端的数据操作:Redis相比memacached来说,拥有更多的数据结构并支持更丰富的操作,通常在memacached中,你需要将数据拿到客户端来进行类似的修改在set回去,这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的get/set 一样高效。所以如果需要缓存能够支持更复杂的结构和操作,那么Redis会是不错的选择。
-
内存使用对比:使用简单的key -value 存储的话,memacached的内存利用率高;而Redis采用哈希结构来做key -value 存储,由于其组合式的压缩,其内村利用率要更高;
-
性能对比:由于redis是单核的,而memacached可以使用多核,所以平均每一个核上Redis在存储大量小数据时比memcached性能更高。而在100K以上数据,memcached性能比redis高;处理大数据能力 memacached 要比redis跟优秀一些
-
集群模式:memcached没有原生的集群模式,需要依靠客户端实现往集群中分片写入数据;但Redis原生支持cluster模式;
Redis是基于reactor模式开发的网络事件处理器,这个处理器叫做文件事件处理器。这个文件事件处理器,是单线程的,redis才叫做单线程的模型,采用多路IO复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器处理这个事件。
-
如果被监听的socket准备好执行 accept/read/write/close 等操作的时候,跟操作对应文件事件就会产生,这个时候文件事件处理器就会调用之前关联好的事件处理器来处理这个事件;
-
文件事件处理器是单线程模型,但是通过io多路复用机制监听多个socket,可以实现高性能网络通信,又可以跟内部其他单线程模块进行对接,保证了Redis模型内部的简单性;
-
文件事件处理器包括4个模块 :多个socket,IO多路复用程序,文件事件分派器,事件处理器(链接应答处理器、命令请求处理器、命令响应处理器 等等);
-
IO多路复用程序监听多个socket,并将socket放入队列中,每次从队列中取出socket 交给事件分派器,事件分派器把socket交给对应的事件处理器;
-
然后一个socket的事件处理完以后,IO多路复用器会将下一个socket 给事件分派器。文件事件分派器会根据当前的socket的当前事件,来选择对应的事件处理器来处理
-
为什么单线程的Redis比多线程的memacached效率要高得多?
-
redis是纯内存操作
-
核心是基于非阻塞的IO多路复用
-
单线程反而避免了多线程的上下文切换的问题
问题三:Redis都要哪些数据类型?分别在哪些场景中使用比较合适?
数据类型
-
String
-
hash
这个是类似于map的数据结构,可以将结构化的数据,比如对象给缓存起来,然后每次读写缓存的时候,可以就操作hash里的某个字段
key =150
value ={ "id"=150,"name"="fangtao","age"=20}
hash类型的数据可以用来存放对象,吧一些简单的对象给缓存起来,后续操作时,可以直接仅仅修改这个对象中的某个字段的值
-
list
有序列表,
如微博,某个大V的粉丝,就可以以list的形式缓存起来
-
set
无序集合,自动去重
如果你需要对数据进行全局去重,可以也基于JVM的hashset去重;
但是如果系统是部署在多台机器上,就可以用redis进行全局去重
可以基于set玩交集,并集,差集,比如交集,可以吧两个人的好友列表都整理到一个交集,看有多少共同的好友是哪些
-
sorted set
排序的set ,去重但可以排序,写进去给一个分数,自动根据分数排序;最大的特点是有个分数可以自定义排序规则
问题四:Redis的过期策略能介绍一下吗?手写一个LRU?
-
Redis的过期策略
-
给redis中的数据设置过期时间
redis如何对这批过期的key删除?
定期删除和惰性删除
所谓定期删除,Redis每隔100ms 会随机抽取一些设置了过期时间的key是否过期,如果过期则删除;
这里会有问题:假设 redis中放了10w个key,都设置了过期时间,那么每隔100MS就要去检查10w个key,那么Redis基本死了,CPU负载会很高,都消耗在检查过期key上,因此就出现了惰性删除策略;
所谓惰性删除,在获取一个key的时候,Redis会检查一下,如果这个key已经过期,就直接删除,不会返回人和结果,就惰性删除,并不是key到了过期时间就删除,而是客户端在get这个key的时候,Redis才会去检查并删除
通过两种策略结合起来,redis可以保证过期key一定会被删除
如果定期删除漏掉了很多过期key,然后你也没及时检查,也没有走惰性删除,此时会怎样?大量的key堆积在redis内存中,导致Redis内存消耗殆尽,从而Redis崩溃,怎么办?
-
内存淘汰
如果redis的内存占用过多的时候,此时会进行内存淘汰,有如下一些策略;
-
neoviction:当内存不足以容纳新的数据写入时,新写入数据会失败,这个基本不会用
-
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近使用的key(这个最常用)
-
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个一般没人用吧,为啥要随机,肯定是吧最近最少使用的key删除掉;
-
valotile-lru: 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key
-
valotile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
-
valotie-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期的key优先移除
手写一个LRU算法
可以参考下JDK中的linkedHashMap类来实现LRU算法
问题五:如何保证redis的高并发,高可用?redis的主从复制原理是什么?Redis的哨兵原理能介绍一下吗?
1.redis如何保证高并发?
读写分离,一般来说,对缓存,都是用来支撑读高并发的,写的请求是较少的;
架构做成主从架构,一主多从,主节点上Redis负责处理写请求,并将数据同步复制到其他的从节点上redis,从节点负责处理读请求。所有的读请求全部走从节点。
如果要提高并发请求处理能力,可以水平扩容,就可以应对高QPS 的场景
2.理解redis replication的原理
3.redis replication 的核心机制
-
redis采用异步方式复制数据到slave节点;在Redis 启动以后,slave node会周期性地确认自己每次复制的数据量
-
一个master node 是可以配置多个slave node;
-
每个slave node 在复制数据的时候,是不会 block master node 的正常工作的
-
slave node 在做复制的时候,也不会中断自己的查询工作;他会用旧的数据集来提供服务;但复制完成以后,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了
-
slave node 主要用来做横向扩容,做读写分离,扩容的slave node 可以提高读的吞吐量
slave node 跟高可用性有很大的关系
4.master 持久化对于主从架构的安全保障的意义
如果采用了主从架构,那么必须开启 master node 持久化!
不建议用slave node作为 master node 的数据备份,因为那样的话,如果关掉master的持久化,可能在master宕机重启后,数据为空,然后经过数据复制到slave node ,导致slave node 数据全部清空,最终造成数据丢失
-
-
如果这个 slave node是重新连接到master node ,那么master node仅仅复制给slave node缺少的数据
-
否则如果这个slave node是第一次连接 master node,那么 master node 会发送full resynchronization
-
开始full resynchronization 的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完后,master会将这个RDB文件发送给slave node,slave node会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给 slave node,slave也会同步这些数据
-
slave node 如果跟master node 有网络故障,断开了链接,会自动重连。master如果发现有多个slave node 都来重新连接,仅仅会启动一个 RDB save操作,用一份数据服务所有slave node
-
从Redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制
-
master node会在内存中创建个backlog;master和slave都会保存 replica offset 还有一个master id ,offset 就是保存在backlog 中,如果master和slave网络连接断掉了,slave会让master 从上次的replica offset 开始继续复制
-
但是如果没有找到replica offset ,那么将会进行一次resynchronization , 即重新复制
7.无磁盘化复制
master 在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了
repl-diskless-sync
repl-diskless-sync-delay ,等待一段时间后再复制,因为要等更多的slave重新连接过来
8.过期key处理
slave不会过期key,只会等待master 过期key;如果master node 要过期一个key 或者通过 lru 淘汰一个key,那么会模拟一条del命令发送给slave
9.redis主从复制完整流程
-
-
slave node 内部有个定时任务,每秒检查是否有新的master node 的信息需要复制,如果有,就创建一个socket建立与mastern node 的网络连接
-
slave node 发送一个ping命令给master node
-
口令认证,如果master node设置了requirepass,那么slave node 必须发送masterauto的口令进行认证
-
master node第一次执行全量复制,将所有数据发送给slave node
-
master node 后续持续将写命令,异步复制给slave node
10.数据同步相关的核心机制
指的就是第一次slave node 去链接master node,进行全量复制的一些细节的机制
-
master 和 slave 都会维护一个offset
master 个slave 都会自身不断累加offset
slave 每秒都会上报自己的offset给master,同时master 保存每个slave的offset
==offset 的作用是,主要是让slave 和master 知道互相数据不一致的情况==
-
backlog
master node 有一个backlog,默认大小是1MB
master node 在给slave node复制数据的时候,也会将数据在backlog中同步一份
backlog 主要是用于做全量复制中断重连后的增量复制
-
master run id
客户端通过 info server 命令,可以看到 master run id
如果根据 host+IP定位一个master node,是不靠谱的,如果master node 重启或数据发生变化,那么slave node 应该根据不同的run id 区分,run id 不同就做全量复制
-
psync
从节点使用psync 从master node进行复制, psync runid offset
master node 会根据自身的情况返回响应消息,可能是fullResync runid offset 触发全量复制,也可能是continue 触发增量复制
11.全量复制
-
master 执行bgsave ,在本地生成一份RDB快照文件
-
master node 将rdb快照文件发送给slave node,如果rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可以适当调大这个参数
-
对于千兆网卡的机器,一般每秒传100M,6G文件,很可能就超过60秒
-
master node 在生成rdb时,也会将所有新的写命令缓存在内存中,在slave node保存了rdb以后,再将新的写命令复制给slave node
-
如果在复制过程中,内存缓冲区消耗持续超过64MB,或者一次超过256MB,那么停止复制,复制失败
-
slave node 接收到rdb后,清空自己的旧数据,然后重新加载rdb到自己内存中,同时基于旧的数据版本对外提供服务
-
如果 slave node 开启了AOF,那么会立即执行BGREWRIEAOF,重写AOF
rdb生成、rdb通过网络拷贝、slave旧数据的清理、slave aof rewrite,很耗费时间
如果复制的数据量在4G~6G,那么很可能全量复制时间消耗到1分半到2分钟
12.增量复制
-
如果全量复制过程中,出现master-slave 网络中断,那么slave 重新连接到master 以后,会触发增量复制
-
master 直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB
-
master 根据slave 发送的psync中的offset来从backlog中获取数据
13.heartbeat
主从节点互相都会发送heartbeat信息(心跳)
master默认每隔10秒发送一次heartbeat,slave 每隔1秒发送一次heartbeat
14.异步复制
master每次接收到复制命令时,先在内部写入数据,在异步把数据发送给slave
15.Redis的高可用性问题
首先来理解Redis的不可用性:
Redis的高可用架构
1.哨兵的介绍
sentinal ,也叫哨兵
哨兵是redis集群中非常重要 的一个组件,主要功能如下
-
集群监控,监控集群中的master node 和slave node进程是否正常工作
-
消息通知,如果集群中某个Redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
-
故障转移, 如果master node 挂掉了,会在集群中选举一个slave node作为master node
-
配置中心,如果故障转移发生了,通知client新的master地址
哨兵本身也是分布式的,作为一个哨兵集群去运行,互相协调工作
-
故障转移时,判断一个master node是宕机了,需要大部分哨兵同意才行,设计到分布式选举的问题
-
即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就是很坑爹了
2.哨兵的核心知识
-
哨兵至少需要3个实例,来保证自己的健壮性
-
哨兵+Redis主从的部署架构,不能保证redis 0数据丢失,只能保证Redis集群的高可用
3.为什么redis哨兵集群只有两个节点无法工作
哨兵集群必须部署2个以上节点
如果哨兵集群仅仅部署了2个哨兵实例 quorum =1
-
master宕机,哨兵1 和哨兵2 只要有一个哨兵认为master宕机就可以进行切换,同时哨兵1和哨兵2 会选取一个来执行故障转移
-
同时这个时候,需要majorty,也就是大多数哨兵都运行,2个哨兵的majorty 就是2,2个哨兵都运行着,就可以允许执行故障转移
-
但是如果一个master和一个哨兵运行的机器宕机了,那么哨兵只有1个了,此时就没有majority 来允许执行故障转移,虽然另外一台机器还有一个slave node ,但是不能执行故障转移;
Redis 哨兵+主从部署架构造成数据丢失的两种情况
主备切换的时候,可能会造成数据丢失
-
异步复制,导致数据的丢失
因为master -> slave 的复制是异步的,所以可能有部分数据还没复制到slave ,master主机就宕机了,此时这些部分数据就丢失了。
-
脑裂导致的数据丢失
-
也就是说,某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着,此时哨兵就可能认为master宕机了,然后开始选举,将其他slave切换成master
-
这个时候,集群里就会有两个master node,这就是所谓的脑裂
-
此时虽然某个slave切换成了master,但是可能client还没来得及切换到新的master,还继续写旧的master的数据可能也丢失了,因此旧master再次恢复的时候,会被作为一个slave挂到master上去,自己的数据清空,重新从新的master复制数据
-
3.解决异步复制和脑裂导致的数据丢失
min-slaves-to-write 1
min-slaves-max-lag 10
要求至少有一个slave,数据复制和同步的延迟不能超过10秒
如果说一旦所有的slave,数据复制和同步的延迟都超过10秒,那么这个时候,master就不会接受任何请求
上面这个两个配置可以减少 异步复制和脑裂导致的数据丢失
-
减少异步复制的数据丢失
有了min-slaves-max-lag 这个配置,就可说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多,那么就拒绝请求,这样可以吧master宕机时由于部分数据未同步到slave导致数据丢失的损失降到最低
-
减少脑壳的数据丢失
如果一个master node 出现脑裂,跟其他slave丢了链接,那么上面那个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave 超过10秒没有给自己ack信息,那么就直接拒绝客户端的请求
这样脑裂后的旧的master不会接受client 的新数据,也就避免了数据丢失
哨兵底层概念理解
-
sdown和odown 转换机制
sdown和odown两种失败状态
-
sdown是主观宕机,就一个哨兵如果自己觉得一个master宕机了,那么就是主观宕机
-
odown是客观宕机,如果quonum数量的哨兵都觉得一个master宕机了,那么就是客观宕机
-
如果一个哨兵在指定时间内,收到了quorum指定数量的其他哨兵也认为那个master是sdown了,那么就是odown了,客观认为master宕机了
2.哨兵集群的自动发现机制
-
哨兵互相之间的发现,是通过redis的 pub/sub系统实现的,每个哨兵都会往 一个特定的channel里面发送一个消息,这时候所有其他的哨兵都能收到这个消息,并感知到其他哨兵的存在
-
每隔2秒钟,每隔哨兵都会往自己监控的某个 master+slaves 对应的 channel里发送一个消息,内容是自己的host/IP和run id 还有对这个master的监控配置
-
每个哨兵也会去监控每个 master+slaves对应的channel,然后去感知同样在监听这个master+slaves的其他哨兵的存在
-
每隔哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步
3.slave 配置的自动纠正
哨兵会复制纠正slave的一些配置,比如该slave要称为候选的master,哨兵会确保slave复制现在master数据;如果slave链接到了一个错误master上,比如故障转移
4.slave ->master的选举
如果一个master被认为是odown了,而majority 哨兵都允许了准备切换,那么这个哨兵就会执行准备切换操作,此时首先选举一个slave来 会考虑了slave 的一些信息
-
跟master断开的时长
-
slave 优先级
-
复制offset
-
run id
如果一个slave 跟master断开链接已经超过了 down-after-millseconds的10倍,外加master宕机的时长,那么slave 就认为不适合选举为master
接下来会对slave 进行排序
1.按照slave优先级进行排序,slave priority
2.如果slave priority相同,那么看replica offset ,哪个slave复制的数据越多,offset越靠后,优先级就越高
3.如果上面两个条件都相同,那么选择一个run id 比较小的那个slave
-
-
quorum和majority
-
每次哨兵要做主备切换时,首先要quorum数量的哨兵都sdown以后才能是odown;然后选举除一个哨兵进行主备切换,这个哨兵还需要得到majority数量的哨兵的授权,才能开始主备切换操作
-
如果 quorum < majority ,比如5个哨兵,majority是3,quorum是2,那么只要3个哨兵授权就可以切换
-
如果 quorum >=majority ,比如5个哨兵,quorum为5,那么必须5个哨兵都同意授权,才能进行切换
3.configration epoch
哨兵会对一套 master+slave进行监控,有相应的监控配置
-
执行切换的那个哨兵,会从要切换到的新master(slave ->master) 哪里得到configration epoch,这就是一个version号,每次切换的version好都必须唯一
-
如果第一个选举出的哨兵在执行切换失败后,那么其他哨兵,会等待failover-time 时间,然后继续接替执行切换,此时会重新获取一个configration epoch,作为新的version号
4.configration 传播
-
哨兵切换完成后,会在本地更新生成host配置,并通过 redis的pub/sub 消息机制。同步给其他哨兵;
-
这里之前的的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成一次新的切换后,新的master配置是跟着新的version号的
-
其他的哨兵都是根据版本号的大小来更新自己的master 配置
总结
redis高并发,高可用的面试回答
-
redis高并发:主从架构,一主多从;单主用来写入数据,一般能达到几w的QPS,多从用来读数据,一般能达到10几w的QPS
-
redis 高并发的同时,还需要容纳海量数据的场景, 那就需要redis集群,能达到每秒几十万的读写并发
-
redis的高可用,如果是做了主从架构,那么就加上哨兵就可以实现了,任意一台实例宕机自动进行主备切换。
问题:怎么保证redis挂掉以后还能进行数据恢复
redis持久化方式有哪些?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的
前言:redis如果仅仅是将缓存数据放到内存里,那么当redis宕机以后,数据也就丢失了,所以在redis接收到写命令去写缓存数据到内存后,应该异步地将数据写入到本地磁盘里,这样当重启redis后,才能自动从磁盘文件中同步数据到内存中
1.RDB和AOF两种机制的介绍
-
RDB持久化机制,对redis中的数据执行周期性的持久化
-
AOF机制对每条命令作为日志,以append-only的模式写入一个日志文件中,在redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集
-
如果我们只想吧redis 仅仅作为纯内存的缓存来用,可以禁用RDB和AOF所有持久化机制
-
如果同时使用RDB和AOF机制,那么在redis重启的时候,会使用AOF机制重新构建数据,因为AOF中的数据更加完整
-
通过RDB和AOF,都可以将redis内存中的数据持久化到磁盘上,然后可以将这部分数据备份到别的地方去
图解:
2.RDB持久化机制的优点
-
RDB会生成多个数据文件,每个数据文件都代表某一时刻中redis的数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程的安全存储上,如阿里云,以预定好的备份策略来定期备份redis中的数据
-
RDB对redis对外提供的读写服务,影响非常小,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可
-
相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复redis进程,更加快速
AOF,存放的是指令日志,做数据恢复时,其实是要回放和执行所有的指令日志,来恢复出来内存的数据
RDB,就是一份数据文件,恢复的时候,直接加载到内存中即可
RDB可以保证redis的性能更好,每次写都是写redis内存的,只在一定的时候,才会将数据写入磁盘中
AOF 每次都是要写入文件,虽然每次都是先写入os cache 中,但是还有一定的时间开销,速度较慢
3.RDB持久化机制的缺点
-
如果想要在redis故障时,尽可能少的丢失数据,那么RDB没有AOF好。一般来说,RDB数据快照文件,每5分钟生成一份,这个时候一旦redis 进程宕机了,那么会丢失最近5分钟的数据
-
RDB每次在Fock子进程去执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停(一般不要让RDB间隔太长,否则每次生成的RDB文件太大了,导致服务暂停较久)
4.AOF持久化机制的优点
-
AOF可以更好的保证redis的数据不被丢失,一般AOF会每隔1秒,通过一个后台线程去执行fsync操作,最多失去1秒钟的数据
-
AOF日志文件以append-only模式写入,所以就没有磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也易恢复
-
AOF日志文件即使过大,出现后台的rewrite操作,但不会印象客户端的读写。因为在rewrite log的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的小日志出来,在创建日志文件的时候,老的日志文件还是照常写。当新的日志merge后的日志文件ready的时候,在交换新老日志文件即可。
-
AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性删除的紧急恢复。比如某个人不小心用flushall 命令清空了所有数据,只有这个时候rewrite操作没有执行,就把当前的AOF文件拷贝出来,删除掉最后的flushall命令,然后在将AOF文件放回去,就可以恢复数据了
5.AOF持久化机制的缺点
-
对于同一份数据来说,AOF日志文件要比RDB文件大的多
-
AOF开启后,支持写QPS会比RDB的QPS低,因为AOF 一般会配置成每秒fsync一份文件,当然,每秒一次fsync,性能还是很高的
-
如果要保证一条数据都不丢,也可以,就是AOF的fsync设置成每写入一条数据,就执行一次fsync,但那样的话,redis的QPS将会大幅度降低
-
唯一比较大的缺点是,做数据恢复的时候比较慢,还有做冷备(定期的备份),可能自己要手写复杂的脚本
-
6.RDB和AOF应该如何取舍
不要仅仅使用RDB,那样可能丢失很多数据(AOF记录的是指令的日志,RDB是数据快照)
也不要仅仅使用AOF,因为那样有两个问题,第一,通过AOF做冷备,没有RDB做冷备,来得恢复速度快;第二,RDB每次都生成的是数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug
综合使用RDB和AOF持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择;用RDB来做不同程度的冷备,在AOF文件丢失或破损不可用的时候,还可以使用RDB来进行快速的数据恢复;
问题7:redis集群模式的工作原理?在集群模式下,redis中的key是如何寻址的?分布式寻址都有哪些算法?了解一致性hash算法吗?
redis原生支持的redis集群模式,可以做到多台机器上,部署多个redis实例,每个实例存储一部分数据,同时每个redis实例可以挂redis从实例,自动确保,如果redis主实例挂了,会自动切换到redis从实例上来
redis cluster
-
自动将数据进行分片,每个master上放一部分数据
-
提供内置的高可用支持,部分master不可用时,还是可以继续使用
-
在redis cluster架构下,每个redis要开放2个端口,比如 一个是6379,另一个加10000的端口号,比如 16379
16379端口用来进行节点间的通信,也就是集群总线。cluster bus 的通信,用来进行故障检测,配置更新,故障转移授权; cluster bus(集群总线) 用了另外一种二进制协议,主要用于节点间高效的数据交换,占用更少的网络宽带和处理时间。
一致性hash算法(自动缓存迁移) +虚拟节点(自动均衡负载)
redis cluster 的 hash slot算法
-
redis cluster有16384个 hash slot,对每个 key 计算CRC16值,然后对16384取模,可以获取key对应hash slot;
-
redis cluster 中每个master都会持有部分slot,比如3个master,那么可能每个master持有5000多个hash slot;
-
hash slot 让node的增加和移除很简单,增加一个master,就将其他master 的hash slot 移动部分过去,减少一个master ,就将它的hash slot 移动到其他master上去
-
移动 hash slot 的成本是很低的
-
客户端的API,可以对指定的数据,让他们走同一个hash slot,通过hash tag 来实现
Master 节点间的通信机制
1.基础通信原理
-
redis cluster节点间采用 gossip协议进行通信
跟集中式不同,不是将集群元数据(节点数据,故障,等等) 集中存储在某个节点上,而是互相之间不断通信,保持整个集群中所有节点数据时完整的
-
10000端口
每个节点都开放一个专门用于节点通信的端口,
了解什么是缓存雪崩?redis崩溃之后会怎么样?系统应该如何应对这种情况?如何处理redis的 穿透?
1.缓存雪崩
由于系统中缓存层的组件宕机了,进而导致大量的请求涌入数据库,导致数据库负载过高而崩溃,即便数据库重启也会因为请求数量太多而立马崩溃,最终系统不可用
2.缓存雪崩的解决方案
事前:redis采用主从架构+ redis 哨兵保证redis集群的高可用性,避免全盘崩溃
事中:本地ehcache 缓存 +hystrix 限流&降级 ,避免mysql崩溃
事后:redis必须开启持久化,快速恢复缓存数据
3.缓存穿透以及解决方案
如何保证缓存与数据库的双写一致性?
最经典的缓存+数据库读写的模式: cache aside pattern
-
cache aside pattern
-
读的时候先读缓存,缓存没有的话在读数据库,然后取出数据后放入缓存
-
更新的时候,先删除缓存,再更新数据库
-
-
为什么是删除缓存,而不是更新缓存
因为很多时候,复杂点的缓存的场景,因为缓存有的时候,不简单是数据库中直接取出的值
比如更新一个表的某个字段,然后对应其缓存,是需要查询另外两个表的数据,并进行运算,才能计算出缓存最新的值
更新缓存的代价是很高的,如果频繁去修改一个缓存设计的表,那么这个缓存会被频繁更新
问题是,这个缓存到的会不会被频繁访问到呢?
举个例子,一个缓存涉及的表的字段,在一分钟内就修改了20次,那么缓存更新20次,但是这个缓存在一分钟就被读取了一次,实际上,如果只是删除缓存的话,那么1分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。
其实删除缓存,而不是更新缓存,就是一个lazy计算额思想,
不要每次都重复做复杂的计算,不管他会不会用到;而是让它需要被使用的时候再去重新计算
最初级的缓存数据库不一致的解决方案
问题:先修改数据,在删除数据,如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据不一致
解决思路: 先删除缓存,再修改数据库;如果删除缓存成功了,修改数据库失败了,那么数据库中的是旧数据,缓存中的时空的,数据不会不一致;因为读的时候缓存并没有,则读数据库中的旧数据,并更新到缓存中