幂等设计

幂等设计

基本概念

幂等(idempotent)是一个数学与计算机学概念,常见于抽象代数中。
在编程中,一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。

常见场景

  1. 客户端发起重复请求,服务端处理需要保证幂等;
  2. 服务端RPC同步调用,被调用方需要保证幂等;
  3. 服务端异步消息实现分布式事务,消费者处理需要保证幂等。

广义上的RPC,包括客户端对服务端的api调用、后端系统的内网调用、跨机房调用等。一次RPC大体上包括三个步骤:发送请求、执行过程、接收响应。由于网络传输存在不确定性,导致RPC调用存在一个陷阱,即有可能出现第一、第二步都成功、第三步失败的情况,此时RPC的调用方由于接收不到结果,无法判断被调用方是否已经完成过程调用,只能按失败处理。
通常RPC调用方会针对网络失败进行重试。在上述情况下,如果远端代码不具备幂等性,却进行了重试,将导致系统的数据一致性遭到破坏,本该只执行一次的事务被执行了两次。
对于异步消息的消费者来讲,也有同样的问题。在手动ACK的情况下,消息的处理需要接收消息、处理消息、ACK三步,ACK的失败也会导致相同的问题。

重试是降低系统失败率的重要手段。因此,幂等设计是非常有必要的。幂等性在技术上同时要求可重入性。在分布式系统中,重试和并发都是常态,允许多次调用代码也应该支持并发调用。分布式锁(包括数据库锁)可以解决这一问题。

常用幂等设计方法

1. 本地事务幂等设计
先考虑数据库的增删改查四种基本操作:
- select,天生幂等
- insert,数据库自增主键时不具备幂等
- 基于主键update,具备幂等,但是带查询的更新除外(形如update t set x = x + 1 where …)
- 基于主建delete,具备幂等
- 非基于主键的udpate/delete操作,需要具体问题具体分析
很显然,如果一个事务中所有数据库操作都是幂等操作,且不存在外部调用,那么该事务一定也是幂等的。

如果事务中存在非幂等的数据库操作,唯一键为本地事务的幂等性提供了实现基础。使用带有唯一键的去重表,在事务中先执行insert去重表,再执行其他业务操作,然后提交事务,事务过程中出现异常回滚事务,可以保证事务的幂等性。
本地事务幂等设计流程图

考虑两种失败的情况:
- Insert去重表失败,事务回滚,无任何影响,调用方应停止重试
- Insert去重表成功,业务操作失败,事务回滚,之前插入的记录也将消失,无任何影响,调用方可以选择重试
以上两种失败的情况下,事务的幂等性是可以保证的。

补充说明,在使用JDBC连接MySQL的情况下,如果程序需要捕获唯一键冲突异常,可以catch com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException。

在并发的情况下,如果两个(数据库的)客户端同时执行该事务且唯一键相同,此时唯一键能起到分布式锁的作用。

2. 分布式事务幂等设计

要保证分布式事务的幂等性,需要各个子事务都保证幂等性,否则整体幂等性的很难实现。

在基于关系型数据库的系统中,分布式事务最终还是要依靠数据库的本地事务来实现事务的ACID。
考虑存在A、B两个子系统分别实现各自的事务,需要整合为一个分布式事务,其中A系统的本地事务基于MySQL实现,保证幂等;B系统的本地事务也保证幂等,对外提供RPC服务接口,其具体实现这里不关心。

在这种情况下,有一个简单的不需要事务仲裁者的分布式事务方案,就是在A事务中嵌套对B的调用。具体流程是A事务先执行本地数据库操作,再调用B接口,然后提交事务。

同样,考虑三种失败的情况:
- A本地操作失败,事务回滚,无任何影响
- A本地操作成功,调用B失败,如果是唯一性冲突导致的失败,A正常提交;如果是其他失败,事务回滚,此时外部调用方可以重试
- 很极端的情况,A本地操作成功,调用B成功,事务提交失败,此时外部调用方可以重试

这个方案并不是一个高可靠的解决方案,但是实现上非常简单。

其缺陷在于如果外部调用方没有进行重试,那么可能会产生对B的多余调用。当然,如果在业务上对B的少量多余调用是可接受的,比如B是次要业务,那这个方案也是可以用的。

分布式事务不可能实现绝对的一致性。我们应该从概率的角度思考问题,如果能将不一致的概率降到最低,比如保证幂等性加重试,再辅以业务监控和人工干预,就可以实现系统整体上较高的一致性。

实现幂等的手段

未完待续

[1]: http://www.caosh.me/be-tech/idempotence-using-unique-key/ 使用数据库唯一键实现事务幂等性
[2]: http://www.cnblogs.com/firstdream/p/7089735.html 幂等设计
[3]: http://chenzhou123520.iteye.com/blog/1863407 乐观锁
[4]: http://chenzhou123520.iteye.com/blog/1860954 悲观锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值