分布式事务最终一致性

原创:mysql05/10/2020发布pv:0uv:0ip:0twitter #mysql

原文地址:https://www.douyacun.com/article/78899ae36e0dae22e49e54e62448f970

最终一致性

建议阅读

原则还是尽量保持单机事物,只有单库执行才能保证数据库的ACID特性,遇到跨库事物, 通常通过“最终一致”事物保证事物执行的吞吐量,

  • 定义
  • 原理

定义

系统中所有的数据副本经过一段时间后最终能够达到一致的状态

原理

保证核心事物的正向执行,然后保存事物中间状态,其他事物异步执行,执行完成后达到最终的事物一致,避免跨库事物时间序列执行阻塞,提升事物吞吐量

实现模式

  • 可靠事件模式
  • 业务补偿模式
  • TCC模式

可靠事件

关键点在于:可靠事件投递避免事件重复消费

可靠事件投递:

  • 每个服务原子的业务操作和发布事件
  • 事件代理系统确保事件传递至少一次,避免重复消费,要求事件消费系统实现 幂等性,eg. 支付服务不能因为重复收到事件就多次支付

本地事件表

  1. 在同一个本地事物中记录业务数据和事件
  2. 实时发布一个事件 事件系统 立即通知关联的业务服务,事件发布成功立即删除记录的事件
  3. 事件恢复服务定时从事件表中恢复未发布成功的事件,重新发布,重新发布成功才删除记录

2要求实时性,第三条保证一定发布,额外的事件数据库可能会给数据库带来额外的压力

外部事件表

将事件持久化到外部事件系统,事件系统需要提供实时事件服务以接受微服务发布事件,事件系统需要提供事件恢复服务来确认和恢复服务

  1. 业务服务 在事物提交前,通过实时事件服务向事件系统请求 发送事件, 事件系统只记录事件不会真正发送事件
  2. 业务服务 在事物提交后,向事件系统确认发送,事件得到确认后事件系统才真正发送事件
  3. 业务服务 在事物回滚后,请求事件系统取消发送
  4. 事件系统定时查找未确认发送的事件向业务服务查询状态,根据返回状态决定取消还是发布

需要一次确认操作 并且需要 业务服务提供额外的查询接口, 事件幂等性

issue

  1. 幂等性

    TODO:: 待完善

    幂等事件要考虑执行顺序,

    • 保证事件顺序的一个简单做法就是在添加时间戳,微服务记录最后处理的时间戳,如果事件戳早于记录的时间戳,丢弃
    • 更稳妥的做法是,每个事件有个全局递增的事件id,为每条事件记录处理结果,如果处理过返回上一次的处理结果,否则调度执行事件
  2. 事件消费的开销

    需要考虑重复执行一条事件和查询存储结果的开销

    对于重复处理开销很小的事件,或者只有非常少的事件会被重复处理接收,可以重复处理一次事件,在将事件数据持久化时由数据库 唯一索引 抛出唯一性约束异常

    对于重复处理一条事件的开销比额外一次查询的开销要高很多, 可以使用过滤服务过滤处理的过的事件, 每个事件执行完成后记录执行结果

  3. 过滤服务是在事件处理完成后记录结果,可能处理中间就再次收到重复事件

    可以记录事件的处理过程:

    • 接收
    • 处理中
    • 收到应答
    • 处理完成

    可以根据不同的状态,做不同的处理

  4. 业务服务的 消息处理状态,发送请求成功但是没有收到处理结果

    选择暂时不一致性,采用对账和人工接入的方式保证一致性

业务补偿模式

  • 业务异常:业务逻辑异常,eg. 商品库存不足、账户余额不足
  • 技术异常:非业务逻辑异常,eg. 网络连接异常、请求超市

补偿模式使用一个额外的协调服务来保证各个需要保证一致性的微服务,协调服务按顺序调用各个微服务,如果某个微服务调用异常(包括业务异常和技术异常)就取消之前已经调用成功的微服务

为了降低开发的复杂度和效率,协调服务需要实现为一个通用补偿框架,提供服务编排和自动完成补偿的能力 - 确定失败的步骤和状态,从而确定补偿的范围 - 能提供补偿操作使用到的业务数据 - 生成全局唯一的业务流水号 - 关联表,获取各个步骤的业务数据和状态 - 提供重试策略

重试策略

  • 失败原因不是暂时性的,由于业务因素导致的,应立即终止重试
  • 错误是一些罕见的异常,eg. 网络传输过程中数据丢失,应该立即重试
  • 如果错误的是系统繁忙,eg. 500,或者超时 需要等一段时间再次重试
  • 重试操作要指定次数,达到上线就不应该再次重试了
  • 如果重试时仍然错误,可逐渐增加时间,直到达到上限后
  • 如果某个时刻存在大量重试操作,补偿框架要控制请求的流量,防止对工作服务造成过大的压力

TCC模式

建议阅读

3个阶段

try

预处理阶段

confirm

事物提交阶段

cancel

事物回滚阶段