文章目录
- 前言
- 一、你们项目中使用Redis 作为分布式锁吗
- 1.1 将余券存入到Redis 中,当有人抢券进行-1操作并存回:
- 1.2 分布式锁的使用
- 1.3 Redis 分布式锁是怎么实现的:
- 1.4 Redisson 分布式锁是可重入的吗:
- 1.5 Redis 出现脑裂时如何保证数据的一致性
- 二、你都知道Redis 集群的哪些部署方式
- 2.1 主从部署
- 2.1.2 主从架构图:
- 2.1.2 主从数据的同步是怎么做的:
- 2.2 哨兵模式:
- 2.2.1 哨兵模式架构:
- 2.2.2 Sentinel的选举:
- 2.2.3 哨兵模式下的脑裂问题:
- 2.3 分片模式:
- 2.3.1 分片架构:
- 2.3.2 分片是怎么实现的:
- 三、Redis 单线程为什么还能保证高吞吐
- 3.1 Redis 高性能的三个原因:
- 3.2 Redis的线程网络模型:
- 3.2.1 单线程模型:
- 3.2.2 多线程模型:
- 3.3 Redis 的IO多路复用:
- 总结
前言
你们项目中使用过Redis 作为分布式锁吗,你们是怎么实现的,锁过期怎么办,redis 脑裂怎么办,Redis 真能保证一致性吗,本文重点对面试的问题进行介绍,祝愿每位程序员都能上岸!!!。
一、你们项目中使用Redis 作为分布式锁吗
分布式锁通常被用在对公用的资源进行修改时使用,如分布式任务调度,抢单等场景;
1.1 将余券存入到Redis 中,当有人抢券进行-1操作并存回:
如果按照上面的代码进行,在并发场景下很有可能出现超卖的情况:
此时就可以考虑使用分布式锁进行加锁处理超卖的问题;
1.2 分布式锁的使用
每个线程进入该方法后,都需要先获取到一把全局的排他锁,只有获取到了锁的线程才能进入优惠券余量的查询,然后扣减库存操作,最后在进行锁的释放,获取不到锁的线程则无法抢券。
1.3 Redis 分布式锁是怎么实现的:
Redis实现分布式锁主要利用Redis的setnx命令。setnx是SETif not exists(如果不存在,则 SET)的简写。
为了防止死锁,通常在使用setnx 时,需要为key 设置一个过期时间,那么锁的过期时间又应该怎么设置; Redisson中提供了看门狗功能,
来进行锁的自动延期。看门狗是一个线程,默认每隔10s 去查看主线程的任务是否结束,如果没有结束就进行所得续期,
依次来保证主线程在任务执行完成之前一直持有锁。
这里注意一个使用上的细节:
加锁和设置过期时间,都是通过lua 脚本保证其原子性;这里如果传入了锁的过期时间,则不会启用看门狗进行锁的续期 ;
1.4 Redisson 分布式锁是可重入的吗:
Redisson 实现的分布式锁 底层数据结构,保存了当前持有锁的线程,和锁的重入次数,所以它是支持锁的重入;
1.5 Redis 出现脑裂时如何保证数据的一致性
redis 分布式锁-数据主从一致性问题:主节点还没有将锁的key 信息同步到从节点,此时主节点宕机后,通过选举上位的主节点没有锁信息,此时线程在来获取锁就成功了,丢失了所的互斥性。针对此场景Redis 提供了红锁来支持
RedLock(红锁):不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁(n/2+1),避免在一个redis实例上加锁;红锁实现起来比较复杂,而且并发性能差,redis 官网也不推荐使用。
Redisson锁不能解决主从数据一致的问题吗
不能解决,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了,如果业务中非要保证数据的强一致性,建议采用zookeeper实现的分布式锁;
二、你都知道Redis 集群的哪些部署方式
2.1 主从部署
一个Master 多个Slave,Master 负责数据的写入,Slave 可以负责数据的读取;单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。
2.1.2 主从架构图:
2.1.2 主从数据的同步是怎么做的:
1)全量同步过程:
- 从节点与主节点建立连接,发送replid 和offset;
- 如果是第一次同步,则返回给从节点 replid 和offset,从节点进行保存;
- 然后通过bgsave 生成rdb 文件,并且发送给从节点;
- 从节点清空本地数据 ,然后加载rdb;
- 后续同步,则通过offset 将新产生的数据 发送给到从节点;
2)增量同步过程:
因为slave 已经保存了offset 漂移量,此时主节点只需要将offset 之后的数据给到slave 即可;
2.2 哨兵模式:
主从模式下,当主节点出现故障后,无法再响应写入请求,此时必须手动将其中的一个slave 置为Master 节点才能继续提供服务;这样显然太麻烦了。可以使用哨兵模式对redis 的主从节点进行监控,并且当主节点下线后也可以触发自动选举Master。
2.2.1 哨兵模式架构:
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:
- 监控:Sentinel会不断检查您的master和slave是否按预期工作;
- 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主;
- 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端;
2.2.2 Sentinel的选举:
Sentinel 会检查集群内的所有节点,如果Master 下线则会触发选举;
服务状态监控如下:
- Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
- 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
- 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
哨兵选主规则:
- 首先判断主与从节点断开时间长短,如超过指定值就排除该从节点;
- 然后判断从节点的slave-priority值,越小优先级越高
- 如果slave-prority一样,则判断slave节点的offset值,越大优先级越高
- 最后是判断slave节点的运行id大小,越小优先级越高。
2.2.3 哨兵模式下的脑裂问题:
出现网络问题,触发了集群的选举,然后后续网络恢复后,集群出现两个主节点,新晋升的主节点将老的主节点强制降级为从节点,如果出现网络问题时,redis 客户端还是连接了老的主节点进行了数据的写入,此时老的主节点被降级为从节点后,则会造成数据丢失;我们可以修改redis的配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求就可以避免大量的数据丢失。
2.3 分片模式:
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
- 海量数据存储问题;
- 高并发写的问题;
2.3.1 分片架构:
使用分片集群可以解决上述问题,分片集群特征:
- 集群中有多个master,每个master保存不同数据
- 每个master都可以有多个slave节点
- master之间通过ping监测彼此健康状态
- 客户端请求可以访问集群任意节点,最终都会被转发到正确节点;
2.3.2 分片是怎么实现的:
Redis 分片集群引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。
三、Redis 单线程为什么还能保证高吞吐
Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度;
3.1 Redis 高性能的三个原因:
- Redis是纯内存操作,执行速度非常快
- 采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
- 使用I/O多路复用模型,非阻塞IO
3.2 Redis的线程网络模型:
3.2.1 单线程模型:
3.2.2 多线程模型:
redis 最新的版本 在命令解析和结果返回时都使用多线程模型,在命令执行时,是单线程串行执行
3.3 Redis 的IO多路复用:
IO多路复用:是利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
此外:select 模型底层是使用数组来存储socket 的所以连接socket的数量有一定限制;poll模型底层使用链表来存储socket 所以socket 的连接是没有限制的,但是poll 和select 一样都需要不断地进行轮训来获取到哪个socket 就绪;而epoll 提供了事件监听机制,可以在有socket 连接或者可读写的时候 进行通知;
总结
本文对Redis 的主从,哨兵,分片三种集群部署进行介绍,并对Redis 分布式锁的使用问题进行概括。