50-分布式

幂等实现方式

对于插入实现幂等

  • 插入前先判断数据是否存在。
    我们会在插入或者更新前先判断下,当前这个数据数据库中是否已经存在,如果不存在则不允许重复插入,不存在则可插入。

  • 主键、唯一索引。

    1. 创建表,并根据请求的某个特殊字段建立唯一索引,或者主键索引。
    2. 客户端请求服务端,服务端先将这次的请求信息存入 MySQL 的去重表中。然后判断是否插入成功,如果插入成功,则继续做后续业务请求。如果插入失败,则代表已经执行过当前请求。

对于更新实现幂等

  • 乐观锁。
    乐观锁就是在表中新增一个version(版本号)字段。通过版本号的方式,来控制 update 的操作的幂等性。
    用户查询出要修改的数据,系统将数据返回给页面,将数据版本号放入隐藏域,用户修改数据,点击提交,将版本号一同提交给后台,后台使用版本号作为更新条件。

  • 悲观锁。
    当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。
    如:支付订单。

  • token。
    token 分成两个阶段:申请 token 阶段和提交阶段。

    1. 第一阶段:客户端向系统发起一次申请 token 的请求,系统将 token 保存到 Redis 中。
    2. 第二阶段:客户端携带申请到的 token 发起提交请求,系统会检查 Redis 中是否存在该 token,如果存在表示是第一次发起的请求,删除缓存中 token 后开始进行相应处理;如果缓存中不存在,表示非法请求或者是重复请求, 返回提示即可。

分布式事务几种解决方案

  • 2PC(Two-phase commit protocol、二阶段提交)

    • 准备阶段(prepare):协调者会给各参与者发送准备命令,你可以把准备命令理解成除了提交事务之外啥事都做完了。
    • 提交阶段(commit/rollback):同步等待所有资源的响应之后就进入第二阶段即提交阶段(注意提交阶段不一定是提交事务,也可能是回滚事务)。
  • 3PC。

  • TCC(Try、Confirm、Cancel)事务补偿。核心思想:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。TCC 模型是把锁的粒度完全交给业务处理。TCC 分为三个阶段:

    • Try 指的是预留,即资源的预留和锁定。
    • Confirm 指的是确认操作,这一步其实就是真正的执行了。Try 阶段执行成功并开始执行。Confirm 阶段时,默认 Confirm 阶段是不会出错的。即:只要 Try 成功,Confirm 一定成功。
    • Cancel 指的是撤销操作,可以理解为在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
  • SAGA 事务。SAGA 事务把大事务拆分成若干个子事务,每个子事务都可以被看作原子行为。我们需要对每个子事务设计对应的补偿动作。如果各个子事务都能提交成功,那么事务就可以顺利完成,否则我们就要采取以下两种恢复策略之一:

    • 正向恢复:如果某个子事务提交失败,则一直对该事务进行重试,直至成功为止,这种适用于事务最终都要成功的场景。
    • 反向恢复:如果某个子事务提交失败,则对该子事务及其之前所有的子事务进行补偿操作。
  • 本地消息表。
    本地消息表顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中,然后在执行业务的时候 将业务的执行和将消息放入消息表中的操作放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。
    然后再去调用下一个操作,如果下一个操作调用成功了好说,消息表的消息状态可以直接改成已成功。
    如果调用失败也没事,会有 后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。
    这时候有可能消息对应的操作不成功,因此也需要重试,重试就得保证对应服务的方法是幂等的,而且一般重试会有最大次数,超过最大次数可以记录下报警让人工处理。

  • 消息事务

  • 最大努力通知。最大努力通知其实只是表明了一种柔性事务的思想:我已经尽力我最大的努力想达成事务的最终一致了


50-分布式
https://flepeng.github.io/interview-50-分布式/
作者
Lepeng
发布于
2020年8月8日
许可协议