知识卡片
用幂等设计支撑失败重试
内容
异步写操作失败后要不要重试、怎么重试,是分布式系统里一个常见但容易被想当然处理的问题——雪球用一个具体场景说明了幂等设计在这里的必要性:用户发一个帖子,API调用时已经给用户返回了成功,但后端写数据库的时候超时了,这时候能不能告诉用户”发帖失败”?显然不行,因为用户已经收到了成功的响应。唯一的办法是重试、重试、再重试,直到写DB真正成功为止。但这里有个隐藏的风险:万一上次那次”超时”的写入,实际上是在超时之前就已经写成功了呢?如果这时候不做任何防护就直接重试,就会往数据库里插入两条重复的记录(比如同一条帖子被插入了两次)。解决办法是把这类关键的写操作步骤设计成幂等的——简单来说,就是数据库层每个表都要有业务逻辑上的唯一性检查(比如给帖子内容加一个业务层面的唯一标识,写入前先检查这个标识是否已存在),这样即便同一个写请求因为重试被发送了多次,最终落到数据库里的效果和只发送一次完全一样,不会因为”不确定上次到底有没有写成功”而被迫在”可能重复写入”和”可能永久丢失一次写入”之间二选一。这个案例揭示的一般性原理是:分布式系统里”这个操作到底有没有成功”本身经常是不确定的(超时不等于失败,也不等于成功),与其试图消除这种不确定性(做不到),不如设计成”重复执行也不会产生副作用”的幂等操作,把不确定性的处理成本从”调用方需要精确判断结果”转移到”被调用方对重复请求天然免疫”,这样调用方就可以简单粗暴地”失败就重试”,不用纠结上一次到底成没成功。
参考来源
- 位置:《高可用架构(第1卷)》第1章《高可用架构案例精选》"1.5 雪球在股市风暴下的高可用架构改造分享"节,"1.5.5 疑问与解惑"(源文件:_epub-src/OEBPS/Text/Chapter1_5_6.xhtml)
- 结论依据:原文用"用户发帖,API已返回成功但写DB超时"的场景说明"那就重试重试再重试,直到写DB成功……那就需要把这个写入做成幂等,支持多次写入同一条记录。简单来说,DB层就是每个表都要有业务逻辑上的唯一性检查",直接支撑本卡片结论。
- 原始内容:举个例子:用户发一个帖子,API调用的时候已经给用户返回成功了,但后端写DB的时候超时了,怎么办?不能再告诉用户发帖失败了吧?那就重试重试再重试,直到写DB成功……那就需要把这个写入做成幂等,支持多次写入同一条记录。