分布式高并发系统如何保证对外接口的幂等性？
幂等意味着，对于同一个交易的多次处理，结果不变。怎么讲呢？ 假如客户账户Account1现在有100元，执行一次交易（Order01）扣款10元，现在有就变成了90元，在分布式情况下，假如网络超时失败之类的情况下，就会导致可能客户端拿到了TimeoutException，但是实际上Server端执行成功了，这时客户端可能会发起重试，或者用MQ处理的话，MQ会重试。如果再执行一次就扣款10元，就变成了80元，显然这是不对的。况且如果重试操作发生了多次，余额被扣成了负数，客户会疯掉。理想的状态就是，多次的重复处理，结果应该不变，还是90元。假设处理订单的服务方法是f，那么就是： 方法f怎么实现呢？伪代码： 这段代码实现很简单，就是直接把账户减掉订单金额。这个逻辑明显有问题，在上面的Step2步骤的时候，会导致结果是80，而不是我们期望的不变，还是90元。 一个改进办法就是我们可以参考乐观锁：每次先拿到当前的某个版本号标志或者价格，然后修改的时候把这个标识作为条件，就可以在余额已经变化的情况下，不操作了（核心原理就是通过某个条件，判断记录是不是已经变化了）： 这样，只要在一个事务操作里，多次操作是没有关系的，发现金额变了，就不会再变了。多加的幂等判断条件是时间戳，版本号字段，等等都可以，只要是能代表字段被修改过就成。 上面的处理，还有什么问题吗？答案是，如果在上一个订单处理了一段时间以后又有别的订单处理or并发处理，或者是原有订单数据被人工的修订过，那么可能幂等条件被破坏掉了。 继续用上面的例子，假如订单Order01成功以后，订单Order02这时候增加了10元，那么账户又变成100元，这时候要是Order01再来一次，显然要是按照上面说的按100元的条件对比，还是会执行。 怎么办呢？ 一个简单的办法就是，把成功执行过的订单id都记录下来，或者把订单处理的事务里，给订单记上一个成功状态，这个订单状态的操作一定要和账户操作在一个同步的事务里。每次操作的时候用这个状态作为条件，相当于一个悲观锁，就不会重复了。 那么有没有跟简单的办法呢？如果我们假设订单不多，或者可能会重复的数据集中在一定时间范围内，那么可以用内存的办法去重，更简单高效： 例如，我们把新进操作成功的OrderID都直接丢到RoaringBitmap里，假如一天以内的数据是热数据有10-20万，这10-20万个都放到这种压缩的bitmap问题不大，占用内存很小（2-10M），使用也简单，直接检查是不是某个id就可，如果存在就不做任何操作。完全不碰数据库，所以没有io操作，内存的检查操作代价在微秒级。系统每次重启初始化的时候，可以把上一批天的id预先拉出来加载到内存即可。如果获取一天以上的数据，可以先从DB加载，然后放到bitmap，同时可以考虑一下，系统的业务处理流程是不是有问题，或者处了故障，才会导致1天以上数据重复处理，从设计的源头解决问题。。。 可以看到，这种设计比前面的方式复杂的多，但是如果在你的场景里可用，也比其他方法高效的多。高效的方法，往往跟上下文，具体的场景有关系，需要深刻分析问题来探索。 总结一下：幂等就是让多次重复操作的结果跟一次操作的结果是一样的。 可以采用的办法： 附RoaringBitmap的github地址： 