黑马Java面试笔记之Redis篇(分布式锁)

面试题

我看你做的项目中,都用到了redis,你在最近的项目中那些场景使用了redis呢

         如果回答了分布式锁,那么就会有以下这个问题

redis分布式锁,是如何实现的?

 需要结合项目中的业务进行回答,通常情况下,分布式锁使用的场景

集群情况下的定时任务、枪单、幂等性场景

案例

以下设置一个抢券场景

抢券执行流程

        当线程1查询优惠券时,查到还有一张,当进行查询库存是否充足时,线程2也进行查询,此时线程2也可以查到一张优惠券,线程1和线程2同时减1,咋会出现超卖情况

如果项目是单体项目,并且只启动一台服务,加synchronized锁可以解决这个问题,如下

但是为了支持更多的并发请求,往往会把项目进行集群部署,即将代码部署到多台服务器上,如果使用集群部署的话,再使用synchronized锁就会出现问题

         synchronized锁是本地的锁,属于gvm,每个服务都有各自的gvm,只能解决同一个gvm下线程的互斥,解决不了多个gvm下线程的互斥,所以在集群情况下,就不能使用本地的锁来解决了

只能使用外部的锁来解决,即分布式锁,Redis就可以作为分布式锁

一. Redis分布式锁

        Redis实现分布式锁主要利用Redis的setnx命令。setnx时SET if not exists(如果不存在 ,则AET)的简写。

这里面试官有可能就问了

这里有两种方案,一种是根据业务执行时间预估,一种是给锁续期

根据业务执行时间预估是不可行的,如果出现卡顿等问题,都会导致业务执行时间变慢;

第二种给锁续期也就是redisson实现的分布式锁,以下是执行流程(问的比较多)

如果面试官问:

使用redisson实现的分布式锁是否可以重入?

 同一个线程是可以重入的,不是同一个线程则不可以重入

redisson实现的分布式锁能保证主从数据一致性吗?

 使用RedLock红锁保证分布式锁的主从一致性

但是红锁是由缺点的,实现复杂,性能差,运维繁琐,所以使用的很少。

 总结

面试官:Redis分布式锁如何实现?

候选人:在redis中提供了一个命令setnx(SET if not exists)

        由于redis是单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或者删除key的时候是其他客户端不能设置这个key的

面试官:好的,那你如何控制Redis实现分布式锁的有效时长呢?

候选人:嗯,的确,redis的setnx指令不好控制这个问题,我们当时采用的redis的一个框架redisson实现的。

        在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当业务完成之后需要使用释放锁就可以了

       还有一个好处就是 在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,他会自选不断尝试获取锁,如果客户1释放之后,客户2就可以马上持有锁,性能也得到了提升。

面试官:好的,redisson实现的分布式锁是可重入的吗?

候选人:嗯,是可重入的。这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数

面试官:redisson实现的分布式锁能解决主从一致性问题吗?

候选人:这个是不能的,但是可以使用redisson提供的红锁来解决,但是这样的话,性能就太低了,如果业务中非要保证数据的强一致性,建议采用zookeeper实现的分布式锁


还有一些redis其他的面试问题,这些问题可能跟业务没啥关系,但是仍是一些高频面试问题

二. Redis集群方案

Redis集群有哪些方案,知道吗?

 在Redis中提供集群方案总共有三种

  • 主从复制
  • 哨兵模式
  • 分片集群

在集群中其他的面试问题有以下几个

2.1 主从复制

特点:单节点Redis的并发能力是有上限的,要进一步提高redis的并发能力,就需要搭建主从集群,实现读写分离

主从数据同步原理

主从全量同步

主从增量同步

 总结

2.2 哨兵模式

作用:Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵结构和作用如下

极大保障redis主从的高可用

服务状态监控

        Sentinel基于心跳机制检测服务状态,每隔一秒向集群的每个实例发送ping命令:

  • 主管下线:如果sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
  • 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。

哨兵模式会出现脑裂问题

        这是一个正常结合哨兵模式的主从架构

         此时由于网络问题,主节点master和哨兵模式处在不同分区,此时哨兵只能检测从节点,检测不了主节点

         当网络恢复后

解决方案如下: 

 总结

 

 2.3 分片集群

主从和哨兵可以解决高可用,高并发读的问题。但是仍然有两个问题没有解决:

  • 海量数据存储问题
  • 高并发写的问题

使用分片集群可以解决上述问题,分片集群的特征:

  • 集群中有多个master,每个master保存不同数据
  • 每个master都可以有多个slave节点
  • master之间通过ping检测彼此健康状态
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点

 分片集群结构-数据读写

        Redis分片集群引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模解决放置哪个槽,集群的每一个节点负责一部分hash槽。

 总结

 

面试官:redis的分片集群有什么作用?

候选人:分片集群主要解决的是,海量数据存储的问题,集群中有多个master,每个master保存不同数据,并且还可以给每一个master设置多个slave节点,就可以继续增大集群的高并发能力。同时每个master之间通过ping检测彼此健康状态,就类似于哨兵模式了。当客户端请求可以访问集群任意节点,最终都会被转发到正确节点

面试官:Redis分片集群中数据是怎么存储和读取的?

候选人:嗯,在redis集群中是这样的

        Redis集群引入了哈希槽的概念,有16384个哈希槽,集群中每个主节点绑定了一定范围的哈希槽范围,key通过CRC16校验后对16384取模来决定放置哪个槽,通过槽找到对应的节点进行存储。

        取值的逻辑是一样的。


三.其他面试问题

Redis是单线程的,但是为什么还那么快?

  •  Redis是纯内存操作,执行速度非常快
  • 采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
  • 使用I/O多路复用模型,非阻塞IO

能解释一下I/O多路复用模型吗

         Redis是纯内存操作,执行速度非常快,他的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型主要就是实现了搞笑的网络请求

  • 用户空间和内核空间
  • 常见的IO模型
    • 阻塞IO(Blocking IO)
    • 非阻塞IO(Nonblocking IO)
    • IO多路复用(IO Multiplexing)
  • Redis网络模型

3.1 用户空间和内核空间

 3.2 阻塞IO(Blocking IO)

 3.3 非阻塞IO(Nonblocking IO)

 3.4 IO多路复用(IO Multiplexing)

        IO多路复用是利用单个线程来同时监听多个Socket,并在某个Socket可读,可写时得到通知,从而避免无效的等待,充分利用CPU资源。不过监听Socket的方式,通知的方式又有多种实现,常见的有:

  • select
  • poll
  • epoll

三者是有差异的

 3.4 Redis网络模型

        Redis通过IO多路复用来提高网络性能,并且支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库

 总结

能解释一下I/O多路复用模型吗?

面试官:Redis是单线程的,为什么还那么快?

候选人:嗯,这个有几个原因

        1.完全基于内存,C语言编写

        2. 采用单线程,避免不必要的上下文切换可竞争条件

        3.使用多路I/O复用模型,非阻塞IO

        例如:bgsave和bgrewriteaof都是在后台执行操作,不影响主线程的正常使用,不会产生阻塞。

面试官:能解释一下I/O多路复用模型?

候选人:嗯,I/O多路复用是指利用单个线程来同时监听多个Socket,并在某个Socket可读,可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用的epoll模式实现,他会在通知用户进程Socket就绪的同时,把已就绪的Socket写入用户空间,不需要挨个遍历Socket来判断是否就绪,提升了性能。

        其中Redis的网络模型就是使用I/O多路复用结合事件的处理器来应对多个Socket请求,比如,提供了连接应答处理器,命令回复处理器,命令请求处理器。

        在Redis6.0之后,为了提升更好的性能,在命令回复处理器使用了多线程来处理回复事件,在命令请求处理器中,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值