04.面试突击-上
# 该从哪些方面准备面试?
我们大部分去准备面试的时候,可能会去看很多的八股文,觉得只要熟练掌握了这些答案,就可以面到很好的薪资,找到理想的工作
但是其实不是这样的,八股文只是你的 基础能力
,稍微有一点水平的面试官都不会去像一个提问机器一样,一道一道题去问你,而是希望通过对话去了解你对项目、对技术的看法,以及你解决问题的能力,因此需要从面试底层的思维去了解面试,再针对性的学习,才可以有一个比较好的效果!
你可以这样想一下,假如你是面试官,你会怎样去考察面试者的一个综合能力呢?作为面试官,你肯定知道面试者背了许多的八股文,所以八股文只是你去找工作要过的第一关,接下来,面试官会通过某一个具体问题,一点一点追问细节,以及生产中所需要的一些问题,通过这些才可以真正看出来你是否真的掌握了这些积能,以及你在做项目中,是否有自己的思考!
接下来,从 5 个方面来说一下去面试的话,需要从哪几个方面准备
- 首先是
技术广度
因为面试官招聘你,是需要去做项目干活的,那么首先你的技术栈必须要匹配到公司的技术栈,并且到公司可以直接上手做项目,而不是再从零去培养,因此会先考察技术的广度,判断你都了解学习过哪些技术
就比如 Redis 用过吗?项目中哪里用到了 Redis 呢?原理了解吗?集群部署?高可用原理?单线程高并发的原理?
Dubbo 用过吗?底层原理?用在项目中是做什么?
RocketMQ 用过吗?为什么用呢?那里引入了?底层原理?遇到过消息丢失的问题吗?集群部署?高可用如何保证?
还有就是基础的 JVM、数据库(事务、索引、sql 优化、锁)、并发方面的内容
如果这些都答得比较好的话,证明这些技术是掌握了的,那么招你来工作基本可以直接上手,一般薪资在 10-20k 之间
- 第二点是
项目经验
通过项目经验一般就能看出来你做项目中有没有自己的一些想法以及思考,项目经验方面通常会问你学到的各种技术是如何结合项目进行落地的,以及落地之后碰到的一些实践中的问题,如何解决,有没有对项目做过一些优化之类的?
比如说你学了 RocketMQ,那么你在项目中怎么去使用 RocketMQ 了呢?具体在那些场景下引入了 RocketMQ,不引入的话会有什么问题呢?
如果使用 RocketMQ,碰到消息丢失了怎么处理呢?消息如果大量积压怎么处理呢?
如果使用分库分表,那么核心的表每天新增了多少 GB 的数据呢?目前表中已经有多少的数据了?什么时候分的表?什么时候分的库?
在分表之前,SQL 性能是怎样的呢?分表之后 SQL 性能提升了多少?
分库之前每个数据库上放多少 GB 的数据?分库之后拆分到了几台数据库服务器上去?每台服务器存放多少的数据呢?
像这些问题,在自己学习的时候可能就忽略了,并且缺乏一些自己的思考,以及由于是自己学习的项目,确实没有碰到过这些场景,因此对于这些 细节性
问题,一定要去多查一些资料,提前准备,多思考!
- 第三点是
生产经验
这块主要是去问真正将项目部署在生产环境中,会遇到的一些情况以及在技术调研时,你是如何进行技术选型的?
就比如在分布式这块,如果项目中使用了
网关
,那么在使用网关之前,你都调研了哪几种网关,为什么选用了某一个网关?
这一块内容的话,其实也就是考察你使用网关的时候,有没有去了解其他同样类型的产品,其实选用网关主要就是看这个网关的功能、使用的开发语言、以及扩展性方面是否符合自己公司的需要
那么项目在上线之后,你们公司的系统每天的访问量是多少呢?高峰期的访问量呢?
对于访问量,可能大多数人心里不太有底,因为可能平时做的项目访问量并不算太高并且项目也可能是自己学习做的,根本就没有访问量,那么怎么去知道系统的访问量呢?可以对不同配置的机器进行 压测
分析,压的多了,心里就有数了
网关一般都怎么部署了?部署了几台机器?每台机器的配置是怎样的呢?如果服务上下线,网关如何实现的动态路由呢?
其实网关一般部署 2-3 台就可以,每台的配置的话,看自己系统的访问量,4C8G 的服务器,每秒钟扛个几百的请求肯定是没有问题的,8C16G 的机器可以进一步抗上千的请求,16C32G 和 32C64G 的机器当然抗的请求量就更多了
有没有网关的扩容方案呢?如果压力过大,网关如何扩容?有没有计算过网关请求路由的性能如何?一个请求经过网关路由的平均时长是多少呢?
这其实就是考察对网关的一些监控指标有没有做过,以及并发量上来之后,有没有什么具体的应对方案
生产经验这一块的内容,可能对于很多应届生来说不会考,这属于是工作 5 年左右的工程师所需要掌握的内容,但是了解一下,以后在工作中会更加注意这方面的细节,如果生产经验的内容掌握的没有问题的话,30k 左右还是可以的
- 第四点是
技术深度
这一点就是考察某一个技术的底层原理了,比如 RocketMQ 的源码、Dubbo 的源码等等
那么你可能会有疑问,有必要学习源码吗?学习那么多有什么用呢?
你对源码的掌握程度其实就决定了你的技术深度,在生产环境中,如果碰到了一些异常或者报错,导致系统挂掉,了解源码,你可以很快的就去定位到问题出现在了哪里,可以直接根据异常去分析技术的源码,从源码级别定位到问题的所在
,再去解决问题,这就是学习源码带来的 竞争力
比如,Dubbo 报错,RocketMQ 出现异常无法写入下消息,ES 查询很慢等等,如果不了解源码,可能对于这些问题,根本就无从下手
- 最后一点是
系统设计
比如设计一个秒杀系统、12306 购票系统,支撑上亿用户去购买火车票,设计一个红包系统,你会如何从哪些方面考虑?
这考察的就是 综合能力
了,公司招你进来,肯定是希望你可以独当一面的,独立负责一块东西,通过考察你对系统设计的了解,可以很清晰的知道你在这方面的能力
不过如果仅仅是应届生面试,20k 的薪资,面试官也根本不会去考察你系统设计的能力,如果是更高级的岗位,这一块的内容就必须掌握,而且是从细节掌握
# 分布式技术面试实战
如果跟面试官聊到了 分布式
这方面的内容,该怎么去聊呢,或者说是面试官会去问哪些内容呢,下面对分布式面试内容进行分析
目前主流的微服务框架主要就是两个 Dubbo
、 Spring Cloud
,可能有些项目使用 Dubbo,有些使用 Spring Cloud
其实两者最主要的区别就在于 生态
,Spring Cloud 的生态很丰富,提供了注册中心、网关、服务熔断、服务限流等等很完善的一系列组件,功能比 Dubbo 丰富很多,而 Dubbo 设计的目的主要是作为一个 RPC 服务框架
使用,专注于服务注册、服务发现、负载均衡等服务治理的功能
简单来说,就是 Dubbo 主要是为了高性能的服务调用,而 Spring Cloud 是为了提供一套完整的微服务解决方案,而 Spring Cloud Alibaba 则整合了 Spring Cloud 和阿里巴巴的一些开源组件,如 Nacos、Dubbo、Sentinel 等等,通过 Spring Cloud Alibaba 可以迅速的将 Spring Cloud 接入阿里中间件,搭建分布式系统
# 技术选型
那么先来思考第一个问题:你们公司微服务框架选用了哪一种,为什么这样选择呢?
简单来说,可以从 公司的需求
和 对哪一种微服务框架更加熟悉
这两方面来说,就比如说,我对 Dubbo 更加熟悉,那么肯定使用 Dubbo,这样对于前期生产成本以及后期维护成本都是大大降低了,其实选用哪一种技术,也不用说的很天花乱坠,只要说明需求即可
其次,可以去 Dubbo 官网,Dubbo 在官网也有与 Spring Cloud 的比较
如 Spring Cloud 存在了一些问题如下,而这些问题也就是 Dubbo 的优势:
- 落地成本以及后期维护成本大
- 欠缺服务治理功能,尤其负载均衡、流量路由方面较弱
- 基于 HTTP 进行通信,性能不如 RPC 框架
# 技术原理
那么假如说,微服务框架选择了 Dubbo,那面试官肯定是要问问你有没有看过 Dubbo 的源码呢?原理了解吗?
这里我就以 Dubbo 来举例,如果你使用 Spring Cloud 或 Spring Cloud Alibaba,问题都是类似的,你一定要对这个问题,画一个原理的流程图,可以和面试官清晰的讲解出来,Dubbo 整理的流程图我也画出来了,如下图,流程我也写成文字:
- 每个服务提供者都会去注册中心注册自己,包括自己的地址(ip+port)
- 服务消费者去消费时,从注册中心(Dubbo 使用 ZooKeeper 作为注册中心)中拉取服务列表
- 消费者会去为远程代理对象创建一个动态代理对象,通过动态代理来拦截方法的执行
- 在代理对象的拦截中,会去执行一系列的操作
- 负载均衡,选择一台机器进行通信
- 选择一种通信协议:Dubbo 提供了自定义的高性能 rpc 通信协议
- 将请求进行封装,并且序列化
- 通过网络通信框架,将远程调用请求传给 Dubbo 服务提供者
- Dubbo 服务提供者收到后,也会进行一系列操作解析请求,最后调用本地服务,将执行结果返回给服务消费者
# 由面到点
整体的一个 Dubbo 原理说完之后,肯定会从 Dubbo 某一个具体的功能切入,比如说一下 Dubbo 底层的网络通信机制原理,这其实考察的就是 Dubbo 底层网络通信框架 Netty 的原理,只要讲一下 Netty 如何进行网络通信,以及 Netty 的线程模型是怎样的就可以了
具体的技术细节就不说了,这里主要说一下面试的逻辑,只有熟悉面试官的逻辑之后,才可以更好的去准备面试
# 系统设计层面
并且对于一个技术,可以多去看一下它的官网,看一下官网怎么说这个技术的优点,以及它的架构设计!
Dubbo 在系统设计层面,如何实现了它的高度可扩展能力呢?使用了 SPI 来实现
Dubbo 中将核心的组件全部接口化,组件和组件之间的调用全部依托于接口,之后再动态去寻找配置的实现类,如果没有配置,就去使用默认的实现类
并且 Dubbo 提供了配置自己实现的组件的功能,如果你自己实现了一个组件,通过配置,可以在 Dubbo 运行的时候,直接使用你的组件而不是默认的组件
Dubbo 提供了很多的 SPI 扩展实现,如:
- 协议扩展
- 调用拦截扩展
- 集群扩展
- ...
更多细节可以查看 Dubbo 官方文档
# 自己设计
如果面试官让你自己设计一个 RPC 框架,你该从哪些方面来设计呢?
这个东西,其实也没法简简单单说清楚,其实也就是问你 Dubbo 底层的原理,只不过,让你自己设计,可能并不需要太复杂的功能,你需要可以从整体流程来说一下 RPC 框架需要哪些功能部分,那么接下来我就简单从各个功能来说一下:
注册中心
:RPC 框架是需要服务之间进行调用,那么一定要有一个地方去存储服务的地址,可以进行服务注册
和服务发现
的功能的,注册中心使用 ZooKeeper 实现动态代理
:RPC 框架中,服务消费者肯定是没有服务提供者的一个实例对象的,因为他们分布在不同机器上,所以需要去创建动态代理对象负载均衡
:这个是在消费方实现的,消费者从注册中心拉取到服务提供者的地址,可以根据多个地址进行负载均衡序列化、网络通信
:消费者最终会将调用请求发送到服务提供者去,最终服务提供者执行完本地方法,再将执行结果给返回,这其中通信框架可以使用 Netty(高性能网络通信),并且需要将调用请求序列化为二进制的字节数组,才可以通过网络发送出去
我自己也写过一个 RPC 框架,这里将流程图给出来,可以看一下整体的 rpc 调用流程是怎样的:
# 注册中心面试实战
注册中心技术选型:
在分布式系统中,注册中心肯定是必不可少的,那么对于注册中心如何选择呢?
如果使用 Dubbo 作为服务框架,那么 Dubbo 底层的注册中心使用的就是 ZooKeeper
如果使用 SpringCloud 作为服务框架,一般注册中心都会选择 Eureka 或者 Nacos,配套进行使用
注册中心原理:
那么注册中心的 原理
肯定是要了解的,通过注册中心可以用来做那些事情:
- 如何注册服务信息?
- 如何进行服务发现?
- 注册中心如何集群部署?
这些都是具体的原理方面的内容,不放在这里展开说了,主要了解一下在注册中心这里,会问哪些内容
我的专栏里就有 ZooKeeper 的内容,可以去查看一下
CAP 理论:在分布式系统中,被讨论最多的一个就是 CAP 理论
,即一致性 (Consistency)、可用性 (Availability)、分区容错性 (Partition tolerance)无法同时满足,最多只能满足两个,因此大多数场景下都是满足 CP 或者 AP ,而注册中心就是用于分布式系统中的,因此需要去了解使用的注册中心符合 CAP 中的哪两个:
- 在 ZooKeeper 中,只有一个 Leader 可以写数据,写完数据之后会同步给其他的 Follower,如果 Leader 挂了,就会去重新选举一个 Leader,选举期间会出现短暂的
写
服务不可用情况,因此 ZK 中为了保证 C 而放弃了 A,所以 ZK满足了 CP
- 在 Eureka 中,
满足 AP
,为了保证可用性而放弃了数据的一致性,怎么做的呢?Eureka 中各个节点都是平等的,所以某个节点宕机不会影响其他节点的工作,如果从一台机器中查询数据失败,那么可以尝试从其他的机器中查询,这里查到的数据可能不是最新的(不保证数据的一致性)
大规模服务实例场景下的表现:
对于 ZooKeeper 和 Eureka 来说,其实都不适合大规模的服务实例,因为如果服务实例数量达到几千上万,如果服务上下线,那么对于 zk 来说,需要去通知其他节点服务上下线,那么会导致 zk 的 网络瞬时流量很大
,可能直接给 zk 所在机器的网络带宽打满,甚至直接打挂掉
对于 Eureka 来说也是,接收很多服务实例的请求,大量的心跳请求会导致 Eureka 压力太大
ZooKeeper 和 Eureka 一般也就能支撑几百上千的服务实例,
注册中心生产环境可以抗多少并发请求呢?
注册中心,一般会使用比较高配置的机器来部署,如 8C16G 或者 16C32G
那么一般来说,使用 8C16G 的机器部署,每秒钟抗几千的并发请求还是没有问题的,那么如果使用 16C32G 甚至 32C64G,可以抗更大数量的请求,并且对于注册中心以及后边要将到的网关,都会使用比较好的机器来部署,因为这些是 基础性的系统
,如果出问题会导致服务大面积瘫痪
一般注册中心怎么部署呢?
可以去看一看 ZooKeeper 如何集群部署,部署细节可以简单了解一下,也不会问具体部署细节,但是要清楚 部署的流程
问的话,可能就是问 ZK 一般集群部署几台机器?部署的机器什么配置?
机器配置以及可以扛下请求数量在上边已经说过了,zk 部署集群的话,一般都是 小集群部署
,1 主 2 从即可,zk 本身就不适合大集群部署,这与 zk 的架构设计有关,因为数据写入 Leader 之后,需要同步给 Follower 的,如果 Follower 数量过多,会导致同步速度很慢,影响 zk 的写性能
# 网关系统面试实战
# 技术选型方面
这一块的话,主要是考察对 网关技术
的了解,比如你使用了分布式系统,那你整个系统前肯定是有一个网关的
那你是如何去对网关进行技术选型的呢?这其实就是考察你对常见的几种网关是否熟悉,常用的几种网关以及优缺点如下:
Nginx
:性能高,成熟,但是可扩展性不足,并且 Nginx 使用 c 编写,很难根据源码去进行定制化开发Zuul
:Zuul 有两个大的版本 Zuul1 和 Zuul2,是基于 Java 实现的,核心功能比较简单,如果需要一些灰度发布、限流、动态路由之类的功能,需要自己二次开发Spring Cloud Gateway
:目的就是为了替换 Zuul1,功能比较完善,性能相对于 Zuul1 来说好了很多自研网关
:目前许多互联网都自研自己的网关,自研网关的好处就是可以根据自己业务特点提供一个定制化、高性能、可扩展的 API 网关解决方案,例如美团技术团队就自研了 Shepherd API 网关,可以参考文章:https://tech.meituan.com/2021/05/20/shepherd-api-gateway.html
上边只是简单的提到了一些优缺点,如果你去面试,并且在简历中有较多的分布式相关的项目,一定要去对这些技术选型好好了解一下,不要对每个问题都只是知道个大概,再问就什么不知道了!
# 网关的核心功能:
那么如果系统中使用了网关,你是希望去使用它的什么功能呢?一定要了解网关的 应用场景
,因为很可能讲完这个之后,会问你,让你自己设计一个网关,你会怎么设计呢?这不正是考察网关的功能以及对每个功能点如何进行设计的吗?
这里将网关的功能按照重要顺序列一下,重要的列在前边:
- 动态路由:新上线某个服务,可以动态的将请求路径和服务的映射关系
热加载到网关
里去,服务增加或减少机器,网关也可以自动感知到
- 灰度发布:新功能正式上线之前,将新功能在少量机器上进行发布测试
- 授权认证:对发送到网关的请求进行授权认证
- 限流熔断
- 性能监控:监控每个接口的
耗时
、成功率
、QPS
- 系统日志:打印接口请求日志
- 数据缓存
# 网关部署的机器配置:
这是属于网关系统在生产环境部署的内容了,这个之前在讲注册中心也讲过机器配置的问题,这里再啰嗦一下,多看看就记住了
常用的机器配置就是 4C8G、8C16G、16C32G、32C64G
那么像注册中心、网关系统,这种都是属于 基础架构类型的系统
,一定要上配置高一点的机器,8C16G 以上的
网关系统部署在 8C16G 的机器上,每秒钟抗几千的请求是可以的
16C32G 的话,抗上万的请求也是没问题的
将机器配置和对应的请求量级大概可以对应起来就可以
并且网关系统一般是使用集群部署的,通过 Nginx 将请求再分散到多个网关系统上,可以抗更多请求,因为网关系统一般不会是整个系统的性能瓶颈
网关在整个系统中所处的地位如图所示:
# 网关中一些核心技术实现思路
可以去了解一下网关中核心技术是如何实现的,这里就以 动态路由
和 灰度发布
来简单说一下实现思路
- 动态路由
动态路由目的就是让 网关系统可以感知到服务上下线
,你可以想一下学到的哪一个技术可以实现这个功能呢?
这个不就是 通知
功能吗?
那么直接通过 RocketMQ 就可以实现了,新服务上线,发送一个 MQ 通知,让网关系统去拉取最新的服务地址,如果机器下线,也可以发送 MQ 通知,让网关系统剔除掉这个服务即可
- 灰度发布
这里说一下实现灰度发布的一个思路
首先,需要创建一张灰度发布表,包含字段如下:
id int(11)
service_id varchar(255)
path varchar(255)
enable_gray_release int(11)
通过定时任务去查灰度发布表,存入 Map 中
再做一个灰度发布的 拦截器
,比对请求路径是否启用灰度发布,如果启用灰度发布,就将流量转发到新部署的机器上去
这里将新版本的系统设置一个标志位,比如 ReleaseVersion
,如果这个值为 NEW
的话,表示是新部署的系统,那么就可以根据这个标志位判断哪些机器上部署的系统是新版本了,将流量散发到这些新版本的机器上去
# 生产部署面试实战
接下来说一下,对于 生产部署
方面会从哪些方面来问?
# 服务如何部署?
比如,你们生产环境中,各个服务是如何进行部署的呢?
其实也就是问:
网关
、注册中心
以及各个服务
是如何部署的?- 部署的
机器配置
是怎样的? - 每天
访问量
是多少? 高峰期接口访问量
是多少?
回答的话,也不用回答细节,将整个流程给回答一下:
网关部署
的话,就根据系统的访问量,如果不大,部署 2-3 个网关系统即可
注册中心
一般都是集群部署,比如 zk 集群部署(1 主 2 从)
服务
部署一般看每个服务的负载情况了,负载高的服务,就多部署几个,分散一下请求
而对于 系统访问量
来说,可能很多人都没有什么概念,并且对于机器可以抗多少数量的并发请求也都不太清楚
这些都是属于更细节一些的东西了,属于 进阶部分
,可以了解一下,但是深入学习这些内容之前,一定要先将基础把握住,不要本末倒置了
其实想要看系统访问量的话,很简单的一个方式就是,在代码中加入一些统计的接口,比如统计每个接口请求数量、接口请求延时、系统访问量,可以直接在代码中记录,再向外暴露出一个接口,可以查询到这些 统计值
就比如说,一个接口请求一次之后,在代码中,对这个接口请求次数加 1,用 AtomicLong 来统计每天的接口请求数量,接口平均请求延时的话,将接口请求延时加起来除以接口请求次数也能算出来
这里还要了解一下 性能监控
中的一些指标,T99、T95
如 T99 = 50ms
,表示 99% 的请求耗费时长都在 50ms 以内
T95 = 50ms
,表示 95% 的请求耗费时长都在 50ms 以内
怎么看系统可以承载的最大负荷呢?
使用开源的 压测工具
,例如 Jmeter,去模拟用户向系统发送请求
通过控制发送请求数量,查看系统的负载情况,以及系统最多可以处理的并发请求数量
比如发送 1000 个请求,系统只可以处理 800 个,那么就说明系统最大处理并发请求数量为 800
# 数据库如何部署
对于数据库来说的话,一般也会使用 比较好的机器配置
来部署,16C32G 的机器比较适合,32C64G 的机器可以根据数据库的负载来选择,负载较高的话,可以升级一下机器的配置
那么一般来说 16C32G 的机器,每秒抗 小几千的并发请求
还是问题不大的,只要 不存在一个 SQL 执行 2-3 秒的情况
就可以!
但是对于数据库来说,如果平均每秒都是三四千请求,此时 MySQL 机器虽然不会崩掉,但是它的负载是很高的,包括 CPU 使用率、磁盘 IO、网络负载都是处于比较高的水平
大部分的系统,其实每秒钟也就几十个请求,高峰期几百个请求,系统都是可以抗住的
# 如果访问量扩大 10 倍,如何应对?
访问量扩大 10 倍的话
对于
网关系统
来说,部署 10 倍的网关机器即可,再通过 nginx 将请求分发到不同的网关中去,就可以分散大量请求压力对于
服务实例
来说,也是比较简单,通过增加服务实例的数量,并且注册到注册中心
去,这样通过负载均衡
将一个服务的压力也给分散到多个服务实例中去了对于
数据库
来说,如果原本高峰期每秒三四百请求,扩大 10 倍后,每秒三四千的访问量,如果对数据库横向扩容比较麻烦,因此可以考虑给部署数据库的服务器提升配置,比如从 16C32G 提升到 32C64G 的配置,抗 3-4000 的请求还是没有问题的
在生产部署面试方面,主要就是问一些系统 QPS 、部署以及应对并发量上来之后的措施,这其实就考察有没有生产经验,或者说你有没有对系统进行过 压测
并进行性能优化
压测方面的内容,可以关注我,发送
"压测"
领取一份压测视频教程!
# 分布式锁、幂等性问题实战
接下来主要说一下,分布式系统中,在业务以及技术实践中,经常遇到的一些问题
# 接口有处理幂等性问题吗?
什么时候需要处理 幂等性问题
呢?
就比如 创建订单
场景中,如果因为网络抖动原因,导致请求超时了,此时去重试创建订单操作,发送了 重复请求
,那么就可能导致出现了重复创建订单的操作
怎么解决呢?对接口进行 幂等性处理
幂等性处理的解决方案我之前也写过一篇文章,可以参考一下:幂等性处理 (opens new window)
具体选择哪一种方案,需要根据自己的业务需求来选
一般不需要对所有的接口都保证幂等性,而是仅仅 对少数的核心接口保证幂等性
比如对于创建类的操作,可以通过 数据库中的唯一索引
来保证幂等性
对于更新类的操作,可以通过 Redis
来保证幂等性
# 分布式锁问题
先说一下什么时候需要去使用 分布式锁
就比如,多个服务对同一个数据进行操作的时候,为了保证该数据的 并发安全
,需要在操作前加上 分布式锁
这里举个分页缓存的例子,在 Redis 中构建一个分页缓存,那么在第一次查询时,会进行分页缓存的构建,如果分页缓存中的数据发生变化,也需要对分页缓存进行构建,如果不加分布式锁,就会出现 数据库和缓存不一致
的问题出现:
- 当用户 A 新增分享的时候,另一个用户 B 此时正好来查询用户 A 的分享列表,用户 B 线程先去缓存中查询,发现没有,再去数据库中查询用户 A 的分享列表,此时 B 拿到了 A 新增分享之前的旧数据,此时如果用户 A 新增分享并落库,并且去缓存中对用户 A 的列表缓存进行重建,那么此时缓存列表中是用户 A 的最新数据,但是此时用户 B 的线程在数据库中已经查到了用户 A 的旧数据,用户 B 的线程继续执行,将用户 A 的旧数据给放入到列表缓存中,覆盖掉了用户 A 更新的缓存,那么此时就会导致缓存数据库不一致
这个我之前也写过一篇文章,可以查看具体操作:Redis 分页缓存+分布式锁 (opens new window)
分布式锁常见的有:Redis 实现的分布式锁、ZooKeeper 实现的分布式锁
那么对于这两种分布式锁实现的 原理
一定要清楚,其中使用 Redis 的分布式锁一般都是用 Redisson
框架提供的,里边的分布式锁的源码,有时间的情况下,真的要好好了解一下!
其实选择分布式锁,使用 Redis 分布式锁比较多,因为 Redis 在高并发场景下的表现比 ZooKeeper 要好一些
而对于 ZooKeeper 分布式锁来说,了解它原理的同时,也要去了解一下存在的 羊群效应
以及如何对羊群效应进行优化!
我在这一篇文章中也写了,可以详细看一下:ZooKeeper 中的分布式读写锁应对羊群效应 (opens new window)
如果 Redis 集群故障时,分布式锁会失效吗?
其实是 会失效
的,如果客户端 A 去 Redis 集群中添加分布式锁,先将锁写入到 Redis 的 master 中,如果 master 还没来得及将分布式锁同步到 slave 就宕机了,那么就会导致 slave 中并没有分布式锁的信息,如果客户端 B 来加锁,还是可以加上同一把锁,结果就是客户端 A 和 B 都拿着同一把分布式锁在执行
这种情况解决的话,解决思路是加分布式锁时,必须 master 和 slave 都同时写入分布式锁信息
之后,才算加锁成功,这样就不会出现上边的问题了
这样解决的话,就抛弃了一部分性能,来换取可靠性
不过这种问题出现的概率还是很小的,任何系统设计方案都存在利弊
,舍弃一部分对自己业务不重要的可靠性来提升性能或者相反,都是根据自己业务需要来进行选择
分布式锁在高并发场景下的优化问题
如果面试一些互联网大厂,提高分布式锁这块的内容,可能面试官会去问你,有对分布式锁进行过性能优化吗?
这一块的内容,可能大家没有想过这个问题,其实思路也比较简单,就是 分段加锁
,目的就是减小你的锁粒度,以此来 提高系统的并发度
就比如,扣减库存,需要添加分布式锁,如果库存有 10000 个,那么可以在数据库中创建 10 个库存字段,stock_1
、stock_2
...,那么每次对库存操作,就对其中的一个库存字段给加锁就可以的,将库存分成了 10 个库存字段,原来一个分布式锁对库存加锁,同一时间只可以一个线程来处理,库存分片之后,同一时间可以有 10 个线程获取分布式锁,并发度提高了 十倍
如果某次来扣减库存时,库存不足的情况下,需要合并库存进行扣减,合并库存就是对其他库存字段加锁,并且查询库存,如果足够,就进行库存扣减操作
总的来说,对分布式锁的优化就是:分段锁 + 合并库存扣减