后端架构学习

后端架构学习

一、架构设计面试精讲


架构设计面试精讲脑图

1. 开篇

1-1. 面试问题

  1. 基础问题答的好,栽在架构上:中高级研发会面试
    • 初级研发:redis是否可以做分布式锁,存在问题,一致性问题
    • 中高级研发:分布式缓存系统的数据分布,赋值,以及共识算法,主要是看面试者能力天花板
  2. 技术能力满足岗位要求,面不到职位,问题是对技术的认知不够,技术价值和两点吐出不了,回答问题视角。
  • 没有设计经验,不了解应该准备哪些架构问题
  • 没有大厂经理,不知道如何回答架构设计问题
  • 没有技术认知,不知道什么视角回答问题,面试官满意

1-2. 目标:架构设计误区,识别技术陷阱,掌握面试中架构设计问题的知识体系

1-2-1. 面试方式

  1. 假设一种场景,让候选人做技术设计
  2. 让候选人画出自己做过最复杂的系统架构图,再提具体问设计题

1-2-2. 设计问题的技术体系(6大模块)

  1. 架构原理与技术认知
  2. 分布式技术原理和设计
  3. 中间件常用组件原理和设计问题
  4. 数据库原理与设计问题(mysql很零散)
  5. 缓存:分布式缓存原理和设计问题,高并发解决方案
  6. 业务系统架构:互联网高性能高可用设计问题

1-3. 总结

互联网会把技术层次设卡,通过架构设计上各类知识点,讲研发工程师分层

1-4. 讲课方式

案例背景--案例分析--原理剖析--解答方法

2. 三个架构技术认知

2-1. 架构设计认知(立足点连成线扩成面)

初级-中高级-架构

2-1-1. 案例交易流程系统:系统拆分

(报价系统,促销系统,订单系统)

  • 研发人员少,系统拆分带来系统复杂度 为什么要拆?
  1. 订单系统层面:如果耦合到整个交易系统中,其他功能发布上线会影响到订单系统
  2. 促销系统层面:出于保障交易流程稳定性考虑,发生异常时可以让促销系统剧本降级的能力
  3. 报价系统层面:出于专业化和快速迭代的考虑,目的是为了可以快速响应需求的变化。
  4. 从复杂度评估层面:在规范了api的格式定义和调用方式后,系统的复杂度可以维持在可控的访问。

点:三个系统的以前存在的问题,线:交易流程串联起拆分逻辑,面:从复杂度和成本考虑表述了设计原则

2-1-2. 底层设计逻辑

  • 为什么拆分:系统,模块解耦
  • 为什么系统解耦:拆分后职责单一,功能内聚
  • 为什么职责单一:功能逻辑和迭代速度更快
  • 为什么关注开发效率:毋庸置疑,企业提效

总结:架构拆分是管理在技术上提效的一种手段

2-2. 分析问题的认知

  • 业务方:关注点在系统能力(满足市场需求)
  • 管理者:关注人效提升(满足快速迭代需求)
  • 技术人员:关注系统设计原则(做系统设计立足点满足不同人对系统的诉求)

这个系统设计立足点就是针对系统现极端业务发展带来的主要矛盾提出,才有更有价值且被认可。

1.矛盾1:多人协作(比单体项目少数人协作复杂了)研发效率不能匹配业务发展速度,单靠加人解决不了问题。(面试架构能力也就是看是否抓住主要矛盾)

  • 此阶段系统架构核心原则就不能随便定义保证高性能高可用了,而是对原有系统进行系统边界拆分,让研发人员提速,快速响应需求变化;对架构师要求:对业务领域和团队人员有足够了解。

2-2-1. 软件复杂度来源两点:

  1. 本质复杂度:人的复杂性和业务复杂性
  2. 偶然复杂度(开发工具,高性能高可用框架)

利用技术解决业务领域中的核心问题带来巨大价值 ,即解决本质复杂性问题#4CAF50

2-3. 能力边界认知

2-3-1. 高级研发和架构师区别在哪?

  • 功能性需求
    研发:采用合理技术实现系统功能需求;架构师:采用合理技术设计系统功能性架构
  • 非功能性需求
    研发:无要求;架构师:在系统高可用,高性能,高扩展,容灾性等技术导向方面做出有价值贡献

2-3-2. 能力驾驭边界影响范围

中高级研发:模块或者子系统层面
架构师:全系统层面
高级架构师:某一领域层面

2-3-2-1. 提升驾驭边界的能力
  • 多总结,形成知识体系
  • 方法论:沉淀
  • 认知能力:提升(了解不同行业业务的特点)
  • 架构边界的机会(跳出舒适区,认知到系统的不同层次的认知)

3. 架构师进行面试讲解技术方案

3-1. 如何设计这个系统?

  1. 设计思路,不要陷入技术点细节(否则产生全局思维不足印象)

3-2. 案例设计评论系统

发个评论底层后台逻辑是点评系统会调用一系列系统的接口能力(如图片新系统,广告系统,消息系统,甚至风控系统)

3-2-1. 项目初期:快速开发上线,商品评论发布通过同步rpc,远程调用各系统接口完成,系统少,逻辑简单符合系统设计

  • 随着业务发展:rpc同步调用问题暴露
  1. 过多依赖其他接口评论接口性能低,可用性收到其他系统影响。
  2. 评论系统上线,其他系统需要跟进联调测试导致需求迭代缓慢

核心矛盾:系统间耦合度过高

3-2-1-1. 解决办法采用异步化解耦(引入mq消息管道)

1。 架构上系统业务逻辑拆分,将原来强依赖的同步rpc调用变成异步消息触发

3-3. 如何做这个点评新系统的改造?

解决问题的方法是术,解决问题的底层思维(4步)是道

  1. 谈复杂来源
  2. 谈解决方案
  3. 谈评估标准
  4. 说技术实现

3-3-1. 复杂来源

先分析系统复杂度,明确设计原则,进而设计架构方案,整体项目才不会找错方向。

3-3-1-1. 功能性复杂度(业务领域本质复杂度)
  1. 系统间耦合
  2. 产品业务发展快速
  3. 协作效率越来越低
3-3-1-2. 非功能性复杂度(tps,qps平均值(目标峰值)的4倍)

高性能
高可用
扩展性
安全性
低成本
。。。

  • 一天点评消息管道100w条,1条消息10个子系统读取,每秒目标值tps 11,qps 115=》4倍 tps 44,qps 460 这个要求不需要高性能方案。
    未来增长目标峰值4倍:tps 176,qps 1840还达不到系统压测极限。

  • 高可用性必须考虑(考虑用户体验)

3-3-1-3. 需要改造的复杂性来源两点
  1. 功能性:业务发展带来的耦合,开发效率慢
  2. 非功能性:保证系统的高可用性

此时就可以明确方案设计目标,就可以开始架构设计了

3-3-2. 解决方案

  1. 采用开源的MQ消息管道:引入消息中间件运维成本
  2. 采用开源的redis实现消息队列:有运维成本,一般系统都有redis不会少所以相较mq减少了一个中间件,另外redis消息队列比mq轻量
  3. 采用内存队列+Mysql来实现:基于mysql不需要引入任何中间件,既有内存队列异步持久化到mysql,通过定时任务读取mysql的消息数据。

3-3-3. 评估标准(设计原则:控制了项目风险)

  1. 不可单点
  2. 松耦合
  3. 系统可监控
  4. 系统可降级(兜底)

设计原则在更高层面:性能,可用性,it成本,投入成本,实现复杂度,安全性,扩展性,数据兜底技术方案:这些在不同场景的不同阶段会起到决定性作用

3-3-3-1. 功能性复杂度

站在leader角度:随着业务发展考虑现有研发团队,it,投入周期是否匹配三种架构解决方案

3-3-3-2. 非功能性复杂度
  1. 无单点原则:三种方案都支持
  2. 可水平扩展:mq,redis都支持;但是mysql+内存队列就要涉及分库分表改造,还要提前根据业务考虑容量预估。
  3. 可降级原则:限流(抛弃预估容量外的用户),降级(提供有损服务),熔断(一般伴随降级处理抛弃异常系统的调用):默认数据库不可降级(降级还存在降级的层次和开发成本,如redis是否降级到数据库)

多个解决方案不要陷入技术点的优劣,越大的项目越难有结果。应该关心考虑的问题方式选择是否符合解决了核心矛盾而不是关系选型结果。

3-3-4. 技术实现

确定了技术解决方案之后,要进一步说明技术的落地实现合深层原理。

3-3-4-1. 比如你选择redis实现解决方案:

redis几点实现方式,各种优缺点有哪些?

  • redis list 的lpush() lpob()
  • redis 的订阅和发布模式
  • redis的有序集合实现方式

3-3-5. 总结

  1. 系统复杂度结合业务场景业务发展阶段
  2. 解决方案建立在复杂度来源基础上
  3. 评估架构方案:至少从功能和非功能判断是否合理
  4. 技术实现细节:技术的实现原理,而非技术的框架组合。

3-3-6. 架构师视角:就是全局的视角包括空间全局和时间全局,

  • 空间全局上要看到整个系统的领域边界
  • 时间全局上要看到整个系统的发展周期

4. 面试官是符合考察与CAP相关的分布式理论?

CAP是分布式核心基础理论
不同岗位要求深度不一样

Consistency 数据一致性 Availability 服务可用性 Partitin tolerance 分区容错性(必须保留分区)

如果选择可用性:就选择AP模型,选择一致性就选CP模型。

CAP理论描述了在出现网络分区的情况下,要在C和A之间做取舍,所以会影响站在调用端的视角看系统是不可用的。 60分
因为:在面试理论问题时,浮于表面的概念性阐述,无法证明技术能力;最好通过以下4步回答:

  1. CAP原理
  2. 实践经验
  3. 技术认知
  4. 具体面试题具体分析

4-1. CAP原理

4-2. 实践经验(BASE理论)

Basically Available 基本可用;Soft State 软状态;Eventually Consistent 最终一致性

4-2-1. 保证基本可用(核心功能可用):

  1. 电商双十一大促,访问压力大的时候关闭商品排行榜等功能展示,保证商品交易主流程的可用--服务讲解
  2. 为了错开双十一高峰期,电商网站会将预售商品的支付时间延后10-20分钟--流量削峰
  3. 在你抢购商品的时候,会在队列中等待处理--延迟队列

4-2-2. 软状态和最终一致性

允许系统中的数据存在中间状态,为了一段时间的系统可用牺牲了一段时间内数据一致性,从而保证最终数据一致性;目前这种数据处理方式几乎是互联网的标配设计。

  • 案例用户在下单的时候不存在真正的扣减库存,而是在前台记个数,等后台提交的时候通过异步任务批量处理。
4-2-2-1. 初中级研发
  1. 充分理解理论原理,不能浮在概念上
  2. 有自己的思考,思考能力
  3. 理论用于实践,讨论实际中处理问题时的思考逻辑

4-3. 技术认知

4-3-1. 高级研发,架构师(提现知识体系和技术判断)

分布式系统看起来像个计算机,包含5大部分:输入,存储,控制,运算,输出。
每个节点都有存储器和运算器;节点间的通信是输入和输出,节点间管理是控制器。(就像网络计算机)

  • 存储器:即分布式存储系统,如nosql数据库
  • 运算器:即分布式计算,如分布式并行计算
  • 输入输出:即分布式系统通信,如同步RPC调用和异步消息队列
  • 控制器:即调度管理,如流量调度,任务调度与资源调度。

从4个角度概括分布式系统的知识体系

4-3-1. 解题思路

4-3-1-1. 说明现实存在的问题

一般redis使用setnx方法实现锁和超时时间控制锁的失效时间,存在A到A1同步当主节点A挂掉,锁还没同步到从节点A1时,根据哨兵机制,A1就会变成主继续提供服务,另一个线程也能请求锁。=》出现两个线程同时拿到锁的情况

4-3-1-2. 回归理论的指导

根据对CAP的理解,redis是AP模型的架构,用于CP的场景在底层技术选型上就是错误的。

4-3-1-3. 扩展知识体系

redis是分布式存储系统,分布式存储系统的知识体系,就要思考它的数据存储,数据复制,数据分布,数据一致性,怎么做的?用哪些技术实现的?

学会从多维度,多角度去对比,分析同一分布式问题的不同方法权衡各种方法的优缺点,形成自己的技术认知和技术判断力;这个需要做的多记得才深区分明显。

4-3-1-4. 有技术的判断力

通过redis能想到目前分布式缓存系统技术现状和技术实现,如果让你造一个redis出来,需要考虑哪些问题
实际工作中不推荐重复造轮子,但是面试中还要表现 出具备造轮子的能力

5. 如何深度回答分布式系统原理问题(数据分片,存储,复制)

分布式存储-》分布-》分片-》复制,一致性相关协议算法的思路

5-1. 案例背景

  1. 如何设计一个支持海量商品存储的高扩展性架构?
  2. 在做分库分表时,基于Hash取模和一致性hash数据分片如何实现的?
  3. 在电商大促期间,如何对热点数据做存储策略?
  4. 强一致性和最终一致性数据共识算法是如何实现的?

过程:分布式存储-》数据分片-》数据复制-》数据副本(高可用唯一手段)-》数据容错(主从节点)-》数据一致性-》协议算法。

5-1-1. 分库分表常用分片原则

5-1-1-1. Hash分片:优点分布均匀实现简单

缺点扩展差,一旦调整hash的模的值就会涉及数据大量位置的调整导致一下问题。
问题1,问题2:如何解决hash分片的缺点,既保证数据分布均匀,又保证扩展性?

  • 使用一致性Hash:将存储节点和数据都映射到一个首尾相连的Hash环上,存储节点一般可以根据IP地质进行Hash运算,数据的存储位置从数据映射在环上的未知开始,依照顺时针方向锁找到的第一个存储节点

一致性hash分布均匀(本质是静态的分片方式),并发写入性能也不错,提高了稳定性,但是hash分片难针对热点商品做单独的架构设计!

问题3:如何解决热点数据分片存储答案是做范围分片

5-1-1-2. 范围分片

要达到分片的灵活性架构,更好的方式是基于分片元数据请求分片元数据获取的信息,可以拿到数据分片信息,还包括数据量,读写qps和分片副本的健康状态等。

控制数据流向哪个分区:
一般1. 预先设定主键的生成规则,根据规则生成数据的分片路由,但这种分片方式会侵入各条件主数据的业务规则;更好的方式是基于分片元数据,数据分片前先问一下分片元数据,数据存储在哪,然后再根据可操作地址,操作数据。

分片元数据也是数据也要保证元数据服务的可用性和一致性?
最直接的方式是专门给元数据做一个服务集群,并通过一致性算法复制数据;把数据的服务高可用和一致性问题交给外围协调组件,如etcd集群。

5-1-1-2-1. 分三步:
  1. 给分片元数据做集群服务,并通过ETCD存储数据分片信息
  2. 每个数据存储实例节点定时向元数据服务集群同步心跳和分片信息
  3. 当调用端的请求过来时,元数据服务节点只需要做好高可用和缓存即可。

5-2. 挖掘能力(问一致性协议和共识算法)

  1. ETCD是如何解决数据共识问题的?为什么要选择这种数据 复制方式呢?
    要从一致性算法原理层面解答,思路是:etcd的共识算法是什么,还有哪些常用的共识算法,以及为什么etcd会做这样的选型?
  • etcd共识算法是基于raft协议实现的强一致性共识算法;常用的强一致性共识算法还有paxos.
  1. paxos算法解决了什么问题?Basic Paxos算法的工作流程是什么?Paxos算法和Raft算法的区别又是什么?

造成分布式系统不可用场景很多比如硬件损坏,网络丢包等,解决这些问题就是做副本,也就是主从模式;如何保证一致性的前提下提高系统可用性。

paxos算法就是解决一致性前提下的系统可用性问题。

paxos(广播方式)分base和multi两类
base paxos算法实现复杂所以etcd选了raft.
raft其实是一种multi-prxos算法工业界常用

一切以领导者为准实现一些列共识,并不是所有节点都可以当选领导者,对leader的选举有限制只有日志最全的节点才能当选leader.

TiDB就是对raft算法优化。

5-2-1. base paxos和multi-paxos区别?

Paxos 算法包含 2 个部分:
1、Basic Paxos : 描述多节点之间如何就某个值达成共识
2、Multi-Paxos : 描述执行多个Basic Paxos实例,对一系列值达成共识:主要是leader的出现

分别为 Proposer提案者、Acceptor表决者、Learner学习者。Paxos 算法和 2PC 一样,也有两个阶段,分别为 Prepare 和 Accept 阶段。

basePaxos 算法会出现死循环问题,即不同节点提案因为步骤中通过的不同时效性导致无限提案的情况。这个问题通过只允许一个提案者能提案可以解决。(multi)

5-3. 中心化的强一致性算法问题存在性能问题,有没有更好的架构思路?

raft算法保证强一致性,并不能保证最终一致性。

需要保证最终一致性,raft算法不适用,因为raft需要保证大部分节点正常运行后才能保证可用性。

保证最终一致性可以选择基于Gossip协议的实现方式

一个节点有新数据后节点活跃,项其他节点传播从而实现最终一致性(谣言传播机制)适合动态变化的分布式系统。

副本少(共识节点少) 副本多(共识节点多)
共识算法 Paxos,Raft Gossip
现有产品 ETCD CockroachDB

6. 海量并发场景下如何回答分布式事务一致性问题

系统之间互相调用,完成一次写操作就涉及到小操作的事务问题。
怎么设计才能实现系统之间的事务一致性呢?

6-1. 案例下单业务:

商品系统--->扣减库存
促销系统--》扣减优惠券
订单系统--》生成订单

6-2. 解决方案

  • 两阶段提交协议(two-phase commit,2PC)、3PC(落地成本大,银行金融采用,强一致性)
  • TCC(落地成本大,银行采用)
  • 基于消息队列(互联网,工业常用)

6-3. 回答过程

  • 介绍目前主流实现分布式事务一致性的方案
  • 回答出可实现的方案MQ和关键知识点
  • 提出其他方案横向扩展,2pc

6-3-1. 2PC(3PC多了询问阶段,如果超时就不进行加锁准备放弃事务)

  • 准备阶段:协调者Coordinator 事务管理器
  • 提交阶段:参与者Participant 具体操作执行的资源管理器

规范定义了两个管理器的接口,事务管理器负责全局事务协调,资源管理器负责实际资源比如mysql,orcal数据库;
spring通过jpa的

  • transitant manage实现分布式事务,
  • 通过多个resource manage管理多个数据源。
6-3-1-1. 2pc如何运行的?

6-3-1-1-1. 各阶段过程

6-3-1-2. 为什么不会基于2PC实现分布式事务一致性(侵入性低优点)?

存在问题

  1. 准备阶段会对每台机器进行资源锁定,比如mysql的行锁;否则在提交阶段如果不提前锁定,提交前数据发生改变就会出现数据不一致情况比如D1库在提交之前另外一个线程扣减库存为1,这个提交线程再去扣减库存就会出现-1线程问题
  2. 死锁问题:枷锁后的数据库出现故障,其他线程无法再去枷锁(锁不释放)。
  3. 性能问题:操作同一个数据数据加锁其他操作只能等待阻塞,并发问题操作只能串行。.

提交过程中某台节点出现网络问题也会导致不一致情况。

6-3-2. 基于MQ的可靠消息投递方案

放弃强一致性,而选择最终一致性,来提高系统的可用性

订单系统---》MQ消息队列---》促销系统

RabbitMq,RocketMq

6-3-2-1. MQ自动应答机制导致消息丢失

促销系统订阅消息事件消费消息中间件消息,消息中间件删除消息,而促销系统又发生异常,导致消息丢失。

6-3-2-1-1. 解决改为手动应答

在促销系统完成后(异步(批处理存在丢失可能)可以提前response 200)给消息中间件回复ok,消息中间件才删除消息。

6-3-2-2. 流量大的情况(如订单系统发送的优惠消息过多促销系统流量大,促销系统限流了),消息积压甚至消息队列超过投递重试次数被放到死信队列
6-3-2-2-1. 订单系统作为生产者无法感知促销系统作为消费者的响应结果,如果订单系统知道促销系统的响应,即使消息丢失,订单系统可以定时任务扫描把未完成的消息重新投递,进行消息的补偿。
6-3-2-3. 落地实现流程


通过消息应答反馈给消费队列上游可以更新消费状态实现消息的最终一致性

定时任务把待发送的重新投递即可实现消息的最终一致性。

分布式事务会不会看是不是可落地的双向确认

6-4. TCC

TCC 本质上也是一个二阶段提交协议,但和 JTA 中的二阶段协议不同的是,它是一个服务层的协议,因此开发者可以根据业务自由控制资源锁定的粒度

JTA 二阶段事务的参与方都要实现 prepare、commit、rollback 一样,TCC 的事务参与方也必须实现 try、confirm、cancel 三个接口

TCC 对应用的侵入性强。业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作,改造成本高

7. 如何回答锁实现原理

分布式锁是解决协调分布式系统之间,同步访问共享资源的一种方式,在分布式环境下,多个系统在同时操作共享资源时,发起操作的系统通常会通过一种方式去协调其他系统,然后获取访问权限,得到访问权限后才可以写入数据,其他系统必须等待权限释放。

  • 面试分布式锁问的比较细(关注细节)
  • 模拟系统秒杀场景,向你提问,如何回答?

7-1. 可选方案

  • mysql实现
  • redis实现
  • zk实现

选择一种方案后面试官一般就会拉锯式追问框架的锁特性细节和解决方式

redis:setNS,服务器挂掉死锁怎么防止?加过期时间,怎么保证加锁和过期时间是原子操作?redis2.6版本前加锁和过期是两个命令非原子操作,后面合成一个原子命令了。

Red Lock-红锁 实现分布式锁 可以参考

如果面试官感觉回答思路清晰,给出的方案也可以落地,那么会去而定你具备初中级研发的设计能力

会继续追问分布式锁原理问题(这样可以通过是否了解其他mysql,zk等实现分布式锁的差异分析你是否对底层原理了解)而非组件使用上。

目的是看是否有造轮子的能力,不借助第三方组件,你怎么设计分布式锁?

7-2. 锁原理和设计分布式锁(造轮子能力)

设计一个分布式锁,需要明确分布式锁经常出现哪些问题,以及怎么解决?

  1. 可用性问题:无论何时都要保证锁服务的可用性
  2. 死锁问题:客户端一定可以获取锁,即使锁住某个资源的客户端在释放锁之前崩溃或者网络不可达。
  3. 集群同步时产生数据不一致,导致新进程有可能拿到锁,而之前的进程还以为自己有所,导致两个进程操作了同一个资源。

7-2-1. 案例扣减库存

了解分布式锁的常见实现方式,优缺点,以及方案背后的原理

7-2-1-1. 方案1基于关系库实现分布式锁

做法如下:先查询数据库是否存在记录,为了防止幻读,通过数据库行锁select for update (悲观锁)锁住这行数据,然后将查询和插入的sql在同一个事务中提交。
以订单表为例:

select id from order where order_id=xxx for update
事务1 事务2
msyql>set auto commit;mysql>select * from table_1 where id=1 for update; msyql>set auto commit;mysql>select * from table_1 where id=2 for update;
msyql>mysql>select * from table_1 where id=2 for update; mysql>select * from table_1 where id=1 for update;
因为事务2已取得id=2的排他锁,所以事务1等待 因为事务1已取得id=1的排他锁,所以事务2等待

因为代码逻辑里有两个事务,每个事务逻辑里各自的锁后,又去申请另一个事务的锁!导致两个事务相互锁等待导致死锁;超时控制可以解决交叉锁问题

高兵发场景下会导致大部分请求排队等待。性能存在问题

7-2-1-1-1. 延伸下免两个问题
  1. 数据库事务隔离级别,如果让系统支持海量并发 ,影响数据库的并发处理能力就是数据库隔离级别;四种隔离级别从低到高
    读未提交-》 读已提交-》可重复读-》可串行化

数据库隔离级别越高,系统的并发小性能就越差,所以串行化性能最差对事务就没有并发。
2. 基于乐观锁的方式实现分布式锁(select for update是悲观锁阻塞事务提交导致锁等待)以下通过版本号ver,old_ver的值实现乐观锁(防止锁等待)。

  1. #select 同时获取ver值 
  2. select amount,old_ver from order where order_id=xxx; 
  3. # update的时候检查ver值是否与第2步获取到的值相同 
  4. update order set ver=old_ver+1,amount=yyy where ordder_id=xxx and ver=old_ver; 

如果更新结果的记录数为1,就表示成功,如果更新结果的记录数为0,就表示已经被其他应用更新过了(update的where条件不满足),需要做异常处理。

7-2-1-2. 方案2 基于分布式缓存实现分布式锁(加锁和释放锁的原子性解决)

数据库的性能限制了业务的并发量,所以针对“618和双十一大促”等请求量剧增的场景,要引入基于缓存的分布式锁,这个方案可以避免大量请求直接访问数据库,提高系统的响应能力。
redis实现腹部是锁

  1. SET lock_key unique_value NX PX 10000 

lock_key是key键;unique_value是客户端生成的唯一标识;NX代表只在lock_key不存在时,才对lock_key进行设置操作;PX 10000表示设置lock_key的过期时间为10s,这是为了避免客户端繁盛异常而无法释放锁。

解锁的过程就是删lock_key,但不能乱删要保证解锁的客户端和加锁的客户端是一个(通过unique_value判断,选择Lua脚本解锁是为了保证解锁操作的原子性)。

  1. //释放锁时,先比较unique_value是否相等,避免锁的误释放 
  2. if redis.call("get",KEYS[1])==ARGV[1]then 
  3. return redis.call("del",KEYS[1]) 
  4. else 
  5. return 0 
  6. end 

redis执行lua脚本可以原子执行

7-2-1-2-1. 深度问题
  1. 不能仅停留在操作上,还要请求redis分布式锁的优缺点
  2. redis的超时时间设置问题
  3. 架构层面设计redis怎么解决集群下分布式锁的可靠性问题?
  • 优缺点
    • 优点: 性能高效2.实现方便(注意2.6.12版本后,否则set key和超时时间是两个命令会导致set key成功超时失败,后续线程无法获取锁造成死锁)3. 避免单点故障
    • 缺点:主要是超时间设置不合理

解决:可以基于续约的方式设置超时时间,先设置一个超时时间,写一个守护进程,当锁失效时续约加锁,当主执行线程完成的时候,进行销毁续约锁即可,实现比较复杂;结合业务实现场景,redis集群节点异步导致高可用问题=》redis官方已经设计了分布式锁算法RedLock解决这个问题。

7-2-1-2-2. RedisLock 原理解决锁可靠性

客户端依次向redis多个实例进行加锁,即使某个实例出现问题,其他实例依旧有锁,上述问题B客户端依旧可以找到自己的锁。

我们假设目前有N个独立的Redis实例,客户端先按顺序依次向N个实例执行加锁操作,这里的加锁操作和单实例执行的加锁操作一样,需要注意点 是Redlock算法设置了加锁的超时时间,为了避免因为某个redis实例发生故障而一直等待的情况

客户端完成了和所有redis实例的加锁操作之后,如果有超过半数的redis实例成功获取到了锁,并且总耗时没有超过锁的有效时间,那么就是加锁成功;

7-2-1-3. 方案3 zk是如何设计和解决锁的问题?优缺点是什么?

对于 ZK 分布式锁而言:
优点:

  • ZK 天生设计定位就是分布式协调,强一致性。锁的模型健壮、简单易用、适合做分布式锁。
  • 如果获取不到锁,只需要添加一个监听器就可以了,不用一直轮询,性能消耗较小。
    缺点:
    ZK 也有其缺点:如果有较多的客户端频繁的申请加锁、释放锁,对于 ZK 集群的压力会比较大
7-2-1-3-1. 解决方式:Zk分布式锁有两种实现方式。
  1. 一种比较简单,应对并发量不是很大的情况。

获得锁:创建一个临时节点,比如/lock,如果成功获得锁,如果失败没获得锁,返回false;

释放锁:删除/lock节点;

锁等待:使用监听机制,监听lock节点,如果lock节点被删除,重新去抢锁,否则一直等待。

  1. 第二种方式,这种方式比第一种复杂点,但解决了羊群效应问题。

羊群效应:所有的客户端都尝试对一个临时节点去加锁,当一个锁被占有的时候,其他的客户端都会监听这个临时节点。一旦锁被释放,Zookeeper反向通知添加监听的客户端,然后大量的客户端都尝试去对同一个临时节点创建锁,最后也只有一个客户端能获得锁,但是大量的请求造成了很大的网络开销,加重了网络的负载,影响Zookeeper的性能

获得锁:创建临时带序号的节点,排序,判断创建的节点是否是当前目录下最小的,如果最小获得锁结束;

如果不是,获得当前节点的前面一个节点名称,进入锁等待;

释放锁:删除创建的临时带序号节点;

锁等待:获取第一步的获的前一个节点名称,使用监听机制,监听这节点,当这个节点被删除的时候,重新去抢锁。

zk分布式锁过程逻辑
zk分布式锁过程逻辑

7-2-2. 总结

分布式锁是解决多个进程同时访问临街资源的常用方法,在分布式系统中非常普遍,常见的实现方式是基于数据库,基于redis,在同等服务器配置下redis性能最好,数据库最差。
对于分布式锁,要从解决可用性,死锁,脑裂等问题触发展开回答,各分布式锁的实现方案的优缺点和适用场景;

7-2-2-1. 还要考虑以下四种锁的设计原则:
  1. 互斥性:同一共享资源同一时间点只有一个线程
  2. 高可用:不能单点,集群;保证一台不能服务,其他仍能服务。
  3. 锁释放:防止死锁,被动解锁能力
  4. 可重入:一个节点获取锁后,还可以获取整个锁资源。

线程进程--(事务《--》锁)--数据存储的关系
线程进程--(事务《--》锁)--数据存储的关系

8. 如何再面试中展现出造轮子的能力?

8-1. 初级面试(超时时间,重试次数)

初期:RPC远程调用实现微服务的考察
主流RPC框架有很多,工作中基本上都是拿来即用停留在基础概念和使用上,不会深究技术实现。

  • 基础问题:
    RPC的一次调用过程怎样的?RPC的服务发现如何实现的?RPC的负载均衡有哪些?

大多说面试官会从“实践操作”+“原理掌握”两个角度出发递进考察候选人。

电商app详情页每次刷新页面,app都会请求业务网关系统,网关系统远程调用多个下游服务,对于整条调用链路怎么设置RPC超时时间,需要考虑哪些问题?

App调用网管超时时间要大于网关系统调用各服务的超时时间之和,从实践角度来看不合格。

RPC都有超时重传机制,如果触发超时重传对app来说存在请求等待超时的风险,就会出现后端服务还没来得及做降级处理,详情页就等待超时了。

  1. RPC过程涉及其他知识点:考虑平均时长,超时时间,重试次数依据什么设置?
  2. 怎么区分哪些服务科重传,哪些不可重传?
  3. 超出重传次数,一般会触发服务降级,对商品详情页有什么影响?
  • 结合TP99请求耗时:需要结合每一个微服务的TP99耗时,结合业务场景综合衡量;

  • RPC调用方式要站在业务场景下,网管调用各服务的串并行方式,分析是否存在服务上下依赖;

  • 分析河西服务:哪些是核心服务,核心服务是否有备用方案,非核心服务是否有降级策略;

    解答“实践操作类面试题”要结合理论和落地时间,有理有据,有理是分析问题,有据是落地(细节)实践经验

8-2. 深入考察RPC原理(序列化,网络通信性能-协议)

网关每秒接收2w QPS每个微服务就要接收2W QPS的RPC请求,3个服务就要6w/s,怎么设计RPC框架实现?

  1. 优化RPC框架的网络通信性能:高并发下选择高性能的网络编程I/O模型
  2. 选择合适的RPC序列化方式,进而提升封包和解包的性能。

对于中间件不推荐造轮子但是要对工具框架考察造轮子的能力可以切入是否对原理了解,技术栈全面认知

RPC远程调用涉及网络通信;通信必须是2禁止数据,转换算法且可逆 ,这个转换即序列化和反序列化;

网络通信中发送数据可以是一个一个(半包)发送也可以是几个包在一起发送(粘包)为了解决包的问题需要提前约定数据格式即“RPC协议“,大多数协议包含数据头和消息体;头用于识别体用于请求的业务参数和扩展属性信息。

确定好RPC协议后,一次完整的RPC调用有如下步骤:

  1. 调用方持续把请求参数对象序列化成二进制数据,经过TCP传输服务提供方
  2. 服务提供方从TCP通道里接收到二进制数据
  3. 根据RPC协议,服务提供方讲二进制数据分割出不同的请求数据,经过反序列化讲二进制逆向还原出请求对象,找到对应的实现类,完成真正的方法调用
  4. 然后服务提供方再把执行结果序列化后,回写到对应的TCP通道里面
  5. 调用方获取到应答的数据包后,再反序列化成应答对象

8-2-1. 序列化选型(空间(序列化后体积),时间(序列化时间),兼容性)

  1. JSON:key_value结构,易用且广泛,基于HTTP协议的RPC框架会选择JSON序列化方式,但是空间开销大,通信时需要更多的内存。
  2. Hessian:一种紧凑的二进制序列化框架,性能体积都比较好
  3. Protobuf:Google的序列化标准,序列化后比以上两个都小,兼容性不错。

回答:如果序列化时间比较大就会影响增加rep,resp响应时间,如果序列化体积大,网络吞吐量下降,也会导致响应时间性能下降。

考虑版本迭代,需要考虑序列化协议的兼容性RPC框架要稳定。

8-2-2. 提升网络性能

一个RPC框架选择高性能的网络IO模型(同步阻塞IO(BIO),同步非阻塞IO,IO多路复用(NIO),信号驱动,异步IO(AIO))

8-2-2-1. BIO(两个阻塞问题:服务端等待连接11行,工作线程等待客户端发送数据27行)
  1. public class BIOServer{ 
  2. ServerSocket ss=new ServerSocket(); 
  3. //绑定端口9090 
  4. ss.bind(new InetSocketAddress("localhost",9090)); 
  5. try{ 
  6. Socket s=null; 
  7. while(true){ 
  8. //阻塞等待客户端发送连接请求 
  9. s=ss.accept(); 
  10. new Thread(new ServerTaskThread(s)).start(); 
  11. } 
  12. }catch(Exception e){ 
  13. // 
  14. }finally{ 
  15. if(ss!=null){ 
  16. ss.close(); 
  17. ss=null; 
  18. } 
  19. } 
  20. } 
  21.  
  22. public class ServerTaskThread implements Runnable{ 
  23. //... 
  24. while(true){ 
  25. //阻塞等待客户端发送数据请求过来 
  26. String readLine=in.readLine(); 
  27. if(readLine==null){ 
  28. break; 
  29. } 
  30. //.. 
  31. } 
  32. //.. 
  33. } 

所以BIO网络模型中,每当客户端发送一个连接请求就是一个Socket,服务端就会启动一个新线程去处理客户端连接的读写操作,这回导致服务器资源不够用无法满足高并发网络开发,适用于socket连接不多的场景。

8-2-3. 解决BIO问题用NIO

NIO比BIO提高了服务端工作线程的利用率,增加了一个调度者,实现了Socket连接与Socket数据读写的分离

8-2-3-1. 扩展技术点(Reactor模型具备以下三点)
  • 基于事件驱动-> selector(支持对多个socketChannel的监听)
  • 统一的事件分派中心-> dispatch
  • 事件处理服务-> read & write

三种角色:

  • Reactor 将I/O事件分派给对应的Handler
  • Acceptor 处理客户端新连接,并分派请求到处理器链中
  • Handlers 执行非阻塞读/写 任务
8-2-3-1-1. Reactor模型的3中线程模型:
  • 单线程模型
  • 多线程模型
  • 主从Reactor线程模型
    reactor通过selector监听连接请求分发socketchannel连接acceptor负责创建handler(里面是thread),把channle和handler上

Select 是前面 I/O 复用模型介绍的标准网络编程 API,可以实现应用程序通过一个阻塞对象监听多路连接请求

  1. Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发
    1.1 如果建立连接请求, 则右Acceptor 通过 accept 处理连接请求, 然后创建一个Handler对象处理完成连接后的各种事件
    1.1.1 Reactor主线程 MainReactor 对象通过select 监听连接事件, 收到事件后,通过Acceptor 处理连接事件
    1.1.2 当 Acceptor 处理连接事件后,MainReactor 将连接分配给SubReactor
    1.2 如果不是连接请求,则由reactor分发调用连接对应的handler 来处理

  2. subreactor 将连接加入到连接队列进行监听,并创建handler进行各种事件处理,当有新事件发生时, subreactor 就会调用对应的handler处理

  3. handler 通过read 读取数据,分发给后面的worker 线程处理

  4. worker 线程池分配独立的worker 线程进行业务处理,并返回结果

  5. handler 收到响应的结果后,再通过send 将结果返回给client

Reactor 主线程可以对应多个Reactor 子线程, 即MainRecator 可以关联多个SubReactor

Netty就是基于Reactor模型的java高性能网络框架。

8-3. 总结

  1. 超时时长和重试次数
  2. RPC网络通信IO Reactor模型和序列化
  3. netty框架,阅读一些成熟的框架源码,如谷歌的proto序列化,JRPC框架具备造轮子能力
  4. 一个产品级RPC框架开发处理要求网络通信,序列化,协议基础功能,还要具备链接管理,负载均衡,请求路由,熔断降级,优雅关闭等高级功能设计。

ACCESS_DENIED在windows系统上会出现换行符问题,或者链接拒绝等情况。

9. MQ消息队列丢失,重复,积压问题

9-1. 怎么确保100%不丢

9-1-1. 回答思路

怎么知道有消息丢失?哪些环节可能丢失?如何确保消息不丢失?

  • 生产阶段:收到MQ Broker的ack确认,表示发送成功
  • 消息存储阶段:MQ自己做副本至少两个阶段同步,再返回ack(分布式组件的存储设计)
  • 小费阶段:消费收到后要等执行完业务逻辑再回答ack确认,也能保证不丢。

看似万无一失,但是分布式系统故障不可避免,还需要一种机制(failover)Check消息是否丢?

9-1-1-1. 消息检测
  1. 生产端为每条消息指定唯一ID(比如递增版本号),消费端做ID校验
    具体落地实现:
    利用拦截器机制,在生产端发送消息之前,通过拦截器将消息版本号注入到消息中;然后消费端收到消息后,再通过拦截器检测版本号的连续性或消费状态,这样实现的好处是消息检测的代码不会侵入到业务代码中,可以通过单独的任务来定位丢失的消息,做进一步的排查。

设计解决方案参考:基于MQ的可靠消息投递机制;做到能检测,能可靠投递。

10. 怎么解决被重复消费的问题(重试可能导致重复消费)?

即如何解决消费端幂等性问题?(幂等性就是一条命令任意多次执行所产生的的影响与一次执行的结果相同)

消息日志表中增加一条消息记录(消息ID,是否已完成更新),根据消息记录,异步操作更新用户京豆余额;
基于幂等性这个思路不仅可以是关系数据库,也可以是redis实现唯一约束的方案。

解决丢失,重复的问题前提实现全局唯一ID的技术方案。

方案 顺序性 重复性 存在问题
数据自增主键 递增 不会重复 数据库宕机不可用
UUID 无序 通过多位随机字符串做到低重复,理论上会重复 一直可用
Redis 递增 RDB持久化模式下,会出现重复 Redis宕机不可用
Sonwflake 递增 不会重复 时钟回拨

时钟回拨涉及两种情况①实例停机→时钟回拨→实例重启→计算ID ②实例运行中→时钟回拨→计算ID

Snowflake用机器id和时间戳生成唯一id所以要保证以下问题其中一个不出现即可:

  • 造成时钟回拨的原因多种多样,可能是闰秒回拨,可能是NTP同步,还可能是服务器时间手动调整。总之就是时间回到了过去
 if (refusedSeconds <= 5) {
   try {
   //时间偏差大小小于5ms,则等待两倍时间
   	wait(refusedSeconds << 1);//wait
   } catch (InterruptedException e) {
   	e.printStackTrace();
   }
   currentSecond = getCurrentSecond();
}else {//时钟回拨较大
   //用其他策略修复时钟问题
}
  • 另外wokerId(机器节点ID配置)不会重复(UidGenerator源码项目)

10-1. 消息积压

如果发生消息积压易订是性能问题(一定发生在消费端)

  1. 如果是线上突发问题,就要临时扩容,增加消费端的数量,于此同时,还可以降级一些非核心业务,通过扩容和降级抗住流量,表名自己对线上问题的紧急处理能力。(在扩容消费者实例数量时必须同步增加topic的分区分片数量,否则扩容没有意义
  2. 排查解决异常问题:比如查看监控,日志等分析是否消费端业务逻辑代码出现问题,进而优化消费业务处理逻辑。

如果是基础架构部:还要掌握消息中间件其他知识体系如:
如何选型中间件?消息中间件的队列模型,发布订阅模型区别?为什么消息队列实现高吞吐?序列化,传输协议,内存管理等问题?(主要是中间件底层设计存储和内存的存储设计)

  • 与队列相比,发布者就是生产者,订阅者就是消费者,主题就是队列,他们之间最大的区别就是:一份消息能不能被消费多次

10-2. kafka如何实现高性能

  1. 每个Topic都包含一个或多个Partition,不同Partition可位于不同节点。
  2. 每个Partition包含一个或多个Segment,每个Segment包含一个数据文件和一个与之对应的索引文件
  3. 即使同一节点上的不同Partition也可以置于不同的磁盘;发挥多借点多磁盘的读写并行优势
  4. LSM被设计来提供比传统的B+树或者ISAM更好的写操作吞吐量,利用磁盘的顺序读写
  5. Page Cache,其中文名称为页高速缓冲存储器,简称页高缓;Kafka收到数据后,写磁盘时只是将数据写入Page Cache,并不保证数据一定完全写入磁盘,可以借助kafka复制机制避免数据丢失。
  6. Kafka中存在大量的网络数据持久化到磁盘(Producer到Broker)和磁盘文件通过网络发送(Broker到Consumer)的过程;通过直接基于内核Buffer实现Kafka的数据传输通过Java NIO的FileChannel的transferTo和transferFrom方法零拷贝无需CPU。

11. Mysql索引原理和优化问题

  1. 订单筛选如何优化效率
 select * from order where status=1 order by create_time asc
  • 建立一个status和create_time 组合索引
    这样避免Mysql发生文件排序,因为查询时如果只用到status索引,对create_time排序就要用到filesort,即执行计划中Extra 列会出现Using filesort;所以要利用索引的有序性,在status和create_time建立联合索引,这样根据status筛选后的数据就是按照create_time排好序的避免再fileSort.

11-1. 索引原理和优化策略的一系列问题:

  1. 数据库索引底层使用的是什么数据结构和算法?
索引类型 InnoDB引擎 MyISAM引擎 Memory引擎
B+Tree索引 YES YES YES
HASH索引 NO NO YES
Full-Test索引 NO YES NO
  1. 为什么Mysql InnoDB虚着呢B+Tree做默认索引数据结构
  2. 如何通过执行计划查看索引使用详情?
  3. 有哪些情况会导致索引失效?
  4. 平常有哪些常见的索引优化方法?
  • 问题总结下拉如下几点
    mysql 索引原理
    B+Tree相比较其他结构的优势
    查看执行计划的方法
    索引失效情况
    建立高效索引的技巧。(前缀索引,覆盖索引等)

11-1-1. 索引原理

建商品表

ID 编号 名称 价格
1 001 商品1 500
3 003 商品3 200
5 005 商品5 500
6 006 商品6 600
8 008 商品8 700
10 010 商品10 900
12 012 商品12 120
15 015 商品15 200
58 058 商品58 1000
60 060 商品60 800

执行select * from product where id=15时按照索引如何找到数据?

手动创建一个B+Tree:每个节点含三个子节点(B+Tree允许有M个子节点M>2)

根节点的值:1,18,36分别是子节点(1,6,12)。(18,24,30),(36,41,52)中的最小值。

叶子节点包含所有数据值信息非叶子节点记录key
叶子节点包含所有数据值信息非叶子节点记录key

15在1层根发现在1~18之间;继续找两个子节点2层:找三个子节点1,6,12发现15>12所以15在第三个子节点后面,继续第3层找到链表12,15这个数=》整个过程共进行3次IO操作,所以B+Tree相对二叉树查询效率高

如果查询数据不是ID而是商品编码,就要通过非主键(辅助索引)查询:
先检索辅助索引B+Tree的商品编码找到对应的叶子节点,获取主键值,然后通过主键索引中B+Tree查询到对应的叶子节点,获取整行数据,这个过程叫回表

B+树中的B代表平衡(balance),而不是二叉(binary),因为B+树是从最早的平衡二叉树演化而来的。在讲B+树之前必须先了解二叉查找树、平衡二叉树(AVLTree)和平衡多路查找树(B-Tree),B+树即由这些树逐步优化而来。

参考:https://blog.csdn.net/a764340703/article/details/82621781

  1. 二叉树具有以下性质:左子树的键值小于根的键值,右子树的键值大于根的键值;深度为1的节点的查找次数为1,深度为2的查找次数为2,深度为n的节点的查找次数为n,平衡的目的就是二叉树高度最小,否则二叉树查询效率就越低。

  2. 平衡二叉树(AVL树)在符合二叉查找树的条件下,还满足任何节点的两个子树的高度最大差为1;如果在AVL树中进行插入或删除节点,可能导致AVL树失去平衡,这种失去平衡的二叉树可以概括为四种姿态:LL(左左)、RR(右右)、LR(左右)、RL(右左);可以通过旋转重新恢复平衡。

  3. B-Tree是为磁盘等外存储设备设计的一种平衡查找树;InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB存储引擎中默认每个页的大小为16KB;一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据;

    如图B-Tree相对于AVLTree缩减了节点个数

一棵m阶的B-Tree有如下特性:

  1. 每个节点最多有m个孩子

  2. 除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子

  3. 若根节点不是叶子节点,则至少有2个孩子

  4. 所有叶子节点都在同一层,且不包含其它关键字信息

  5. 每个非终端节点包含n个关键字信息(P0,P1,…Pn, k1,…kn)

  6. 关键字的个数n满足:ceil(m/2)-1 <= n <= m-1

  7. ki(i=1,…n)为关键字,且关键字升序排序

  8. Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)

  9. B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度!B+Tree的高度一般都在2~4层,mysql把根节点常驻内存所以最多1-3次IO操作即可查询到。

如图B+书非叶子节点只存key
如图B+书非叶子节点只存key

面试官一般不会让直接描述查询索引的过程,会考察队索引优化方法的理解,评估对索引原理的掌握程度,比如:

  • 为什么mysql innodb用b+做索引结构?
  1. B+原理2.B+的优势
    优势:

    1. B+树只在叶子存数据,而B树非叶子也存,所以B+树单个节点数据量小相同IO次数查询能更多的节点。
    2. B+叶子节点采用的是双链表链接,适合mysql中常见的范围顺序查找,而B树无法做到这点。
    • 对于有N个叶子节点的B+,搜索复杂度是O(logdN),其中d是最大子节点个数,实际应用中d的值是大于100的,这样保证了数据达到千万级别,B+树依旧在34层,最多34次IO操作就能查到数据。
    • 而二叉树每个父节点只能有2个子节点,意味着复杂度是O(logN) 底数d是2,这个比B+高出不少复杂度,因此IO次数更多。
    1. 与HASH表比较,HASH不适合范围查找适合等值查找,所以B+使用场景更广泛。

    回答过程先说优势,再引入索引原理的查询过程。

  • 有哪些优化方法?

11-1-2. 执行计划

对于执行计划索引解读:

索引计划的参数解读
索引计划的参数解读

11-1-2-1. 索引失效的情况(是否走索引是mysql优化器预估索引扫描代价是否大于全表扫描)
  • 比如模糊查询(会评估%模糊条件发现左右子节点都有可能符合条件,优化器就会决定当前要扫描整个索引,并且还要回表查询,不如全表扫描所以就没用到索引)
  • 其他索引失效:索引列上做了计算,函数,类型转换操作,查询过程中需要扫描秒整个索引并回表,代价高于全表扫描;like匹配使用了前缀匹配符“%abc",字符串不加引号导致类型转换。
11-1-2-1-1. 优化方法
  1. 前缀索引优化:比如字符串使用前几个字符建立索引,减少索引字段大小增加索引页存储的索引值,提高索引查询效率;局限性是order by无法使用前缀索引,无法把前缀索引用作覆盖索引(覆盖索引是select的数据列只用从索引中就能够取得,不必读取数据整行,换句话说查询列要被所建的索引覆盖。)

  2. 覆盖索引优化:sql中query的所有哦字段在B+所有叶子节点都能找到那些索引,从辅助索引中查询到记录,而不许需要通过聚簇索引查询获得,假设我们只需要查询商品名称,价格,有什么办法避免回表呢?

    我们可以通过建立组合索引商品id,名称,价格;如果索引存在这些数据就不会检索主键索引,从而避免回表。所以覆盖索引好处不需要查询整行数据减少大量IO操作。

  3. 联合索引:存在最左匹配原则(区分度大在前),按照最左优先进行索引的匹配,比如联合索引(userpin,username)
    where userpin=1 and username=2 和where userpin=1都能匹配联合索引而where username=2不可以。联合索引的字段顺序对索引效率影响很淡,越靠前的字段被用于索引过滤的概率越高,实际开发工作中减联合索引要把区分度大的字段发昂在前面,这样越有可能被更多的sql用到
    区分度=distinct(column)/count(*)

比如性别区分度就很小不适合建联合索引或放到联合索引前面,而uuid这类就适合。

11-2. 总结

  1. B+减少IO次数;2.执行计划查看索引失效3.针对索引失效进行优化

11-2-1. mysql索引使用原则

索引是否需要判断原则
索引是否需要判断原则

11-2-2. 索引的问题

  1. 带来数据写入延迟,额外空间消耗
  2. mysql海量数据索引提升查询效率有限(其他方案读写分离,分库分表)

12. mysql事务的隔离级别和锁的机制

12-1. 隔离级别

读未提交<读已提交<可重复读<串行化


四种隔离级别对应问题

  • 脏读(读取未提交数据):A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据

    脏读现象

  • 不可重复读 (一个事务读取同一条记录2次,得到的结果不一致)#F44336
    事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。(前后多次读取,数据内容不一致,业务同一个事务前后不可以重复读取同样的数据)。

不可重复度现象:读未提交读已提交都会发生此现象
不可重复度现象:读未提交读已提交都会发生此现象

  • 幻读(一个事务读取2次,得到的记录条数不一致)
    这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务

幻读现象
幻读现象

12-2.

悲观锁:认为锁冲突可能性大,乐观锁:认为锁冲突可能性小。

  • 悲观锁:利用select ..for update 对数据加锁避免其他事务修改数据

  • 乐观锁:通过CAS机制,并不会对数据加锁,而是通过对比数据的时间戳或者版本号,实现版本判断。

  • 高频考点:

  • 脏读,不可重复读,幻读说明

  • mysql 怎么解决脏读(升级隔离级别),不可重复读(升级隔离级别),幻读问题(行锁解决不了,间隙锁解决)

    •  行锁:Record lock,主要更新删除是,等值查询条件是唯一索引时采用的
    •  间隙锁:Gap lock,其实它是行锁中的一种。它锁定的是一个范围区间的索引,遵守左开右闭原则。
    •  Next-Key Lock: 行锁+间隙锁的组合;等值查询用行锁,范围查询用间隙锁。

范围查询时间隙锁工作原理
范围查询时间隙锁工作原理

参考:https://blog.csdn.net/zhanshenzhi2008/article/details/107341636

  1. 怎么理解死锁?如何产生,如何避免?
  • 死锁产生的必要条件(4个满足才能发生):

    1. 互斥:多个线程不能持有一个资源
    2. 持有并等待:A持有了资源1,想获取资源2,但是2被B持有只能等待,而且不会释放资源1
    3. 不可剥夺:A获取了1,在执行完,并不能被其他线程抢占使用资源1
    4. 循环等待:发生死锁时,线程核资源产生请求等待的环形圈。
  • 避免发生死锁(破坏其中一个条件):

    1. 持有并等待:可以一次性申请所有的资源
    2. 不可剥夺:占用部分资源的线程,进一步申请其他资源,如果申请不到,可以主动释放占用它的资源
    3. 循环等待:可以靠按序申请资源(有序分配)来预防。

12-3. 其他知识

  1. 数据库设计基础:基本范式,概念,业务实体关系-数据库结构映射,ORM对象映射关系的开发
  2. 隔离级别:MVCC机制进阶问题的解决,不同索引类型的使用,底层结构优缺点。
  3. SQL优化
  4. 数据库架构设计:读写分离,分库分表

问题:MVCC(多版本并发控制,读写不冲突,写写冲突)和乐观锁的区别?

  1. MVCC 可以保证不阻塞地读到一致的数据。但是,MVCC 并没有对实现细节做约束,为此不同的数据库的语义有所不同,比如:postgres 对写操作也是乐观并发控制;在表中保存同一行数据记录的多个不同版本,每次写操作,都是创建,而回避更新;在事务提交时,按版本号检查当前事务提交的数据是否存在写冲突,则抛异常告知用户,回滚事务;innodb 则只对读无锁,写操作仍是上锁的悲观并发控制,这也意味着,innodb 中只能见到因死锁和不变性约束而回滚,而见不到因为写冲突而回滚;不像 postgres 那样对数据修改在表中创建新纪录,而是每行数据只在表中保留一份,在更新数据时上行锁,同时将旧版数据写入 undo log;表和 undo log 中行数据都记录着事务ID,在检索时,只读取来自当前已提交的事务的行数据;可见 MVCC 中的写操作仍可以按悲观并发控制实现,而 CAS 的写操作只能是乐观并发控制
  2. 还有一个不同在于,MVCC 在语境中倾向于 “对多行数据打快照造平行宇宙”,然而 CAS 一般只是保护单行数据而已

13. mysql如何优化查询方案?(读多写少-读写分离->提高并发;解决复制延迟缓存解决)

订单系统:如何应对十一大促读写流量;如果是用redis做mysql缓存,帮助mysql抗住大部分查询订单中心的请求
应用缓存的原则之一就是保证缓存命中率高:而订单中心查询每个用户都不同,命中率低!!
因此此场景,缓存只能是数据库的前置保护机制,mysql的请求流量还是很大;
互联网系统访问流量大部分是读多写少,差距可能几个数量级,所以考虑优化数据库抗住查询请求,区分读写流量区,做读写分离,方便读的扩展。


读写分离必须是主+从集群

13-1. 主从复制原理

mysql集群如何实现主从复制(提交一个事务到mysql集群,mysql做了哪些操作?)切入对主从复制原理理解,模拟一个业务场景,让给出主从复制的设计解决方案

回答问题需要准备:

  1. 掌握读多写少场景的架构设计思路,知道缓存不能解决本质问题,读写分离才是提升系统并发能力的手段
  2. 了解数据库主从复制的原理,问题,以及解决方案
  3. 从实践触发,做到技术的抽象认知(实践),从方法论(设计原则标准)层面看设计。

13-1-1. 主从复制原理过程

mysql的主从复制依赖binlog,也就是记录mysql所有变化的二进制文件,复制过程就是把binlog的数据从主库传输到从库(一般异步)。
三个阶段


三阶段需要三个线程实现同步

  1. 主库收到客户端请求先写binlog,再提交事务,更新数据,事务完成返回客户端”操作成功“响应
  2. 主库log dump线程同步传输binlog日志,从库创建专门的IO线程,连接log dump线程,接收binlog日志,再把日志信息写入ralay log的中继日志里,再返回给主库“复制成功”的响应
  3. 从库再创建一个用于回放binlog的线程,读取relay log,回放binlog更新从库的数据,最终实现主从一致性。

完成主从复制之后,就可以在写数据时只写主库,读数据只读从库,这样即使写请求会锁表或者锁记录,也不会影响读请求的执行;流量大时多个从库共同承担读流量,从库也可以做为备库,以避免主库故障导致数据丢失

一主多从:是不是流量大的时候增加从库就坑抗住并发读请求了?不是!
因为从库增加,对主库资源消耗比较高,同时受限主库的网络带宽,实际中一个主一般跟2~3个从库,这就是一主多从的集群结构;
mysql默认异步模式,主库提交事务并不会等待binlog同步到各从库就返回客户端结果,一旦主库宕机(主库没落存日志和磁盘)就会发生数据丢失。mysql主从复制还有哪些模型?

  1. 同步复制:事务线程等待所有从库复制成功响应2.异步复制:事务线程不等从库的复制成功响应3.mysql5.7之后增加了一种只需要一部分复制成功响应回来就行的半同步复制
13-1-1-1. 挖掘架构设计能力:主从复制延迟问题

解决方案1:
直接查询主库,要明确查询的数据量不大,否则主库写请求锁行,影响读请求的执行,最终造成主库压力。
扩展问题:主从分离后,以前需要一个数据库地址,现在却需要写一个主库地址多个从库地址还要区分写操作和查询操作,工程代码设计上,怎么实现主库从库的方案?

  • 做法1(复杂工程不利于代码维护)
  1. 配置文件配置多数据源
  2. 通过设置和存储DataSource集合的Map相同的key,以此达到选择不同DataSource的目的,实例需要动态选择
  3. 通常操作数据的业务逻辑都放在service层,我们希望service中不同方法使用不同的库;比如:添加、修改、删除、部分查询方法等,使用master主库来操作,而大部分查询操作可以使用slave库来查询;这里通过拦截器+灵活的自定义注解来实现.。
  • 做法2:独立部署代理中间件
    一般代理中间件使用标准的mysql通信协议,可以代理多个数据库,有点是隔离底层数据库和上层应用的访问复杂度,缺陷是所有sql语句都要跨两次网络传输,性能损耗,需要一定的技术沉淀。

方案2:使用数据冗余
可以在异步调用审核模块时,不仅仅发送商品ID,而是发送审核模块需要的所有评论信息,借此避免在从库中重新查询数据,但要注意每次调用的参数大小,过大的消息会占用网络带宽和通信时间。

方案3:使用缓存解决
可以在写入数据主库的同时,把评论数据写到Redis缓存里,这样其他线程再获取评论信息时会优先查询缓存,也可以保证数据一致性但是存在数据库和缓存数据一致性问题如下表。

线程A 线程B
第一步 更新数据库100 --
第二步 -- 更新数据库200
第三部 -- 更新缓存200
第4步 更新缓存100 --

解决办法:通过锁控制方式保证缓存和数据库数据一致。

13-2. 高级和架构师要求技术认知(不是mysql特有的)

尽可能展示对mysql数据复制的抽象能力。
通过日志复制同步的方式达到共识,领导者选举成功后leader接受客户端请求,每个客户端请求拆分成指令日志,并行的发送给其他节点,要求其他节点复制这个日志条目,最终其他节点复制日志并回放,最终达到各节点数据一致性。这种数据库共识的机制就叫复制状态机,目的是通过日志复制和回放来实现集群中所有节点内的状态一致性。

在Redis Cluster中也用到了backlog来实现主从节点的数据复制;几乎所有存储系统或者数据库基本都用了这套方法解决数据复制和备份恢复等问题。

13-3. 总结

主从复制原理,模式,存在问题,怎么解决;抽象出认知能力:复制状态机原理和应用场景。

读写分离能抗住多大规模?2020 阿里双十一订单创建峰值达到了惊人的58.3万笔/秒;并发数 = QPS✖平均响应时间;
QPS不等于并发连接数,QPS是每秒HTTP请求数量,并发连接数是系统同时处理的请求数量:
峰值每秒请求数(QPS) = (总PV数 * 80%) /(6小时秒数 * 20%)

14. mysql如何优化存储方案?(写多读少 )

交易突破千万级,数据库写性能下降导致查询和写入响应慢。

分库分表:三种实现,垂直拆分,水平拆分,垂直水平拆分。

主要是问业务场景中写多读少时分库分表的整体设计方案和落地思路?

14-1. 什么场景该分库,什么场景该分表?

  • 数据量过大事务中的查询或者更新操作执行缓慢时考虑分表
  • 为了应对高并发,一个库实例扛不住,就把并发请求分散到多个实例中,分库
    分表是因为数据量大(单表),分库是单库的连接性能无法满足要求。

14-2. 复杂的业务如何选择分片策略(最终还是水平扩展)?

  • 垂直拆分:根据数据的业务相关性进行拆分垂直拆库常伴随着系统架构上的调整;比如,商品数据放到商品库,订单数据放到订单库中。
    特点:垂直拆分可以把不同的业务数据进行隔离,更有助于架构上的扩展,但它依然不能解决某一个业务的数据大量膨胀的问题,一旦系统中某一个业务库数据量激增,就要把数据拆分到多个库和数据表中,就是数据做水平拆分
  • 水平拆分是指把单一库表数据按照规则拆分到多个数据库和多个数据表中,拆分出来的表可以分别存放到不同的物理数据库中,关注点是数据扩展,拆分规则就是哈希分片和范围分片。

14-2-1. 范围分片

按照某一个字段的区间来拆分,最好理解的是按照时间字段分片,但是按时间的场景并不多,因为会导致数据粉白不均,最常见的Range分片是按照字段类型,比如商品所属品类进行分,与hash分片不同range就可以加入对于业务的预估,同样因为业务问题会出现热点问题。

14-2-1-1. 针对范围分片热点数据处理
  1. 可以垂直扩展:由于范围分片按照业务特性进行分片,所以对热点数据做垂直扩展,即提升单机处理能力,在业务发展突飞猛进初期建议使用“增强单机硬件性能”的方式提升系统处理能力,因为此阶段一般发展业务抢占市场。
  2. 分片元数据:结合业务的特性,需要在Range的基础上引入“分片元数据”的概念;分片的规则记录在一张表里面,每次执行查询的时候,先取表里查一下要找的数据在哪个分片中。这种方式优点是灵活性高,分片规则随着业务发展随意改动

分片元数据本身要做高可用,缺点:是实现复杂,需要二次查询,需要保证元数据服务高可用,不过分片元数据表可以通过缓存进行提速。

14-2-2. 垂直水平拆分

是综合垂直拆分和水平拆分的一种混合方式,垂直拆分把不同类型的数据存储到不同数据库中,再结合水平拆分,使单标数据量保持在合理范围内,提升性能。


垂直水平拆分案例图

14-3. 如何解决数据分片后的数据查询问题?

未分库分表查询总数可以直接通过sql count()命令,现在分片到多个库表中如何解决?

  1. 可以考虑其他存储方案,比如聚合查询使用频繁时,可以将聚合查询的数据同步到ES中,或者将技术的数据单独存储在一张表中;如果是每日的形式生成的统计类报表数据,也可以将数据同步到HDFS中,然后用大数据技术生成报表。

14-4. 技术认知

  1. mysql是后端需要精通的技能,但是下一代存储出现了newSQL会取代mysql;newSQL是新一代分布式数据库,具备分布式系统的高性能高可用,弹性扩容等能力,还兼容传统数据库sql标准。还提供了和传统数据库不相上下的事务保证,具备了支撑未来交易类业务能力的。
  2. 为了体现技术视野:可以谈一些存储相关的技术领域内容,比如new SQL发展及实现技术原理,讨论相关开源产品比如TiDB,和现有mysql区别切入面试和面试官讨论

14-5. 总结

垂直拆分(业务);水平拆分(数据)=》分片技术:HASH分片,Range分片(分片元数据)=》垂直水平拆分,存在,聚合查询问题大数据解决。

对商品表分库分表,商品关联表如何处理:可以冗余,也可以类似做动态规划分治路由到数据(分片元数据新信息)

15. Redis原理(像Redis和mysql这都是送分题需要回答的有亮点才是抓住机会了)

  1. Redis是单线程还是多线程?(redis线程模型)
    单线程的;优点:实现简单,可以无锁的情况下实现操作,不存在死锁和线程切换的性能消耗;缺点:不能发挥多喝CPU的性能。应基于单线程回答延伸亮点问题。
  2. 亮点部分:Redis只有单线程吗?
    Redis的单线程是指redis的IO线程,redis的持久化,集群同步等工作则是由另外的线程执行。
  3. Redis单线程为什么还那么快?
    Redis的大部分操作都是内存中完成;redis避免了多线程之间的竞争和死锁问题;redis采用了IO多路复用模型机制处理大量的客户端socket请求; 高效的数据结构比如hash表和跳表#4CAF50

15-1. redis4.0版本特性(高性能)

  • redis4.0之前的单线程块是以上几方面
  • reidis4.0之后,redis增加了多线程的支持,这时的多线程主要体现在大数据的异步删除功能上
  • redis6.0之后新增了多线程IO的读写并发能力,redis6.0多线程原理,这就会得到认可

随着硬件提升redis的IO瓶颈明显,6.0后采用多线程处理网络请求,但是对于读写命令仍是单线程处理!!


redis的核心原理考点从高性能高可用角度

15-2. 高可用之持久化

redis持久化怎么做的?=》数据不丢(持久化),服务高可用=》数据复制
为了保证数据不丢,redis需要把内存中的数据落地磁盘,以便缓存服务器重启能够恢复所有数据,这个过程就是redis的数据持久化。

15-2-1. 有三种持久化方式

  1. AOF日志:记录所有的操作命令,并以文本的形式追加到文件中
  2. RDB快照:将某一个时刻的内存数据以二进制方式写入磁盘
  3. 混合持久化方式:Redis4.0之后新增混合方式集成了RDB和AOF的优点。
15-2-1-1. AOF如何实现?

通常情况下:关系型数据库的日志都是“写前日志”,而AOF里记录的是Redis收到的每一条指令,这些命令以文本形式保存,不同的是AOF日志的记录顺序与传统关系数据库正好相反,它是“写后日志”,写后是指Redis要先执行命令,把数据写入内存,然后再记录日志到文件。
redis:1.执行命令写入内存2.记录日志写入磁盘。

15-2-1-1-1. redis为什么先执行命令,再把数据写入日志呢?

因为,redis在写入日志之前,不对命令进行语法检查,所以只记录执行成功的命令,避免了出现记录错误命令的情况,并且,在执行命令完之后再记录,不会阻塞当前的写操作
风险:1. 这条命令数据丢失2. 可能阻塞其他操作(因为aof主线程执行)。

15-2-1-2. RDB如何实现?

为了解决AOF方法做故障恢复时操作缓慢这个问题,redis增加了RDB内存快照的操作,它即可以保证可靠性,又能在宕机时实现快速恢复。
和AOF不同:rdb记录了redis某一时刻的数据,而不是操作,所以在数据恢复时候,只需要把rdb文件读入内存,完成快速恢复。

15-2-1-2-1. rdb做快照会阻塞线程吗(save 主线程阻塞bgsave子线程非阻塞)

为了解决阻塞线程问题,redis提供了两个命令生成rdb快照,save和bgsave;save在主线程执行,会导致阻塞,bgsave会创建子线程来进行写入rdb文件的操作,避免了对主线程的阻塞。(默认配置)

15-2-1-2-2. rdb做快照的时候数据能修改吗?

如果在执行快照的过程中,数据如果能被修改或者不能被修改会带来什么影响?

  • 如果此时可以执行写操作:意味着redis还能正常处理写请求,就可能出现正在执快照的数据是已经被修改了的情况
  • 如果此时不可以进行写操作:意味着redis的所有写操作都要等到快照执行完成之后才能执行,那么就会出现阻塞主线程的问题。
    解决方案:
  1. 如果主线程执行读操作,主线程和bgsave子进程互不影响
  2. 如果主线程执行写操作,则被修改的数据会复制一份副本,然后bgsave子进程会把该副本数据写入RDB文件,在这个过程中,主线程仍然可以直接修改原来的数据

redis对rdb执行频率非常重要,这会影响快照数据的完整性以及redis的稳定性,所以在redis4.0后增加了aof和rdb混合持久化方式;把数据以rdb方式写入文件,再讲后续的操作命令以aof的格式存入文件,既保证了redis重启速度,又降低了数据丢失风险

15-3. 如何设计redis高可用服务(副本)

  1. 主从同步(主从复制)2. 哨兵模式3. redis集群
  • 主从复制是高可用服务最基础的保证,实现方案是将从前的一台redis服务器,同步数据到多台从redis服务器上,即一主多从的模式,这样我们就可以对redis做读写分离了,来承载更多的并发操作,这里和mysql主从复制原理一样;(主从出现宕机需要手动恢复的问题解决:哨兵模式)
  1. Redis Sentinel(哨兵模式):当redis出现宕机,需要手动恢复,redis增加了哨兵模式,可以做到监控主从服务器,并且提供自动容灾恢复功能

  1. redis cluster(集群)

    redis cluster是一种分布式去中心化的运行模式,它将数据分布在不同的服务器上,以此来降低系统对单节点的依赖,从而提高redis服务的读写性能。

Redis方案采用hash槽来数据和实例之间的映射关系,在redis cluster方案中一个切片集群共有16384个hash槽,这些槽类似数据分区,每个键值对会根据它的key,被映射到一个槽中,具体有两步:

  1. 根据键值对key按照CRC16算法计算一个16bit的值
  2. 再用16bit值对16385取模,得到0~16383范围的模数,每个模数代表一个相应编号的hash槽。

15-3-1. redis 哈希槽怎么映射到具体redis实例的(怎么分配的?)

  1. 平均分配:在使用cluster create 命令创建redis集群时,redis会自动把所有hash槽平均分不到集群实例上,比如有9个实例每个实例16384/9个槽
  2. 手动分配:可以使用cluster meet命令手动建立实例间的连接,组成集群,再使用cluster addslots命令指定每个实例上的槽个数。

主从同步是高可用服务基础,故障恢复需要哨兵模式容灾;最常用的redis 集群方案去中心化。

15-4. 总结

  • 线程模型:(4.0:多线程大数据的异步删除功能,6.0版本界限:网络多路复用多线程;执行命令一直是单线程操作)
  • 持久化:aof,rdb混合
  • 高可用:主从同步,哨兵,集群

15-5. 思考题:如何实现redis和mysql数据一致性

  1. 缓存和库:数据不一致两种原因
    1.2、先删除缓存
    1、如果先删除Redis缓存数据,然而还没有来得及写入MySQL,另一个线程就来读取。
    2、这个时候发现缓存为空,则去Mysql数据库中读取旧数据写入缓存,此时缓存中为脏数据。
    3、然后数据库更新后发现Redis和Mysql出现了数据不一致的问题。

1.3、后删除缓存
1、如果先写了库,然后再删除缓存,不幸的写库的线程挂了,导致了缓存没有删除
2、这个时候就会直接读取旧缓存,最终也导致了数据不一致情况
3、因为写和读是并发的,没法保证顺序,就会出现缓存和数据库的数据不一致的问题
2. 解决不一致
方案2.1 延时双删策略
1、先删除缓存
2、再写数据库
3、休眠xxx毫秒(根据具体的业务时间来定)
4、再次删除缓存
导致问题:
1、在缓存过期时间内发生数据存在不一致
2、同时又增加了写请求的耗时

方案2.2 异步更新策略(基于binlog+消息订阅)
1、涉及到更新的数据操作,利用Mysql binlog 进行增量订阅消费
2、将消息发送到消息队列
3、通过消息队列消费将增量数据更新到Redis上
4、操作情况

15-5-1. 数据操作主要分为两种:

1、一种是全量(将所有数据一次性写入Redis)
2、一种是增量(实时更新)

16. 如何回答缓存策略(缓存穿透,并发,雪崩等问题)

16-1. 案例


一般业务使用缓存流程

并发高时有什么问题:面试套路:场景,问题-解决方案-问题-解决方案-落到地!!!
我们以电商平台商品详情页为例,商品详情页的缓存经常存在缓存穿透,缓存并发,缓存雪崩,以及缓存设计等问题,设计出一个高可用高性能的缓存架构方案:

16-2. 缓存穿透

16-2-1. 解决方案

方案1:通用方案
所有指定key预先设定一个默认值,比如空串“Null”,当返回这个空串“Null”时2,我们可以认为这是一个不存在的key,在业务代码中,就可以判断是否取消查询数据库中的啊哦做,或者等待一段时间再请求这个key.如果此时取到的值不在是“Null”,我们考科一认为缓存中存在对应key有值了,这就避免出现请求访问数据库的情况。

16-3. 缓存并发

假设在缓存失效的同时,出现多个客户端并发请求获取同一个key的情况,此时因为key已经过期了,那么所有请求就会到数据库中查询,当查询导数据之后,所有请求再重复将查询到的数据更新到缓存中。
问题:因为所有请求是同一条数据,不仅增加数据库压力,还会因为反复更新缓存占用缓存资源。这就是缓存并发。

16-3-1. 解决方案(状态位-锁)


缓存并发问题解决

16-4. 缓存雪崩

在实际开发中,程序员开发时,会将缓存设置过期时间固定的常量,这可能存在系统运行中,很多缓存key过期时间一样,他们集体失效,如果此时并发度高,就会造成数据库压力出现缓存雪崩现象。

16-4-1. 解决方案

方案1:将缓存失效时间随机打散,这样每个缓存的过期时间就不重复了,降低了缓存失效的概率
方案2:设置缓存不过期:我们可以通过后台服务来更新缓存数据,从而避免因为缓存失效造成的缓存雪崩,也看看一在易订程度上避免缓存并发问题。

16-5. 缓存设计策略

  1. 怎么设计一个动态缓存热点数据的策略?
    依旧电商平台场景为例:要求只缓存用户经常访问top 200的商品

策略整体思路:通过判断数据最新访问时间来做排名,过滤掉不常访问的数据,只留下经常访问的数据。
具体实现细节:先通过缓存系统做一个排序队列,系统根据商品访问时间,更新队列信息,越是最近访问的商品排名越靠前;同时系统会定期过滤掉队列中排名最后的若干商品,然后再从数据库中随机读取这些数的商品加入队列;这样请求到达的时候先从队列获取商品ID,如果命中再从另一个缓存数据结构中获取实际商品信息,并返回。再Redis中可以用zadd方法和zrange方法来完成排序队列和获取若干商品的操作。

这些策略可能会把业务代码和缓存数据耦合在一起,可维护性会越来越差。
2. 怎么设计一个缓存操作与业务分离的架构?(如何符合高内聚低耦合设计原则)

思路:讲缓存操作和业务低代码解耦,实现方案上可以通过mysql binlog+canal+mq的方式 ;比如用户在系统后台增加一套配置信息,


缓存操作和业务分离的架构

16-6. 总结

要考虑缓存的坑:比如与数据库一致性,缓存容量限制,缓存的数据大小等。
(maxmemory 配置Redis不会因为使用了过多的物理内存而导致swap,内存超过这个数值时, 将触发数据淘汰,官方说单例能处理key:2.5亿个;)
思考:如何用redis实现一个计数器:使用ncr() 累加计数

17. 如何证明自己的系统高可用

高级研发和架构师区别是驾驭系统的边界:符合成行历程:负责一个功能-负责一个系统模块-负责一个系统-负责多个系统或业务条线。

系统分两个阶段:

  1. 保证系统的稳定性和可用性
  2. 基于高流量场景下保证系统的并发承载能力

你再上家公司怎么设计系统架构的(框架可以随着技术进步进行更换,但是架构理论和实践经验是不变的),了解设计能力和思路;讲解的过程就是证明系统稳定和可用,高性能。

这会涉及一个公认的论证--SLA(服务等级协议规范)了双方的商务关系或部分商务关系,SLA是服务可用性一个重量衡量指标;业界一般用几个9的SLA服务等级来衡量互联网应用的可用性;不可用时间怎么计算出来的?

4个9即99.99%可用不可可用时长0.01%,一年365天,8760小时一年大约有52.6分钟不可用;SLA=(1-年度不可用时间/年度总时间)100%;3个9不开可用时长526分钟5个9不可用时长5.26分钟。
8760
60*0.0001=52.56

SLA宕机时间3,4个9时间单位写错了
SLA宕机时间3,4个9时间单位写错了

17-1. 架构师视角:怎么设置SLA阀值合理(如何评估高可用)

2个9基本可用3个9较高可用4个9具备自动恢复能力的高可用5个9极高的可用性。电商平台大多是4个9.
N个9要看是流量低峰期还是高峰期,停机一分钟对业务的影响是完全不同的;所以SLA一定是基于某个时间段进行评估,比如一年按照如下公式:
高可用评估=停机时间影响请求量/总的请求量

回答思路:两个度量可用性"N个9"和"影响请求量占比”,结合业务说明第二种更科学,提现业务的价值思考。
如果要面高级:还需要对思路闭环如下:
可评估-可监控-可保证。

证明系统高可用就是回答如何评估高可用,如何监控系统高可用,如何保证系统高可用?

17-2. 如何监控系统高可用

我们以设计一个保证系统服务SLA4个9的健康报警体系为例

17-2-1. 基础设施监控(三个部分:报警指标,监控工具,报警策略)

17-2-1-1. 监控工具

常用有ZABBIX,Open-Falcon,Prometheus这些工具基本监控空所有系统的CPU,内存,磁盘,带宽,网络IO等基础关键指标,再结合运营商提供的监控平台就可以覆盖增高基础设施监控

17-2-1-2. 监控报警策略(由时间维度,报警级别,阀值设定三部分)

每5分钟监控一次
每5分钟监控一次

17-2-2. 系统应用监控


应用监控指标和工具

17-2-3. 存储服务监控

常用的第三方存储有DB,ES,Redis,MQ等,对于存储服务的监控,除了基础指标监控还有一些如:集群节点,分片信息,存储数据信息等相关特有存储指标的监控。面试中基于三个监控核心组成回答有全局的监控视角即可。

公司重视系统稳定,考察责任心意识追问线上告警怎么处理?有响应机制
(响应流程:事前预防,问题监控,事中应对,故障低温,故障解决,事后总结,故障回顾,改进措施)

故障处理机制和能力
故障处理机制和能力

思考题:系统设计中更关心那些可用性指标?

18. 架构师视角:系统容错,降级 实现高可用

网关系统-商品系统-促销系统,积分系统;在企业中一般不同系统由不同研发团队负责;当出现流量高峰是虽然商品系统很容易扩容,但是其依赖的其他服务就不会有实时性的响应。其他系统有可能无法承担大量流量,请求处理缓慢,直到所有线程资源备战满,无法处理后续请求。

18-1. 服务雪崩现象(局部故障导致全局)

18-1-1. 解决(评估,检测,保证)

1.科学方法评估系统可用性指标
2.实时监控报警检测系统可用性
3.通过系统架构设计保证系统可用性
解决思路是:在分布式系统中,当检测到某一个系统或服务响应时间长出现异常时,要想办法停止调用该服务,让服务的调用快速返回失败,从而释放此次请求持有的资源,这就是架构设计中经常提到的降级和熔断机制。
面试官一般通过如下两个问题考察:

  1. 熔断和降级怎么做的?(考察原理性知识的理解)
  2. 你在项目木中如何实现熔断降级(考察实战能力)
18-1-1-1. 原理(电路保险丝)

微服务架构中,熔断机制就是如果服务A调用服务B时,B返回错误或超时的次数超过一定阀值,服务A的后续请求将不再调用服务B,这种设计方式就是断路器模式。
熔断设计的原理:就是断路器模式下,服务调用方为每一个调用的服务维护一个有限状态机,在这个状态机中存在,关闭,半打开和打开三种状态
关闭:正常调用远程服务,半打开:尝试调用远程服务;打开:直接返回错误,不调用远程服务。


熔断机制原理状态机

经常会通过Netfix的开源项目Hystrix实现熔断到店功能,面试官会考察不通过开源组件造轮子实现熔断器的功能能力?

18-1-1-1-1. 造断路器

  • 关闭转打开:首先判断是否熔断中,如果没有则正常调用服务
 //如果是关闭状态
 if(breaker.Close()){
 //失败次数超过阀值
 if(fialCount.incrementAndGet()){
 //设置为打开状态
 breaker.setOpen();
 }
 }
  • 打开转半打开:如果已经熔断,就初始化一个定时器,定时检测服务状态可用性,如果服务达到了熔断的倒计时,就设置为半打开状态
  1. //初始化定时器定期检测服务是否可用 
  2. new Timer("server-recover",true).scheduAtFixedRate(new TimerTask()){ 
  3. @OverWrte 
  4. public void run(){ 
  5. if(breaker.isOpen()){ 
  6. //设置半打开状态 
  7. breaker.setHalfOpen(); 
  8. } 
  9. } 
  10. },0,recoverInterval); 
  • 半打开转关闭:如果服务状态是半打开,判断成功次数是否超过阀值,超过则设置断路器关闭
//如果断路器是半打开状态
if(breaker.isHalfOpen()){
//判断成功次数是否超过阀值
if(successCount.incrementAndGet()>=SUCCESS_THRESHOLD){
//设置断路器为关闭状态
breaker.setClose();
}
}

这样当某个服务节点出现问题,服务调用者就会实时监测到,并且不再请求有问题的服务节点避免单个节点故障导致整个系统的雪崩。

18-2. 降级设计的原理

讲解设计是站在系统整体可用性上考虑问题:当资源和访问量出现矛盾时,在有限的资源下,放弃非核心功能或者服务,保证整体的可用性,熔断也是降级的一种手段。
实现手段:在流量突增时,放弃非核心流程或业务,释放系统资源,让核心业务正常运行。比如618电商平台会关闭评论和退款功能。

18-2-1. 怎么做降级设计机制(服务降级,功能降级)?

18-2-1-1. 服务降级(读操作降级,写操作降级)
  • 读操作降级:做数据兜底服务,将兜底的数据提前存储到缓存中,当服务触发降级时,读操作直接降级到缓存,从缓存中读取兜底数据,如果此时缓存也不存在数据,则返回默认值,不再请求数据库。
    读操作降级的设计原则就是取舍非核心服务。
  • 写操作降级:将之前直接同步调用写数据库的操作,降级为先写缓存,然后再一步写入数据库。
    写操作降级的设计原则:就是取舍系统一致性;实现方式把强一致性转换为最终一致性。
18-2-1-2. 功能降级(产品功能上取舍)

既然在服务降级时已经取舍掉了非核心服务,同样的产品功能层面也要响应的简化。可以通过降级开发控制功能的可用或不可用。另外在设计降级时,离不开降级开关的配置,一般是通过参数化配置的方式存储在配置中心,手动或者自动开启开关实现系统降级。

18-3. 总结

熔断其实就是有限状态机;降级就是做取舍(服务,产品功能解决资源不足和访问量过大问题;是线上降低系统一致性)
总之两个手段是保证系统稳定和可用性的重要手段,当然高可用设计不仅这两个手段,,还有如服务冗余,负载均衡,故障隔离,,服务限流扥搞设计方式。
系统出故障不可避免,特别是第三方对接过程中,架构设计一定要作为不可或缺的一环处理,,通过各种架构手段提高自己的系统可用性。

思考题:结合你在相关目中都制定了哪些降级策略,比如关闭日志详情查询。

19. 怎么证明系统高性能(吞吐量*延迟;TP)

19-1. 初中级要求:性能指标

说支持多少并发需要考虑实际业务需求,否则没有意义,因为性能与业务强相关
比如:一台网络游戏服务器,可以支撑2百名玩家同时在线开黑,可能就算高性能;一台网络直播服务器,支撑2千名用户同时在线,一台电商平台服务器可以支撑2w名用户同时在线下单,可能就算高性能。
这些或许有出入,但是影响不大,在实际业务场景中,要关注很多业务相关性指标:比如游戏需要关注稳定性;视频需要关注延时;电商需要关注一致性;
在明确业务场景后,还需要关注系统的性能指标主要有吞吐量,延迟以及TP

一般来讲:系统建立的会话数量就是用户同时访问系统的数量;也可以用工时估算系统的吞吐量(throughput)和延迟(latency)
并发读=吞吐量*延迟

  • 吞吐量:反映单位时间内处理请求的能力
  • 延迟:从客户端发送请求到接收响应的时间。

一般来说延迟和吞吐量既互斥,又不绝对互斥,可以通过压测分别绘制出两个的曲线图:

吞吐量延迟曲线图
吞吐量延迟曲线图

总体来说:随着系统压力增大,单位时间内系统被访问的次数增加,结合延迟和吞吐量观察的话,那么对一些延迟要求比较高的系统来说,系统优化西能指标是找到延迟去向最低和吞吐亮趋向最高的点。

如果不流量控制,通过曲线可以看出系统压力增大后,系统什么也做不成,这也是不健壮的系统,在系统压力大的特殊业务场景下,直接崩溃,对所有用户拒绝访问的原因。

  • TP(Top percentile)(两个考点,计算tp指标,tp指标相比于性能均值的意义)
    TP指标: 指在一个时间段内,统计该方法每次调用所消耗的时间,并将这些时间按从小到大的顺序进行排序,并取出结果为:总次数 * 指标数 = 对应TP指标的值, 在取出排序好的时间。

计算案例如下:有四次请求耗时分别为:

10ms,1000ms,100ms,2ms

那么我们可以这样计算TP99:4次请求中,99%的请求数为4*0.99,进位取整也就是4次,满足这全部4次请求的的最低耗时为1000ms,也就是TP99的答案是1000ms

tp相比均值的意义比如tp50,4*0.5=2次,4次请求时间排序2ms,10ms,100ms,1000ms;tp50对应的时间是10ms所以对于1000ms来说取均值明显要比tp值大很多,可能这个就是系统异常导致,结果不科学。

19-1-1. 高级和架构师

要站在系统全链路角度思考,从端到端思考系统的性能指标。
架构师视角就是系统的全链路视角,我们从前端请求流程开始,讲解一次请求链路会涉及哪写前后端性能指标?


架构师视角的性能链路

  1. 浏览器输入URL回车,请求会进行DNS查找,浏览器会查到DNS映射的IP地址,对应指标DNS解析时间。怎么提升域名DNS解析时间?
  • 答案:通过DNS缓存或DNS预解析,适当增大域名的TTL值增大DNS服务器缓存域名的时间,金额提升缓存的命中率。也可用dns-prefetch 标签实现域名的预解析,让浏览器在后台把要用的DNS请求提前解析,当用户访问的网页中包含了预解析的域名时,再次解析DNS就不会有延迟了。
  1. 然后跟IP的地址建立连接,由于HTTP是应用层协议,TCP是传输层协议,所以HTTP是基于TCP协议基础上进行数据传输的,所以要建立TCP请求连接,这里可以用TCP连接时间衡量浏览器与web服务器建立连接的请求时间。
  2. 服务器端的延迟和吞吐能力,针对服务端性能的指标,还可以区分基础设施心梗指标,数据库性能指标,以及系统应用性能指标。
    • 基础设施性能指标:CPU利用率,磁盘IO,网络带宽,内存利用率(如果cpu利用率高可能系统出问题了,内存100%可能放了缓存,另外还可以考虑swap交换空间,jvm和full Gc情况)
    • 数据库西能指标主要有SQL查询时间,并发数,连接数,缓存命中率等
    • 系统应用性能指标和系统业务有关,因为业务场景影响架构设计:比如2C的系统一般会设计成同步RPC调用要实时返回C的请求结果;2B一般设计成事件驱动,通过异步通知方式推送或拉取数据,显然异步的吞吐量更高。
  3. 当浏览器和web服务器连接后,就可以进行数据通信了,由于浏览器自伤而下显示html,同时渲染顺序也是自上而下,
    • 所以在用户在浏览器输入url地址后,到他看到网页的第一个视觉标志为止,这段白屏时间可以作为一个性能的衡量指标,优化手段为减少首次文件的加载体积。
    • 首屏时间也是一个重要衡量指标,首屏时间:用户在浏览器输入url并回车,然后看到当前窗口的区域显示完整页面的时间。一般情况下,一个页面总的白屏时间再2s以内,用户会认为系统响应快,2~5s,用户会觉得响应慢,超过5s很可能造成用户流失。

19-2. 如何分析系统的性能瓶颈

通常情况下,系统性能不达标一般反应在TP99的延迟上面,但这只是表层现象,怎么找到系统真正的性能瓶颈呢,可以遵循以下步骤:

  • 设计阶段:定义系统性能目标,如果设计阶段100qps目标,后面要做1000qps明显架构要改动
  • 开发阶段:走查代码和业务流程
  • 测试阶段:压测发现系统性能峰值:绘制吞吐量和延时曲线,找到最佳性能点,并在这个点做限流。如果达不到性能点,比如吞吐量上不去就要考虑延迟问题和吞吐量,
    1. 定位延迟问题:本着端到端的策略逐一排查时间耗时在哪里大到系统流程,小到模块调用?可以使用linux命令kill -3 PID,jstack打印线程堆栈信息;性能检测工具查看JProfiler,垃圾回收,线程运行情况(特别是所的阻塞方面)
    2. 吞吐量问题的定位:对于吞吐量指标要和cpu使用率一起来看,在请求速率逐步增大时,经常会初现一下四种情况:
CPU上升 CPU不变
吞吐量上升 合理情况,持续施加更高压力,知道吞吐量达到峰值 持续压测,继续施加更高压力,直到达到峰值
吞吐量不变 这种情况可能是CPU被其他任务占用,需要找到致使CPU飙升的原因 最常见的情况,持续施压,吞吐量和CPU都没有变化,检查磁盘IO,网络带宽,工作线程

19-3. 总结

评估性能指标:吞吐量,延迟,tp99;全链路性能指标实际生产环境中还会涉及CDN加速,ISP路由策略,边缘计算等一系列网络工程层面的性能优化指标,要在大脑里建立请求的链路蓝图,熟悉每个环节的性能消耗。

思考题:常用的系统或中间件,他们的性能指标是什么?

20. 架构师视角:应对千万流量的问题

基于全链路视角系统设计层面高性能的系统性能设计

20-1. 前端优化

  • 减少请求次数(减少):增加缓存控制,减少图像的请求次数,减少脚本(js,css)请求次数;
  • 页面静态化:相当于把整个页面元素缓存起来,通常是将页面文件事先存储在CDN节点中,比如将商品详情页做静态化,将详情页面元素存储在CDN中,然后所有请求就可以直接以CDN提供服务,就不会到达后端服务,减少了后端服务的压力。
  • 边缘计算:大数据处理的实时性要求越来越高,所以很多大厂开始讲计算能力放到距离用户最近的CDN节点中,这就要求原有的CDN节点要提供可以定制化的计算能力,这部分内容会涉及一些新的概念,是加分项。

20-2. 后端优化

  • 基础设施层面
  • 网络层面
  • 架构层面

后端优化链路点
后端优化链路点

初中级研发,只需要了解系统性能瓶颈,怎么优化即可,高级研发不能停留在技术点上要有自己对技术的理解

比如指标要求TP99=2s,那么表面是99%响应在2s内;深层是任何系统都有承载范围,在并发用户数限定下保证tp99即可。

20-3. 系统设计的思考步骤

  1. 明确指标2.保护系统3.用户体验(兜底)4. 快速扩容

20-3-1. 系统架构的落到地工作需要注意:

  1. 做好系统限流:通过流量控制保证系统稳定性。当实际并发压力超过系统性能能设计指标的时候,就拒绝新的请求连接,让用户排队
  2. 做好快速扩容:一般要储备额外的计算资源,以备不时之需,实现通过预估留出一部分资源池,不直接扩容是考虑成本(把控成本也是设计原则之一);另外一个关键因素就是扩容速度
  3. 做好系统优化:性能设计贯穿系统建设的始终,一个系统研发管理过程为例,包括需求阶段,设计阶段,研发阶段,测试阶段,上线阶段,运行阶段(设计要根据项目阶段随时进行调整)

技术发展到今天,技术问题不存在挑战了,所谓的高性能框架设计,仅仅变成一种标准化的应对流程,要做的就是将业务流程抽象成技术问题,比如具体到队列,缓存涉及等技术细节,

20-3-2. 面试的思路

思考题:你设计的系统中,遇到哪些性能问题,你是怎么解决的?

21. 如何抗住双十一预约抢购活动 案例串讲(预约抢购场景)

大促抢购场景涉及专栏哪些内容?
他们怎么通过架构设计的方式组合在一起,实现一个完整的需求流程呢?
业务场景分四个阶段:商品预约,等待抢购,商品抢购,订单支付

  • 预约:用户进入商品详情页,获取购买资格,等待抢购倒计时
  • 等待:等待倒计时,知道商品开放抢购
  • 抢购:倒计时结束,用户提交抢购订单,排队等待抢购结果,成功后扣减系统库存,生成抢购订单
  • 支付:等待用户支付成功后,更新订单状态,通知用户购买成功

21-1. 预约阶段

很多电商平台为了方便流量运营,改造了传统秒杀场景,通过先预约,再抢购的方式预热商品,并根据预约量调整运营策略,而且在抢购的活动中为了增加商品销售量,会允许预约资格超过实际的库存数量(预约了不一定能抢上?)
如何在高并发下,让每个用户都能得到抢购资格呢?:可以基于06|分布式系统所得实现来控制资格的发放

  1. //单例redis代码 
  2. //上锁 
  3. SET lock_key unique_vlue NX PX 10000 
  4. //解锁 
  5. if redis.call("get",KEYS[1])==ARGV[1]then 
  6. return redis.call("dell",KEYS[1]) 
  7. else return 0 
  8. end 

21-2. 等待阶段

用户预约成功之后,商品详情页会出现一个抢购倒计时,这个倒计时的初始时间是服务端获取的,用户点击购买按钮时会取服务端校验是否已经到了抢购时间。因为抢购商品前,用户户籍频繁刷新强青叶,详情页请求会剧增,如果详情页没做好流量空针,容易成为整个预约抢购系统中的性能瓶颈。

如何解决等待抢购时间内流量突增问题?页面静态化,后端限流

  1. 页面静态化:详情页生成静态页面放到用户最近的CDN上,浏览器会自动缓存页面的静态资源
  2. 服务端限流:对商品详情页中的动态请求接口机设置最大并发访问量,防止超出预期的请求集中访问系统,造成系统压力过载,操作上,可以在商品详情页的后端系统入口层中进行接口限流,如果使用Nginx来做反向代理,可以基于Nginx配置限流算法,如果Ng的ngx_http_limit_req_module和ngx_http_llimit_conn_module,另外需要掌握限流策略的原理,比如令牌桶算法的原理。

21-3. 抢购阶段

用户会点击提交订单,这是,抢购系统会先校验库存,当库存足够时,系统会先扣减库存,然后再生成订单。在这个过程中,短时间之内提交订单的写流量非常高,所以做流量削峰,会将订单暂存在消息队列中,提示用户抢购排队中。。。,然后再 由后端服务异步处理用户的请求。
如何校验库存和扣减库存?
基于数据库:一般不会基于数据 库实现,瞬时写流量极高,给数据库压力太大,非要用数据库实现要通过分布式锁优化扣减库存的并发操作;分布式锁的可靠性要求极高,可以选择多节点Redis分布式锁,或者选型Zookeeper.
基于缓存:一般基于缓存来存储库存,实现扣减,不过要注意,需要考虑缓存系统单点问题,就是多节点存储库存,也要引入锁的策略,保证redis实现库存的一致性。

实现校验库存和扣减库存之后,会生成抢购订单,由于数据库表会承载订单数据,一旦瞬时流量剧增,磁盘IO,数据库请求连接数资源都会出现瓶颈,这时候需要考虑对订单进行分库分表,实现方式可以通过对用户ID字段进行hash取模,将订单分片到多个存储节点提高数库系统并发能力。

商品架构设计中国三个考点:流量削峰,扣减库存,分库分表。

  • 流量削峰:在正式抢购场景中,大量请求,引入消息队列做异步化,然后在后端服务中,启动若干队列处理消息队列中的提单请求,再执行校验库存,下单等逻辑;如何处理消息对了消息参考08:MQ的丢失重复和积压处理。
  • 扣减库存:基于redis实现的时候再redis哨兵某事的集群部署情况下,要解决主从切换数据不一致的问题参考06|分布式系统所得实现原理。
  • 分库分表:如何分库分表参考04|亿级商品存储,深度回答分布式系统原理性问题。
  • 带宽问题:抢购入口的请求量非常大,会占用大量带宽,为了不影响提交订单的请求,从网络工程师角度考虑,通过单独的子域名绑定独立的网络服务器,这里涉及DNS设计与优化。

21-4. 支付阶段

21-5. 总结

  • 互联网中大量数据里的存储设计参考04
  • 关于秒杀和抢购场景下的库存扣减设计参考06
  • 分布式系统之间的事务一致性架构设计参考05
  • 关于架构设计中服务强依赖的设计,一般通过RPC远程同步调用方式实现,参考07讲
  • 系统解耦,流量削峰的设计问题参考08

思考题:如果用户提交订单抢到商品后,系统的库存已经扣减,但是订单中的状态还是未支付,如果此时用户是恶意行为,只抢不付,如何优化架构设计应对这样的操作?

參考:https://www.cnblogs.com/lichihua/p/10803305.html

$goods->query('update order set = store- num where store>=num and goodID = 12345');

扣减库存的三种方案
(1)下单减库存(秒杀商品这种方式最好)
  用户下单(确认订单)时减库存

  优点:实时减库存,避免付款时因库存不足减库存的问题

  缺点:恶意买家大量下单,将库存用完,但是不付款,真正想买的人买不到

(2)付款后减库存(支付成功回调时减库存)
  下单页面显示最新的库存,下单时不会立即减库存,而是等到支付时才会减库存。

  优点:防止恶意买家大量下单用光库存,避免下单减库存的缺点

  缺点:下单页面显示的库存数可能不是最新的库存数,而库存数用完后,下单页面的库存数没有刷新,出现下单数超过库存数,若支付的订单数超过库存数,则会出现支付失败。(其他用户可能提示库存不足,可能出现超卖问题) 因第三方支付返回结果存在时差,同一时间多个用户同时付款成功,会导致下单数目超过库存,商家库存不足容易引发断货和投诉,成本增加

(3)预扣库存(确认订单之后与支付之间保留库存,调起支付界面前锁定库存, 业务系统中最常见的就是预扣库存方案)
  下单页面显示最新的库存,下单后保留这个库存一段时间(比如10分钟),超过保留时间后,库存释放。若保留时间过后再支付,如果没有库存,则支付失败。例如:要求30分钟内支付订单。(①.支付前预占库存,②.限制支付时间:若订单创建成功N分钟不付款,则订单取消,库存回滚,③.检测恶意下单用户加入到店铺黑名单,④.加入限购):设置了“付款减库存”后,买家拍下商品待付款,这是系统会预扣库存出来,就叫“预扣库存”。若买家30分钟后依然未付款,预扣库存释放,回增到总库存里。若买家30分钟内付款了,库存就真正被扣除 ;存在预扣库存时,无法编辑商品库存,也无法更改“库存计数”的设置

同時限制每个用户购买数量。

22. 架构设计知识体系

初级研发-中高级研发|-架构师-更高突破
因为工作中,很多公司没有机会参与架构设计,所以大家基本都是项目采坑,逐渐成长;更好的方式是掌握知识体系,在项目中实践总结。
怎么判断是否有架构师能力并匹配这个岗位?判断是否能独立负责某个子系统,能不能把握系统的规划,逻辑和演进。
架构师落地能力:基础技术架构,业务架构,开发技能。

22-1. 把握新系统技术

  1. 需求分析阶段:对于业务架构,给出一个合理的需求分析抽象模型
  2. 架构设计和选型阶段:充分考虑技术的合理性,制定合理的设计方案
  3. 架构落地阶段:架构师要能指导研发进行落地,并推进项目的执行。

要成为架构师就要做到,做好系统开发各环节的技术把控。

22-2. 知识体系(架构设计理解为计算机系统设计)

存储器;运算器,输入输出;控制器

  • 存储:分布式存储系统,为什么选型分布式存储,分布式存储关注哪些问题(数据分片:范围,哈希(驱魔算法,虚拟通算法,一致性哈希算法),数据复制(同步,异步),容错(副本同步,副本是高可用唯一手段),副本一致性(强一致性,弱一致,最终一致),一致性协议(2PC,Paxos选举,向量时钟,RWN协议,Raft协议),选举分布式锁的问题(解决双主(脑裂)通过租约机制解决网络问题造成双主情况);衡量副本可用性,一致性:引出基础理论CAP,BASE以及PACELC分布式系统体系中最基础(核心)的理论也是最复杂(细节)的问题)
  • 计算(并行计算,分布式计算,云计算(分布式+虚拟化技术),分布式领域计算模式:如分布式并行计算MR涉及思想,基于流失计算Storm,spark,flink的架构设计,如果是系统架构师了解即可)
  • 输入输出:系统间通信技术设计到网络基础协议,网络io模型,最后是偏应用的知识,如连接复用,序列化,RPC,MQ消息队列等;作为架构师要基于高性能要考虑流量的流转过程及应对方案,当请求到达网络设备时要依次考虑:
    • 01 网络设备如何处理流量:涉及中断和缓存
    • 02操作系统如何处理流量:涉及IO模型,select,poll,epoll以及IO多路复用
    • 03:应用系统如何处理流量:涉及NIO开发,如Reactor模式,Netty框架原理等
    • 04:系统线程如何处理流量:涉及多线程的设计模式。
      最后还要掌握分布式系统通信的核心技术RPC和MQ。
  • 控制器:理解系统中的角度系统
    • 流量调度:控制流量的常用方案策略(负载均衡,服务路由,熔断,降级,限流等;其实常用高可用高性能的解决方案都是基于流量的调度)
    • 资源亮度:将流量调度迁移到服务器的计算资源,存储资源或基础资源上面的话就引出基于资源的调度(Mesos,yarn计算资源的调度Hdfs,ClusterFS,Ceph存储资源调度,Kubernetes,Mesos容器资源的调度)要掌握系统调度设计的调度算法和负载策略

架构师一定技术出身,突破技术思维限制,向上立足部门和公司,向下管控系统和研发,站在全局角度规划,组织,系统技术的发展。

管理自己的架构设计能力
管理自己的架构设计能力

23. 架构的术,道,势

自己的核心专业技术-术
自己分析能力-道
公司的顺势而为-势

23-1. 作为架构师兼技术负责人

  • 立规矩:所有研发谈话,明确告知做事的准则和底线
  • 建系统:推进新系统的建设,老系统老人维护,确保老系统不出问题,保证业务正常运行
  • 定节奏:让团队按照新的节奏做事,打破原有工作节奏,整体提速
    一个季度过后,A带出一个福哟激情和战斗力的队伍,而老团队有的改变,有的离开。
    做这三个事情涉及到:思考(认知层),执行(技术层),借势(战略层)的边界把控。

23-2. 思考

  1. 为什么A重新立规矩能立下了
  2. 为甚A推进新系统能获得老板同意
  3. 为什么A重新定节奏,能得到团队的支持。

23-3. 结束语

作为技术人员,一定不要高估辛苦工作的作用,低估判断力的价值。

23-3-1. 老人的四条建议

  • 思考:敏感度思考能力,敏感驱动思考,思考驱动学习和总结,直至敏锐。
  • 表达:基于思考的基础上,良好的表达能力,会表达,敢表达,表达准确;学学习结构化思维,比如金字塔原理(也能提升PPT水平)
  • 惊艳:当做出一件很有价值的事情时,评估的方式要惊艳到自己,才能超出别人的预期;事事有回应,件件有着落,最终提高自己的判断力,懒惰的化就会成为老油条降低自己判断力
  • 认知:一定要对“地位,格局,方法论,手段” 这些看似很空的词有明确的认知,变的具体。作为架构人要有以下几个认知:
    • 一面解释:出现问题A解决办法是什么?
    • 二面解释:业务发展过程中,将系统变得有序,有能力快速响应变化。
    • 部门总监:架构即管理

23-3-2. 新人四条建议

  • 有计划,有几类
  • 学习时间管理四象限
  • 要自己定位自己
  • 要善于归纳总结和思考

23-4. 职业发展中 要体会到三个阶段

做下不做上,做下也做上,做上不坐下

二、架构师的30+6训练

1. 开篇

1-1. 大纲

了解-懂-架构思维-做架构

  • 01互联网架构的前世今生
  • 02~05架构的关键技术:缓存,异步,分布
  • 06~08架构的核心要素:性能,可用,安全
  • 09架构的案例:系统,框架,数据库
  • 10架构师的成长,搞定问题的攻略

1-2. 优秀架构师特点

  • 好奇心
  • 敏锐业务小嗅觉
  • 扎实技术基础
  • 出色变成能力
  • 深刻领悟主流技术产品和模式

1-3. 架构体系

  • 基础:数据结构,操作系统原理,算法能力,设计模式,网络基础
  • 技术:缓存,异步,分布式存储,微服务
  • 架构:高可用,高性能,安全性

2. 架构演进之路

2-1. 互联网系统特点

  • 高并发,大流量
  • 高可用
  • 海量数据
  • 用户分布广泛,网络情况复杂
  • 安全环境恶劣
  • 需求快速变更,频繁发布

2-2. 新系统处理提升的两种途径

  • 垂直伸缩:单机能力更强(传统软件行业:系统或者插口有限制,成本线性增长)
  • 水平伸缩:服务器增多(互联网行业)

2-3. 系统架构演化过程

单机系统(少量用户)->数据库与应用分离(万级用户)->使用缓存改善性能,应用服务集群化(十万级用户)-》使用反向代理和CDN加速响应,数据库读写分离(百万级用户)-》使用分布式文件系统和分布式数据库系统(千万级用户)->使用搜索引擎,NoSql,消息队列与分布式服务(亿级用户)

亿级用户系统架构图
亿级用户系统架构图

缓存,数据库,异步,微服务,消息队列

3. 缓存

3-1. 缓存知识

缓存知识图
缓存知识图

代理缓存:客户端本地不受控制
代理缓存:客户端本地不受控制

多层反向代理缓存前后端都可以反向代理
多层反向代理缓存前后端都可以反向代理

CDN缓存类型流程图
CDN缓存类型流程图

3-2. 通读缓存,旁路缓存

旁路缓存度列键值对
旁路缓存度列键值对

数据读写速度阀值
数据读写速度阀值

分布式对象缓存访问模型:分为路由模块,通讯模块。

4. 分布式消息队列

  1. 同步调用,异步调用
  2. 新消息队列构建异步调用架构
  3. 消息队列好处与挑战

4-1. 消息队列知识图

消息队列知识图
消息队列知识图

模式:解决方案被一次又一次证明成功;反模式:方案反复被证明带来问题。

反模式常见问题:耦合如生产者里写死了序列化方式,如果消费者不一致就是耦合问题
反模式常见问题:耦合如生产者里写死了序列化方式,如果消费者不一致就是耦合问题

5. 存储

  1. Mysql数据复制
  2. 数据库分区
  3. NoSql分布式数据库

5-1. 存储知识

数据库存储知识
数据库存储知识

通过binlog和reloylog实现主主复制
通过binlog和reloylog实现主主复制

mysql主主失效维护过程
mysql主主失效维护过程

分片数据做联合查询性能麻烦;

数据库综合部署方案
数据库综合部署方案

CAP:P分区耐受性了,可以跨网络分区线性伸缩。A可用性,任何时候都可以读写请求响应;C一致性多个备份任何时候都是一致的。

一致性冲突:选举机制实现最终一致性。

6. 微服务架构

  1. 单体应用挑战
    1. 不同部门的相互依赖编译部署困难
    2. 分支管理困难,冲突
    3. 数据库连接好近,新增业务困难
    4. 发布困难
  2. 分布式微服务框架设计
    1. SOA架构(早起微服务思想架构,但是复杂耦合,服务注册中心)

SOA架构
SOA架构

2. 微服务

Dubbo(RPC)
Dubbo(RPC)

SpringCLoud(HTTP)
SpringCLoud(HTTP)

  1. 微服务最佳实践

6-1. 微服务知识思路

知识体系
知识体系

需求与工具的关系是原则
需求与工具的关系是原则

7. 高性能

  1. 系统性测试(性能技术期,响应时间,并发数,吞吐量)=》性能测试,负载测试(最佳情况),压力测试(峰值压力),稳定性测试。

    各种测试吞吐量曲线

    响应时间各负载曲线

测试结果案例表
测试结果案例表

  1. 系统性能优化的分层思想

性能优化分层思想和spark代码优化案例
性能优化分层思想和spark代码优化案例

7-1. 高性能知识

高性能知识图:代码方面线程锁,设计模式,数据结构,资源复用
高性能知识图:代码方面线程锁,设计模式,数据结构,资源复用

8. 高可用

  1. 可用性度量
  2. 可用性策略
  3. 可用性运维

可用性挑战
可用性挑战

故障分计算
故障分计算

负载均衡实现方法:DNS,IP地址(对应http协议,TCP层),MAC地址(对应数据两路层)

DNS均衡
DNS均衡

IP均衡
IP均衡

数据链路层三角模型均衡
数据链路层三角模型均衡

异地多活策略
异地多活策略

灰度发布高可用运维
灰度发布高可用运维

8-1. 知识体系

高可用知识体系
高可用知识体系

9. 安全

  1. web攻击防护
  2. 敏感数据加密和解密
  3. 信息过滤与反垃圾

9-1. 知识图

安全知识图
安全知识图

9-2. 攻击

9-2-1. web攻击

XSS攻击方式1
XSS攻击方式1

xss攻击方式2
xss攻击方式2

9-2-1-1. web防护
  1. 消毒:检查用户提交的脚本是否包含>符号,因为大部分脚本是用这种符号,可以通过转义的方式转成t gt这种字符,显示的还是正常>号,但是这样的脚本无法执行,也就无法达到攻击的目的
  2. HttpOnly:利用客户端权限利用cookie,进行攻击,在cookie上设置cookie only的属性,使恶意脚本无法获取cookie,无法发动攻击

9-2-2. SQL注入攻击

sql攻击图解
sql攻击图解

9-2-2-1. SQL防护
  1. 消毒:拦截请求中的数据,进行正则表达式匹配,如果表达式中包含某些特殊语句,就对这些语句进行转移和消毒,插入一些不可见字符或者进行编码转换,使其无法在数据库中执行。
  2. 参数绑定:通过sql预编译的方式将sql和输入参数绑定,防止将输入参数中的数据当做sql执行,也就是说访问数据库的sql通过预编译的方式已经提交到了数据库,数据库收到请求参数以后,会按照特定的sql命令和请求参数执行,所有的提交数据都会当做参数,而不是命令执行。

9-2-3. CSRF攻击

CSRF攻击流程
CSRF攻击流程

9-2-3-1. 防护
  1. 表单Token(必须指定客户端浏览器和场景下生成的token才能请求,那么攻击者无法构造这个token;通常token利用时间戳,设备指纹,页面内容进行计算, 服务受到请求对请求参数进行校验。)

  2. 验证码
    csrf攻击是在用户不知情情况下请求受信任的服务器,对于敏感操作比如资金方面,可以进行图片验证码和手机验证码才能完成,这样用户就能发现自己被攻击了。

  3. web应用防火墙

    web应用防火墙基于处理逻辑和规则集合(可以针对漏洞升级)结合

9-3. 加密

  • 单项散列加密不可逆(因为大家密码差不多也可以通过查表查出密文和明文的映射,所以经常需要对散列算法加盐生成秘钥类似的东西增加算法复杂度;场景:登录支付密码)
    单项散列加密不可逆

  • 对称加密场景银行卡号,有效期,验证码(加密密钥和解密秘钥一样)

  • 非对称加密(加密秘钥公钥和解密秘钥私钥不同),常见场景https请求。
    非对称加密

    非对称加密还可以用作数字签名,恰好反过来,数字签名是用私钥加密得到密文,通过公钥才能解开,因为私钥只有自己才能拥有。

9-4. 信息过滤与反垃圾

9-4-1. 分类算法

P(B|A)=P(A|B)P(B)/P(A) 贝叶斯算法条件概率;场景垃圾邮件分类系统
垃圾邮件分类流程垃圾邮件文本特征是P(A),P(B)就是垃圾邮件概率

9-4-2. 黑名单(可能非常大比如邮件地址)

  • 布隆过滤器

通过k个无偏hash函数计算得到k个hash值

依次取模数组长度,得到数组索引

判断索引处的值是否全部为1,如果全部为1则存在(这种存在可能是误判),如果存在一个0则必定不存在

关于误判,其实非常好理解,hash函数在怎么好,也无法完全避免hash冲突,也就是说可能会存在多个元素计算的hash值是相同的,那么它们取模数组长度后的到的数组索引也是相同的,这就是误判的原因

10. 案例

10-1. 初创公司架构演示案例

初创公司架构经过三次演进后从万级到百万级日订单
初创公司架构经过三次演进后从万级到百万级日订单

10-2. 分布式存储系统Doris架构案例(数据存储架构案例)

Doris架构特点
Doris架构特点

把数据的hash映射关系,通过建立HASH的虚拟节点和物理节点映射关系;后续扩容数据路由关系只需要新修改映射关系的数据即可,同时也只需要对虚拟节点进行管理即可。

Drois故障恢复方案
Drois故障恢复方案

10-3. 反应式变成框架Flower(akka)架构案例(开发框架架构案例)

反应式系统四个特性:即时响应,回弹性(自我修复),弹性,消息驱动;

FLower架构给技术快捷开发框架
FLower架构给技术快捷开发框架

11. 最后致语

技术金字塔
技术金字塔

posted @ 2022-06-03 00:10  编程未来  阅读(269)  评论(0编辑  收藏  举报