解决方案面试题

高并发系统如何支持,高可用、高并发、高性能

高可用:系统无中断地执行其功能的能力,代表系统经过专门的设计后,减少了停工时间、提高了系统的整体可靠性。

高性能:系统能够快速且有效地处理请求或任务

高并发:系统能够同时处理多个请求或操作,而不会导致性能显著下降或系统崩溃。

 

通过负载均衡、消息队列、缓存、数据冗余等技术手段,将访问压力分散到多个节点或服务器上,从而提高系统的整体性能和可靠性。

高并发问题的处理

  • 硬件上使用高性能cpu和足够的内存
  • 网关进行限流处理
  • 使用消息队列进行流量削峰
  • 使用缓存减少数据库压力
  • 数据库做读写分离

线程安全问题处理

  • 对线程共享的对象使用线程安全类如StringBuffer,ConcurrentHashMap
  • 对于共享的数字处理可以使用原子类AtomicInteger、AtomicLong等原子类
  • 使用synchronized同步机制或锁机制保证一段代码只有一个线程进行处理
  • 可以使用ThreadLocal把不安全的变量封装进来

 

 

要处理的问题:

流量削峰

线程安全

服务器压力,数据库压力

 

硬件方面:

使用高性能的cup

使用足够大的内存

对硬盘做优化

服务器做分布式集群降低服务器压力,高可用

 

软件方面:

动静分离:静态资源单独部署,可以做CDN

做缓存:热门的变动不频繁的数据,做缓存,降低数据库压力

使用消息队列:流量削峰,异步处理,避免集中太多请求

数据库优化:读写分离+主从复制,优化表结构,优化索引,优化sql

程序方面:考虑线程安全问题

做限流:服务器端可以做限流,避免超过应用服务器压力承受范围

代码优化:避免消耗太多资源

 

cdn:内容分发网络,一般会在全国关键节点部署缓存服务器,就近响应请求,降低网络阻塞,提高响应数据

 

分布式事务

需要分布式事务出现的场景

  • 一个业务操作中涉及到多个数据源
  • 一个业务操作涉及到多个服务分别访问数据库

 

解决方案:

  • 两阶段提交方案/三阶段提交方案;
  • 分布式事务框架,如Seata,提供了全局事务ID来追踪跨多个服务的操作,并协调这些操作以确保一致性。
  • 本地消息表;在业务系统中引入本地消息表,用于记录分布式事务中的每一步操作。通过定时任务扫描本地消息表,对失败的操作进行补偿。
  • TCC 补偿模式;Try阶段尝试执行业务,Confirm阶段确认业务执行成功,Cancel阶段在业务执行失败时进行回滚。
  • 基于可靠消息的最终一致性方案:利用消息队列的可靠性,如RabbitMQ、Kafka等,实现业务的最终一致性。发送方将操作消息发送到消息队列,接收方订阅消息并执行相应的操作。

 

两阶段提交方案

第一阶段是表决阶段(准备阶段),所有参与者在本地处理事务但不提交,将本事务能否成功的信息反馈发给协调者;

第二阶段是执行阶段,协调者根据所有参与者的反馈,通知所有参与者,进行提交或者回滚;

 

缺点:

单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器(协调者)宕机,资源管理器就会一直阻塞,导致数据库无法使用。

阻塞问题:一个参与者故障可能导致整个系统阻塞

数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能。比如:在第二阶段中,假设协调者发出了事务 Commit 的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了 Commit 操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。

总的来说,XA 协议比较简单,成本较低,但是其单点问题,以及不能支持高并发(由于同步阻塞)依然是其最大的弱点。

 

 

三阶段提交

第一阶段:协调者询问参与者是否可以执行,如果参与者超时没有回复协调者会终止事务,

第二阶段:如果所有参与者都回复Yes,协调者会向所有参与者发送预提交请求。参与者收到请求后执行事务,但不提交,并记录事务日志。如果参与者超时未收到协调者的信息,会根据自身的策略决定是继续等待还是执行回滚。

第三阶段:协调者根据第二阶段的反馈进行决策: 

  1. 所有参与者均预提交成功:协调者发送提交命令给所有参与者,参与者提交事务。参与者超时未收到协调者消息可以根据策略选择提交或回滚事务。通常情况下,为了保持数据的最终一致性,参与者会选择提交事务。
  2. 有参与者预提交失败或超时:协调者发送中止命令给所有参与者,参与者回滚事务。

通过增加询问阶段和超时机制,降低了阻塞和协调者故障的风险。缺点是性能可能稍差。

这样做可以避免二阶段当协调者迟迟没有发出commit或者rollback通知,参与者在超时后可以自行提交或者回滚

 

 

 

本地消息表:

核心是把大事务转变为小事务,异步执行

将需要分布式处理的任务通过消息或者日志的方式来异步执行,消息或日志可以存到本地文件、数据库等;

 

第一步:第一阶段参与者执行本地事务代码,然后把消息存储到文件/数据库,之后向消息队列发送消息,消息的存储和发送也在本事务中,消息内容要记录一个状态如处理中;(为了避免发送消息丢失,要有个定时任务去查询本地消息的消息状态,如果是处理中就重新发送消息,失败达到一定次数记录日志进行预警)

 

第二步:第二阶段参与者通过订阅的消息来触发自己的事务,处理完向第一阶段参与者发送消息,第一阶段参与者修改数据状态;(如果读取数据失败或者消费过程失败,给第一阶段参与者发送消息,让第一阶段参与者重新发送消息)

 

注意:

避免消息的重复影响业务,业务代码要保证读取消息之后代码的幂等性(执行多少次不影响结果)。

 

优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。

 

缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

 

 

补偿事务(TCC)

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

 

Try 阶段主要是对业务系统做检测及资源预留

Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。

Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

 

举个例子,假入 Bob 要向 Smith 转账,思路大概是: 我们有一个本地方法,里面依次调用

1.首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。

2.在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。

3.如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。

 

 

优点:

实现以及流程相对简单了一些

 

缺点:

需要程序员在实现的时候多写很多补偿的代码

 

 

实际的应用:

  • 避免对分布式事务的依赖
  • 对业务过程进行日志记录,预警处理,当有问题的时候通过预警通知相关人员,通过日志了解具体情况,然后进行处理
  • 通过MQ来确认各个步骤的情况,针对失败的情况可以进行重试处理,重试不行预警通知进行处理

 

东奥电商:

账户余额是采用的的补偿事务的方式,有冻结金额,支付调用成功就扣除。未成功就返回

分布式锁

可以使用redisson提供的分布式锁

 

服务升级方式

 

灰度发布(金丝雀发布)

灰度发布只有一套系统,将一部分旧服务的流量移出升级为新服务(作为金丝雀),进行测试确认,没有问题了对外引入流量,确认没有问题了,更新剩余的服务器为新版本

经常与A/B测试一起使用,用于测试选择多种方案。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。

优势:

用户体验影响小,金丝雀发布过程出现问题只影响少量用户

 

不足:

发布自动化程度不够,发布期间可引发服务中断

 

适用场合:

对新版本功能或性能缺乏足够信心

用户体验要求较高的网站业务场景

缺乏足够的自动化发布工具研发能力

只能适用于兼容迭代的方式,如果是大版本不兼容的场景,就没办法使用这种方式了

 

滚动发布

滚动式发布金丝雀发布的进一步优化,按批次增量滚动发布,是一种自动化程度较高的发布方式,用户体验比较平滑,是目前成熟型技术组织所采用的主流发布方式。

一般是取出一个或者多个服务器停止服务,执行更新,测试没有问题将其投入使用。周而复始,直到集群中所有的实例都更新成新版本。一次滚动式发布一般由若干个发布批次组成,每个批次之间留观察间隔,通过手工验证或监控反馈确保没有问题再发下一批次,回退是发布的逆过程,将新版本流量从 LB 上摘除,清除新版本,发老版本,再将 LB 流量接入老版本。和发布过程一样,回退过程一般也比较慢的。

 

优势:

用户体验影响小,体验较平滑

 

缺点:

发布和回退时间比较缓慢

发布工具比较复杂,需要平滑的流量摘除和拉入能力

 

适用场合:

用户体验不能中断的网站业务场景

有一定的复杂发布工具研发能力;

 

绿蓝发布

蓝绿部署中,一共有两套系统(两个集群):一套是正在提供服务系统,标记为“绿色”;另一套是准备发布的系统,标记为“蓝色”。

不停老版本集群,部署新版本到新的集群然后进行测试,测试没有问题了,将流量切到新版本集群,老版本集群可以销毁,将资源释放出来。

 

优势:

升级切换和回退速度非常快

 

不足:

切换是全量的,如果 新版本有问题,则对用户体验有直接影响;

需要两倍机器资源;

 

适用场合:

对用户体验有一定容忍度的场景

机器资源有富余或者可以按需分配(AWS 云,或自建容器云)

暂不具备复杂滚动发布工具研发能力;

 

A/B测试

同一时间有多个版本的服务对外服务,这些服务都是经过足够测试,达到了上线标准的服务,有差异但是没有新旧之分,A/B测试关注的是不同版本的服务的实际效果,譬如说转化率、订单情况等。相关人员通过分析各个版本服务的实际效果,选出效果最好的版本。

 

同步服务

题目:

a系统提供了所有用户数据的维护服务,b服务想要也维护一份b服务相关的用户数据,当b服务相关的用户数据更改时可以及时更新

 

解决方案:

a服务提供一个b服务用户信息更新的日志表,b服务每天定时查询该表确定更新的用户再进行用户数据查询更新自己数据库用户信息

 

抽奖

 

秒杀

 

路由算法

hash取模

根据路由字段对节点数量进行取模进行路由

缺点:如果节点数量变化老数据就路由不到了,所以要重新分配所有数据

一致性hash

一致性的Hash算法是对2的32方取模。Hash函数的值空间为0 ~ 2^32 - 1;我们的节点和虚拟节点组成一个圆环,可以选择服务器的IP或主机名作为关键字进行哈希,这样每台服务器就确定在了哈希环的一个位置上;将数据Key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针查找,遇到的服务器就是其应该定位到的服务器。

 

一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,有很好的容错性和可扩展性。

 

虚拟节点:

每一个服务器节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。

可以防止数据倾斜导致部分节点数据量很多,部分节点数据量几乎没有

 

应用场景:

memcache集群

redis集群

分库分表中也可以使用

 

java如何实现:

使用treeMap实现

 

如何防止重复提交

什么时候需要防止重复提交

需要先判断数据后操作数据的多个操作

比较常见的重复提交的方式有: 1、多次点击提交按钮;2、刷新等

前端防止:

用户点击完“提交”按钮后,我们可以把按钮设置为不可用或者隐藏状态。

用户点击完“提交”按钮后,弹出加载遮罩层,或跳转到其他页面

 

后端防止:

 

使用token:token存入session中(可以是共享的session放入redis中)

要提交的表单或者调用的接口要传入从服务端获取的一个token值(如uuid),到服务端后对比token,如果一致就可以访问,不一致或session中没有token就表示重复提交,每次访问都要删除掉token

使用互斥锁

进入方法之后根据业务id判断是否加锁,在锁范围内判断是否已经提交过(比如已提交状态存到redis中)

使用redis的SETNX命令

没访问一次,使用跟业务id相关的key,进行SETNX,当不存在时才能设置进去,设置成功,返回1;设置失败,返回0。

posted @ 2023-02-02 10:27  星光闪闪  阅读(421)  评论(0)    收藏  举报