1.面试题
1.1.Redis主从复制
一般Redis集群有主从、哨兵、分片。单点Redis的并发能力是有上限的,要进一步提高Redis 的并发能力,就需要搭建主从集群,实现读写分离。
1.1.1.主从全量同步
Replication ID:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid
offset:偏移量,随着记录再repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果salve的offset小于master的offset,说明slave数据落后于master,需要更新
全量同步的执行步骤
- 从节点发起请求给主节点想要同步数据
- 主节点判断是否是第一次请求(通过查看id的方式),如果是第一次请求则全量同步
- 主节点执行bgsave方法生成RDB文件发送给从节点去执行
- 主节点再记录RDB期间,接收的其他命令会存到repl_baklog文件中,然后再返回给从节点去执行
1.1.2.增量同步
主从增量同步(slave重启或后期数据变化)
增量同步的执行步骤
- 从节点发起同步请求,主节点判断是否为第一次,是第一次返回id和offset,不是第一次返回continue,并且获取到从节点的offset值,主节点从repl_baklog日志文件中获取offset之后的数据同步到从节点。
面试题
Redis集群有那些方案?
介绍一下redis的主从同步
能说一下,主从同步数据的流程
答案
1.2.Redis哨兵模式
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:
- 监控:Sentinel会不断检查master和slave是否按预期工作
- 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
- 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端
1.2.1.哨兵服务状态监控
Sentinel基于心跳机制检测服务状态,每隔1秒向集群的每一个实例发送ping命令:
- 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
- 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
哨兵选主规则
- 首先判断主于从节点断开时间长短,如超过指定值就派该从节点
- 然后判断从节点的slave-priority值,值越小优先级越高
- 如果slave-priority一样,则判断slave节点的offset值,值越大优先级越高
- 最后是判断slave节点的运行id大小,越小优先级越高。
1.2.2.哨兵模式脑裂
由于网络原因,主(master)节点和Sentinel处于不同的网络分区,Sentinel只能检测从节点,此时Sentinel会按照选主的规则选出一个满足条件的从(slave)节点作为新主(master)节点。但是老的主(master)节点还在接收客户端的消息,但是新主(master)节点还没有与客户端建立连接,下图
当网络恢复的时候,Sentinel会将老的主(master)节点降为从(slave)节点,这个时候这个刚刚转变为slave的从节点就会去master请求数据,在此期间它会清空自己的数据,导致在脑裂之前客户端向这个节点写入的数据就被删除了,最终会造成数据丢失
min-replicas-to-write 1
当客户端连接主节点写数据的时候,这个主节点必须要有一个从节点才行,否则直接拒绝请求
min-replicas-max-lag 5
配置主从数据复制和同步的延迟不能超过5秒。
面试题
怎么保证Redis的高并发高可用
你们使用redis是单点还是集群,那种集群
redis的集群脑裂,如何解决
答案
2.算法题
2.1.分析:
我们得到的是一个顺序递增数组省去了排序,在数组中选择两个数的和为目标值此时有两种方式可以选择。
-
暴力递归
- 通过两个for循环把每行每列的数字都跑一遍,如果当num[i]+num[j]大于目标值,这个时候就没必要执行j后面的值了,直接break跳出j的循环,i++下一轮
- 当它等于目标值的时候还要注意题目要求不能是两个索引相同的数的和比如{2,3,3,5,9} target=6,那么你的结果就只能是[2,3]而不是[2,2]或[3,3],所以j的值等于i+1这样就不可能会有相同值了。
- 最后答案加个一,下表是从一开始的
但是这个方法不推荐基本达到了O(n平方)的时间复杂度了,
public int[] twoSum(int[] numbers, int target) { int n=numbers.length; //由于是递增数组所以num[i]+num[j]>target则num[i]+num[j+1]>target //如果 num[i]+num[j]<target num[i-1]+num[j]<target for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ if(numbers[i]+numbers[j]>target){ break; } if(numbers[i]+numbers[j]==target){ return new int[]{i+1,j+1}; } } } //因为方法的泛型是int[]所以即使找到了结果,最后还是返回个空数据 return new int[0]; }
-
双指针
- 再初始状态下,令left指向数组第一个元素,right指向最后一个元素
- 进入循环,控制循环退出条件为left<right
- 再每次循环中,如果left与right的数字之和等于所给target,则返回当前索引值
- 若left与right的数字之和小于所给target,left++
- 若left与right的数字之和大于所给target,right–
时间复杂度:O(n),空间复杂度:O(1)。
public int[] twoSum(int[] numbers, int target) { int n=numbers.length; int left=0; int right=n-1; while(left<right){ if(numbers[left]+numbers[right]==target){ return new int[]{left+1,right+1}; } if(numbers[left]+numbers[right]>target){ right--; } if(numbers[left]+numbers[right]<target){ left++; } } return new int[0]; }
2.2.使用技术重点:
双重for循环的暴力递归,双指针利用题目已经递增排好顺序的特征,左右相加来缩小范围,当然这个题使用二分查找应该是最快的因为这道题面对的数据长度和大小都是很多的,感兴趣可以上网搜二分查找
-1000 <= numbers[i] <= 1000
2 <= numbers.length <= 3 * 104