分布式与微服务面试题

发布时间 2023-03-28 12:00:38作者: 骑马观花

分布式与微服务面试题

说说你对分布式事务的理解?

  1. 本地事务需要满足四大特性:
    1. 原子性 一组操作,要么全部成功,要么全部失败,不能有部分成功部分失败的情况。
    2. 一致性 数据从一种稳定状态切换到另外一种稳定状态,数据是符合业务逻辑的。
    3. 隔离性 事务与事务之间是彼此隔离的,互不影响的。
    4. 持久性 数据完成操作后被持久化到磁盘中。
  2. 分布式事务也需要满足:原子性,一致性,隔离性,持久性。
  3. 什么场景下需要考虑分布式事务呢?
    1. 系统中存在多个服务或者某个业务处理中涉及到处理多个数据库。
    2. 分布式事务是指会涉及到操作多个数据库的事务,在分布式系统中,各个节点之间在物理上相互独立,通过网络进行沟通协调。
  4. CAP理论
    1. 指的是一个分布式系统中,一致性(Consistency,数据的一致性)、可用性(Availability,在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求)、分区容错性(Prrtition tolerance)。CAP定理指的是这三大特性只能同时满足其二,由于分区容错性是客观存在的,所以我们设计分布式系统过程中,需要取舍CP还是AP。
  5. BASE理论
    1. BASE理论是对CAP定理中一致性和可用性进行权衡考虑的结果。BASE理论的核心思想是:
      1. Basically Availiable(基本可用)
      2. Soft state(软状态)
      3. Eventually Consistent(最终一致性)
  6. 分布式事务的解决方案:
    1. seata
    2. 消息队列+本地事件表
    3. 事务消息
    4. 最大努力通知

什么是两阶段提交协议(2PC)?

  1. 两阶段提交协议是指在计算机网络以及数据库领域内,为了使基于分布式架构下的所有节点在进行事务提交时保持一致性而设计的一种算法。在分布式系统中,每个节点虽然可以知晓自己的操作是成功还是失败,但是却无法感知到其他节点的操作的成功或者失败。
  2. 当一个事务跨越多个节点时,为了保证事务的ACID特性,需要引入一个作为协调者的组件来统一掌控所有节点的(参与者)操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。因此,两阶段提交的思路可以概括为:
    1. 参与者将操作的成功或者失败通知协调者,再由协调者根据所有参与者的反馈结果决定各参与者是否要提交操作还是要终止操作。
  3. 所谓两阶段提交协议,包含准备阶段和提交阶段:
    1. 准备阶段:事务协调者(事务管理器)给每个参与者(资源管理器)发送Prepare信息,每个参与者要么直接返回失败(如权限校验失败),要么执行本地事务,将修改操作写进本地的redo和undo日志,但并不提交事务,达到一种“万事俱备,只欠东风”的状态。
    2. 提交阶段:如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。(注意:必须在最后阶段释放锁资源)各参与者向协调者反馈ack完成的消息,协调者收到所有参与者反馈的ack消息后,即完成了整个事务
  4. 两阶段提交协议存在的问题:
    1. 性能问题:所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈【seata的模式,在执行本地事务的时候直接提交事务并释放资源,记录好数据的undo日志和redo日志】。
    2. 可用性问题:如果协调者存在单点故障,参与者会一直处于阻塞状态【事务协调者集群搭建】。
    3. 数据一致性问题:在提交阶段,当协调者向参与者发送了Commit请求后,发生了局部网络异常或者在发送Commit请求过程中协调者发生了故障,导致只有一部分参与者接收到了Commit请求,于是整个分布式系统便出现了数据不一致的现象。(脑裂现象)【可以通过定时任务执行数据检查脚本,看看数据的状态是否符合业务逻辑,对于不符合业务逻辑的数据进行手动补偿】
    4. 数据状态不确定问题:协调者发出Commit消息后宕机了,而唯一接收到该条信息的参与者也宕机了,那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否已经被提交。

什么是补偿性事务?

  1. TCC(Try,Confirm/Cancel)是服务化的二阶段编程模型,采用补偿机制:image-20230319222748228
  2. Try阶段主要是对业务系统做检测及资源预留。
  3. Confirm阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行Confirm阶段,默认Confim阶段是不会出错的,即:只要Try成功,Confirm就一定成功。
  4. Cancel阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源的释放。
  5. 使用场景:
    1. 比如两个账户进行转账,A向B转账100元。
      1. 在Try阶段,将A的账户冻结,并给冻结字段赋值-100元,B账户冻结,冻结字段赋值+100元。
      2. 在Confirm阶段,将A的账户扣减100元并解冻,B的账户增加100元并解冻。
      3. 如果第二步执行成功,那么整个事务就完成了。如果执行失败,则执行对应的Cancel操作,将A和B的账户恢复为事务开始前的状态。
    2. 不同的组件之间需要保持一致性。
      1. 比如Mysql和Redis保持数据一致性。在Try阶段执行各自的修改操作,Confirm阶段执行确认,如果执行失败则利用Cancel恢复为原始的状态。
  6. 优点:
    1. 性能提升:根据具体的业务来控制资源,锁的粒度变小,不会锁定整个资源。
    2. 能保证数据的最终一致性。由于有Confirm和Cancel,能保证数据的最终一致性。
  7. 缺点:
    1. TCC的Try,Confirm和Cancel要按照具体的业务来实现,业务耦合度高,提升了开发的成本。

你是如何通过消息队列实现分布式事务的?

  1. 利用消息队列和事件表实现分布式事务:image-20230319225300412
    1. 客户端请求A服务,服务从1-1开始,将请求插入事件表(比如生成一笔订单),只要插入事件表成功【事件表发送状态设定为未发送】即可返回响应。
    2. A服务中设定定时任务,检测表中的未发送事件,将事件发送到MQ中间件中,并将事件表中的未发送状态修改为已发送。
    3. 服务B消费MQ,拿到消息后也是先将消息插入到事件表中,然后ACK响应MQ已消费成功,事件表中的状态为待处理。
    4. 服务B中的定时任务检测表中待处理的事件,然后执行本地事务,执行成功后更新数据库事件表的状态为已处理。
    5. 优点:
      1. 缩短了响应时间。之前的请求中需要等到服务B完成本地事务后才返回响应,现在在服务A中插入了本地事件表即可返回响应。
      2. 事务的实现是通过编码实现,可靠性较高。
    6. 缺点:
      1. 出现异常时排查难度较高。
  2. 通过RocketMQ的半消息+事务回查机制实现分布式事务:image-20230319230214763
    1. 生产者先发送Half消息到MQ中,然后Half消息发送成功。Half消息是存在CommitLog文件中。
    2. 生产者执行本地事务,如果本地事务执行成功则向MQ发送Commit确认消息。如果本地事务执行失败则向MQ发送Rollback消息。
    3. 如果MQ未收到生产者的确认消息,则执行定时事务回查,检查生产者的本地事务的执行状态,如果本地事务执行成功,则补发Commit确认消息,如果本地事务执行失败,则补发Rollback确认消息。
    4. 如果是Commit消息,则将消息投递给消费者进行消费处理,如果是Rollback消息,则将Half消息删除不进行投递。

分布式ID生成有哪些方案?

  1. 分布式ID的特性
    1. 唯一性:确保生成的ID是业务唯一的
    2. 有序性:确保生成的ID是对于某个用户或者业务是按照一定的数字有序递增的
    3. 高可用性:确保任何时候都能正确的生成ID
    4. 时间戳:ID里面包含时间,不容易重复
  2. 分布式ID的生成方案:
    1. UUID:结合机器的网卡,当地时间,随机数来生成UUID
      1. 优点:本地生成,没有高可用风险
      2. 缺点:长度过长,无序,查询效率低
    2. 数据库自增ID:如Mysql的auto_increment,并且可以设置不同的步长,生成不重复ID的策略来实现高可用。
      1. 优点:数据库生成的ID绝对有序,高可用且实现简单
      2. 缺点:需要独立部署数据库实例,成本高
    3. Redis生成ID:利用Redis的单线程和incr、increby这样的自增原子命令,保证生成的ID是唯一有序的。
      1. 优点:不依赖数据库,性能好,数字ID是天然有序的
      2. 缺点:需要额外引入Redis组件,增加系统的复杂度。
    4. snowflflake(雪花算法):一个64位的Long型数据。第一位是符号位,然后41位时间戳(精确到毫秒级),10位数据机器位,12位毫秒内的序列(同台机器,同一时刻)
      1. 优点:性能高,按时间有序,一般不会造成ID碰撞
      2. 缺点:需要自己实现雪花算法,依赖于机器的时钟

常见的负载均衡算法有哪些?

  1. 轮询负载均衡算法:Round Robin
    1. 适合于所有服务器硬件都差不多的场景。多个服务器一个一个挨着来。
  2. 加权轮询算法:
    1. 按照权重不同来分发,配置好的服务器所占的权重更大些。
    2. 两个服务器的权重分别是6和4,可以在1到10之间取随机数,若数字在1到6之间就走6的权重,取到7到10时就走的4的权重。
  3. 随机轮询算法:
    1. 随机访问一台服务器
  4. 最少连接:
    1. 记录每个服务器正在处理的连接数(请求数),将新的请求分发到最少连接的服务器上。
    2. 可以将服务器的连接数记录到redis中,每增加一个连接,则进行自增。
  5. 原地址散列:
    1. 根据请求来源的ip地址进行哈希计算,只要原地址不变,每次请求映射来的提供服务的节点也不变,有利于进行session会话的保持。