Java面试专题-redis篇集群和IO多路复用等
三、其他面试问题-集群
1.主从复制、主从同步流程
-
redis集群有哪些方案,知道嘛?
在Redis中提供的集群方案总共有三种
● 主从复制
● 哨兵模式
● 分片集群
1.redis主从数据同步的流程是什么?
2.怎么保证redis的高并发高可用?
3.你们使用redis是单点还是集群,哪种集群?
4.Redis分片集群中数据是怎么存储和读取的?
5.Redis集群脑裂,该怎么解决呢?
1.主从复制
单节点redis的并发能力是有上限的,要进一步提高redis的并发能力,就需要搭建主从集群,实现读写分离。
2.主从数据同步原理:
(1)主从全量同步:
-
Replication ld:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid
-
offset 偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。
要是replid不一致,则说明是第一次同步;
若一致,则不是第一次同步,则它不会再生成RDB文件,而是根据偏移量进行同步。
(2)主从增量同步(slave重启或后期数据变化)
总结
- 介绍一下redis的主从同步
单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据。
- 能说一下,主从同步数据的流程
全量同步:
1.从节点请求主节点同步数据(replication id、offset) 2.主节点判断是否是第一次请求,是第一次就与从节点同步版本信息(replication id和offset) 3.主节点执行bgsave,生成RDB文件后,发送给从节点去执行 4.在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)
5.把生成之后的命令日志文件发送给从节点进行同步
增量同步:
1.从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值
2.主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步
2.哨兵模式、集群脑裂
1.哨兵的作用
redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:、
- 监控:Sentinel会不断检查您的master和slave是否按预期工作
- 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
- 通知:Sentinel充当redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给redis的客户端(哨兵通知客户端新的主节点然后继续工作)
2.服务状态监控
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
-
主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
-
客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
哨兵选主规则
- 首先判断主与从节点断开时间长短,如超过指定值就排该从节点(断开的时间越长从节点丢失的数据就越多)
- 然后判断从节点的slave-priority值,越小优先级越高
- 如果slave-prority一样,则判断slave节点的offset值,越大优先级越高(offset值越大表示当前这个从节点比较接近主节点的数据)
- 最后是判断slave节点的运行id大小,越小优先级越高。
3.redis集群(哨兵模式)脑裂
正常主从架构,配合哨兵模式:
由于网络原因,主节点和哨兵处于不同网络分区,那么哨兵按照哨兵选主规则再选择一个主节点,这时候出现了两个master
但是客户端还会给老master写数据,网络恢复后,哨兵会将老节点强制降低为子节点,那么它就会从master中同步数据,那么老节点就会把自己的数据清空,导致数据丢失
解决:
redis中有两个配置参数:
- min-replicas-to-write 1 表示最少的salve节点为1个
- min-replicas-max-lag 5 表示数据复制和同步的延迟不能超过5秒
原理:配置俩个参数后,如果发生脑裂,原主节点就会满足不了这俩个参数,原主节点拒绝旧客户端写请求,等待被降级
总结
- 怎么保证Redis的高并发高可用
哨兵模式:实现主从集群的自动故障恢复(监控、自动故障恢复、通知)
- 你们使用redis是单点还是集群,哪种集群
主从(1主1从)+哨兵就可以了。单节点不超过10G内存,如果Redis内存不足则可以给不同服务分配独立的Redis主从节点
- redis集群脑裂,该怎么解决呢?
集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在老的主节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这时再从新master同步数据,就会导致数据丢失
解决:我们可以修改redis的配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失
3.分片集群、数据读写规则
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
-
海量数据存储问题
-
高并发写的问题
使用分片集群可以解决上述问题,分片集群特征:
-
集群中有多个master,每个master保存不同数据(高并发写)
-
每个master都可以有多个slave节点(高并发读)
-
master之间通过ping监测彼此健康状态
-
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
分片集群结构-数据读写
redis分片集群引入了哈希槽的概念,redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每一个节点负责一部分hash槽。
数据读写流程:
{aaa}有效部分,比如相同的业务数据都想存储在同一个的redis节点下,可设置相同的有效部分
总结
-
redis的分片集群有什么作用
-
集群中有多个master,每个master保存不同数据
-
每个master都可以有多个slave节点
-
master之间通过ping监测彼此健康状态
-
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
-
Redis分片集群中数据是怎么存储和读取的?
-
Redis分片集群引入了哈希槽的概念,Redis集群有16384个哈希槽
-
将16384个插槽分配到不同的实例
-
读写数据:根据key的有效部分计算哈希值,对16384取余(**有效部分,**如果key前面有大括号,大括号的内容就是有效部分,如果没有,则以key本身做为有效部分)余数做为插槽,寻找插槽所在的实例
4.redis是单线程的,那为什么还那么快?
-
1.Redis是单线程的,但是为什么还那么快?
-
Redis是纯内存操作,执行速度非常快
-
采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
-
使用I/O多路复用模型,非阻塞IO
-
2.能解释一下I/O多路复用模型?
Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型主要就 是实现了高效的网络请求
-
用户空间和内核空间
-
常见的IO模型
-
阻塞IO(Blocking IO)
-
非阻塞IO(Nonblocking IO)
-
I0多路复用(IO Multiplexing)
-
-
Redis网络模型
1.用户空间和内核空间
-
Linux系统中一个进程使用的内存情况划分两部分:内核空间、用户空间
-
用户空间只能执行受限的命令(Ring3),而且不能直接调用系统资源必须通过内核提供的接口来访问
-
内核空间可以执行特权命令(Ring0),调用一切系统资源
Linux系统为了提高IO效率,会在用户空间和内核空间都加入缓冲区:
- 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写入设备
- 读数据时,要从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区
2.IO模型
(1)阻塞IO
顾名思义,阻塞IO就是两个阶段都必须阻塞等待:
阶段一:
- 用户进程尝试读取数据(比如网卡数据)
- 此时数据尚未到达,内核需要等待数据
- 此时用户进程也处于阻塞状态
阶段二:
- 数据到达并拷贝到内核缓冲区,代表已就绪
- 将内核数据拷贝到用户缓冲区
- 拷贝过程中,用户进程依然阻塞等待
- 拷贝完成,用户进程解除阻塞,处理数据
可以看到,阻塞IO模型中,用户进程在两个阶段都是阻塞状态。
(2)非阻塞IO
顾名思义,非阻塞IO的recvfrom操作会立即返回结果而不是阻塞用户进程。
阶段一:
- 用户进程尝试读取数据(比如网卡数据)
- 此时数据尚未到达,内核需要等待数据
- 返回异常给用户进程
- 用户进程拿到error后,再次尝试读取
- 循环往复,直到数据就绪
阶段二:
- 将内核数据拷贝到用户缓冲区
- 拷贝过程中,用户进程依然阻塞等待
- 拷贝完成,用户进程解除阻塞,处理数据
可以看到,非阻塞IO模型中,用户进程在第一个阶段是非阻塞,第二个阶段是阻塞状态。虽然是非阻塞,但性能并没有得到提高。而且忙等机制会导致CPU空转,CPU使用率暴增。
(3)IO多路复用
IO多路复用是利用单个线程来同时监听多个socket,并在某个socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。(比前两个性能好)
阶段一:
-
用户进程调用select,指定要监听的Socket集合
-
内核监听对应的多个socket
-
任意一个或多个socket数据就绪则返回readable
-
此过程中用户进程阻塞
阶段二:
- 用户进程找到就绪的socket
- 依次调用recvfrom读取数据
- 内核将数据拷贝到用户空间
- 用户进程处理数据
不过监听socket的方式、通知的方式又有多种实现,常见的有:
-
select
-
poll
-
epoll
差异:
-
select和poll只会通知用户进程有Socket就绪,但不确定具体是哪个Socket,需要用户进程逐个遍历Socket来确认
-
epoll则会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间
-
3.redis网络模型
Redis通过IO多路复用来提高网络性能,并且能支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库
- 能解释一下I/O多路复用模型?
- 1/O多路复用
是指利用单个线程来同时监听多个Socket,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,它会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。
- Redis网络模型
就是使用I/O多路复用结合事件的处理器来应对多个Socket请求
-
连接应答处理器
-
命令回复处理器,在Redis6.0之后,为了提升更好的性能,使用了多线程来处理回复事件
-
命令请求处理器,在Redis6.0之后,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程
开始看黑马Java面试,27届的,大家有什么建议欢迎来说哟