重发又称为幂等性(Idempotency,即f(x) = f(f(x))
),是指重复发送后会得到和和首次发送相同的结果,且不会导致不良的结果。重发是一个很重要的系统特性,数据库系统的事务设计、系统异常的处理都依赖对重发的支持。重发说起来简单,但做起来可不简单,有些交易本身天然支持重发,例如将所有记录状态改为正常,重复做一次并没有什么问题。但有些交易支持重发则很困难,例如向A转账10元,如果重做一次就可能会重复入账。
对于不是天然支持重发的交易,如果要支持可重发,需要做一些特殊处理。简单的方法是通过唯一标识来判重,具体步骤如下:
- 发送方生成消息的唯一标识,在发送消息中附上此标识;
- 接收系统收到消息在处理时,在完成消息处理的同时进行唯一标识的判重。例如记录流水,在流水中包括此标识,并对此标识通过唯一索引来判重;
- 对于消息重复的情况,给客户端反馈特定的错误码、错误信息,接收方以此得知消息已重复。交易结果建议反馈“处理中”,以免客户认为交易失败。
如果流水信息中不便于添加此标识,可以建立一个辅助表,用于存储此标识,通过唯一索引判重。记录辅助表和更新其他信息尽量放在一个事务中以免双方记录不一致。由于判重一般只要保证一段时间即可,故此辅助表的数据不必保留太久。
此处记录辅助表和记录流水放在一个事务中是为了保证一致性,如果不采用事务保证,建议先记录判重辅助表,这时可能会出现记录了辅助表,但未记录流水的情况(可能记录流水时出错,或是记录流水前出错),这时如果再有相同消息进来仍然会报错。这种程度的误报你需要分析业务上是否有问题,这种情况下你的系统响应是否正确。不建议在记录流水后再记录判重表,这时数据库中的流水记录无法回滚,这时客户来查消息结果会得到两条流水,很难区分哪个是正确的结果。
对于系统交易判重不建议采用查询方式,即在记录流水前查询一下流水是否存在,因为会有并发问题,之前生产上也遇到过,当两笔交易几乎同时发送时就会出现判重规则失效,记录重复流水的问题。需要指出,这并不是小概率事件,而是设计缺陷。
系统正确性的保证依赖与系统两方面的能力支持:1.可测试性,即系统支持调用方查询交易结果。2.可重发,且不会因为重复执行造成不良影响,即幂等性。系统只要支持其中一种就行,如果一种都不支持,永远无法保证操作正确性。其实很多实操做就是如此,例如你需要向水中扔1个石头,如果你正巧扔的时候精神溜号了,等你回过神发现水面很平,你不知道是否扔进去了,你现在再扔1个可能多扔了,如果不扔可能前面1个没扔进去。对于这种实操作,必须在执行前深重考虑,确保精神不要溜号,哈哈。