面试-分布式

面试题

一致性hash算法

分布式系统下,常用的Hash算法存在一个问题,当节点数增加和减少时可能会导致其他所有节点的数据需要迁移。

一致性hash算法是对固定的一个数取模(2的32次方-1),将整个值空间看作一个环,对每一个服务器进行哈希确定在换上的位置,对数据进行哈希时使用相同的函数进行哈希,并在环上延一个方向查找,遇到的第一个节点,即位定位到的服务器。

常用的一致性Hash算法的缺点

基本的一致性Hash算法在节点数较少时可能会出现节点在环上的定位不均匀,导致数据倾斜到一个服务器上。

解决办法:引入虚拟节点,即一个节点计算多个hash值

滑动时间窗口

计数器限流

令牌桶

分布式事务解决方案

分布式事务解决方案主要有两种:两段式提交、消息队列

两段式提交

两段式提交主要依赖协调者对所有事务参与者进行协调,主要分为两个阶段:资源准备阶段、资源阶段

  • 资源准备

    • 协调者请求所有事务参与者询问是否可以开启本地事务
    • 参与者开启本地事务但不提交
  • 资源提交

    • 协调者获取所有参与者响应结果判断是否发送提交请求

两段式提交缺点:涉及多节点通信网络消耗大

不适合高并发系统,没有超时机制可能出现参与者阻塞情况

消息队列

消息队列更适合高并发系统,只要能确保消息能正常存储即可保证分布式事务最终一致

依靠消息队列实现分布式事务主要有两种:事务消息、本地消息表

分布式锁

https://www.cnblogs.com/linjiqin/p/8003838.html

分布式锁的实现方案主要有三种:数据库乐观锁、redis锁、zookeeper锁

CAP原理

CAP原理是分布式系统的基本原理

  • Consistent一致性: 同样数据在分布式系统中所有地方数据都保持一致。
  • Available可用性: 分布式系统中的存活节点能快速的进行处理。
  • Partition Tolerant 分区容错性: 分布式系统中某个节点出问题时,系统中存在某种机制能保证继续对外提供服务

分布式系统只能满足CAP原理中的两个特性,一般分布式系统都选择保证可用性分区容错性而舍弃一致性,对数据不一致可以一定程度上容忍,只要保证数据最终一致性即可

数据最终一致性

例如用户A和B同时访问一个数据X,当A将X修改为Y时,可以容忍B读取到的数据仍是X,一段时间后才能读取到数据Y。只需要保证最终数据会同步到各个节点即为数据最终一致性。

分布式ID生成

在业务中,我们往往需要生成全局唯一的ID号,例如生成订单号等

分布式ID往往需要满足如下几个特征

  • 全局唯一:不能出现重复的ID
  • 趋势递增:ID的生成是递增的(提高MySQL数据等以B+树为索引的写入性能,B+树每层的元素是有序的)

UUID

https://cloud.tencent.com/developer/article/1504263

UUID是由32个16进制的字符组成的字符串,示例:550e8400-e29b-41d4-a716-446655440000

其总长度达 32X4 = 128位

在第三组的第一位上的数字表明,当前使用的UUID生成逻辑的版本号,UUID的生成逻辑有5种。

从这里可以看出,Java中UUID的生成逻辑使用的是第四版(随机数的UUID),这也是为什么Java中一般不使用UUID作为主键,因为每次生成的UUID是无序的,而数据库中B+树每层是有序的,数据插入时就需要额外对数据进行重排序,导致写入能力下降。

UUID的版本有五种,接下来依次介绍5种UUID的生成逻辑

1.基于时间的UUID

通过计算当前时间戳、随机数和机器MAC地址得到。由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。但与此同时,使用MAC地址会带来安全性问题,这就是这个版本UUID受到批评的地方。如果应用只是在局域网中使用,也可以使用退化的算法,以IP地址来代替MAC地址。

2.DEC安全的UUID

和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID,这个版本的UUID在实际中较少用到

3.基于名称空间的UUID(MD5)

基于名称的UUID通过计算名称和名称空间的MD5散列值得到,这个版本的UUID保证了:相同名称空间中不同名称生成的UUID的唯一性;不同名称空间中的UUID的唯一性;相同名称空间中相同名称的UUID重复生成是相同的。

4.基于随机数的UUID

根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但随机的东西就像是买彩票:你指望它发财是不可能的,但狗屎运通常会在不经意中到来。可能在测试的时候多线程并发也不见得出现重复,但是却不能保证系统正式上线之后不会出现不重复的UUID,特别是在分布式系统中。

Java中的UUID默认采用的就是这种方式,这种基于随机数的形式产生的UUID是可能会出现的重复值的,不推荐作为分布式ID

5.基于名称空间的UUID(SHA1)

与第三种几乎一样,只是使用的加密算法不同

总结

在Java中主要使用的是第四版和第三版,其中常用的randomUUID就使用的是第四版,其生成逻辑可能会出现UUID重复的问题,所以不推荐使用Java中的UUID作为主键索引。而第三版主要是对传入的字符串生成唯一编号,所以相同的字符串产生的UUID一定相同。

优点

UUID的生成效率极高,且没有网络消耗。数据量大,影响性能

缺点

UUID生成的字符串太长了,一般不使用UUID做为主键。,并且在Java中的使用的是第四版随机数UUID,还会造成ID重复的问题

数据库主键

数据库的主键保证了其ID的唯一性和递增性质,但其缺点也很明显,每次获取ID都需要和数据库进行交互,产生ID的效率较低,并且受数据库容量大小影响,分库分表时很难扩展

Redis生成ID

可以使用使用redis中的incr和incrby来实现原子增加。

在单台redis性能达不到要求时也可以使用多台redis设置相同步长和不同的初始值来实现

例如:初始化5台redis:1 2 3 4 5 每个步长为5。

缺点

使用多个Redis来生成ID的话,不易于扩展,比如需要扩展、减少redis服务器数量时将会很麻烦

雪花算法

雪花算法产生的ID值是long类型,解决了UUID过长的问题。

雪花算法产生的ID是由64bit组成的。分别是41bit毫秒数10bit的机器ID12bit的流水号和1bit的固定值。

由此可以分析出来 雪花算法中,一个节点一毫秒可以产生2的12次方个数(4096)

如果节点数拉满的话雪花算法最多可以支持400多万的TPS

缺点

  1. 雪花算法因为依赖于时钟,而每台机器的时间可能不同步,并且对于一个节点上的机器来说,当机器的时钟被回拨时可能会产生ID重复的问题。

    如何解决时钟回拨问题

    解决办法一:发号时检测当前时间是否小于上次发号时间,出现时钟回拨时则直接抛异常

    解决办法二:检测到时钟回拨时,如果回拨时间比较小则等待,如果回拨时间较大则采用切换机器ID的形式

  2. 由于机器数只有10bit,所以上限只能是1024台机器,限制了机器数

七种常见分布式事务

(142条消息) 七种常见分布式事务详解(2PC、3PC、TCC、Saga、本地事务表、MQ事务消息、最大努力通知)_张维鹏的博客-CSDN博客

2PC(两阶段提交)

主要分为两个阶段:资源准备、资源提交

资源准备

  • 协调者向各参与者发送事务内容询问是否可以开启事务
  • 参与者开启本地事务(不提交事务)
  • 参与者向协调者反馈是否成功开启和获取资源

此时如果部分参与者执行失败则协调者通知其他参与者进行回滚

资源提交

  • 协调者通知所有参与者执行commit或者rollback

  • 参与者执行成功后反馈

  • 协调者收到所有参与者反馈后,返回事务是否执行成功

从上面很明显可以看出,2PC存在一些问题

  • 数据一致性问题:协调者如果在资源提交阶段宕机,可能会出现部分参与者执行commit,出现数据不一致
  • 性能问题:执行过程中,参与者都是事务阻塞性的,从资源准备阶段参与者就需要占用资源。2PC并不适合高并发的场景
  • 可靠性问题:2PC强依赖协调者,协调者如果在二阶段宕机后,参与者将持续处于事务锁定阶段,导致无法参与后续事务

3PC(三阶段提交)

三阶段是二阶段协议的升级版,加入了超时机制和预提交阶段

整体执行流程为:

  • 准备阶段:协调者询问所有参与者是否可以进入事务阶段(参与者主动反馈或者超时)
  • 预提交阶段:当所有参与者均反馈可以时,进入预提交阶段
    • 协调者向所有参与者发送预提交请求
    • 参与者接收后执行本地事务操作(不提交)
    • 响应协调者
  • 任何参与者没有响应执行成功或者执行超时,协调者都认为执行失败,进入中断流程
  • 提交阶段:协调者发送提交请求,如果协调者宕机导致接收不到提交请求时,参与者会自动提交事务(因为前面预提交阶段已经经过所有参与者确认,参与者更相信事务是可以正常执行的)

2PC和3PC的区别

  • 2PC没有准备阶段,第一阶段就是直接执行,容易导致资源浪费
  • 3PC为参与者引入超时机制
    • 参与者
      • 预提交阶段参与者接收不到指令超时自动执行中断
      • 提交阶段参与者接收不到指令自动执行提交
    • 协调者
      • 准备阶段和预提交阶段超时都会执行中断请求

2PC和3PC都不能保证数据绝对一致,可以添加监控,脚本自动补偿

TCC

TCC(Try confirm cancel)是应用层的两阶段提交协议,对代码入侵很大

MQ/本地消息表

基于消息队列进行实现分布式事务

本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。

本地消息表与业务耦合度较高,可以独立抽出一个消息服务来解耦,但缺点是一次事务期间有两次网络请求,消耗更高。

RocketMQ提供的事务消息也可以解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenyang1026

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值