你大概有过这样的经历:在电梯前按了一次按钮,心里还不踏实,又多按了几下。灯已经亮了,再按也不会来两部电梯。反过来,在线支付窗口里连点了两次按钮,也曾担心会不会扣两次款。同样是按一下,一边安全、一边危险,把原因说清楚的概念,就是幂等性。
幂等性(Idempotence)指的是:某个操作做一次还是做多次,结果都不会在第一次完成之后再继续改变。简单说就是“再做一次,已经结束的事也不会再变大”,在连接常断、请求常重复的计算机世界里尤其重要。
本资料会依次说明:幂等性这个名字从何而来、日常例子如何分界、HTTP 如何把这一概念立成约定,以及安全处理重复请求的设计与常见误解。
做两次也和做一次一样的道理
意思是“同样的力”,从数学借来的名字

这个术语据称由美国数学家本杰明·皮尔斯(Benjamin Peirce)在 1870 年讨论线性结合代数(Linear Associative Algebra)的文章中创造。它把表示“相同”的 idem 和表示“力”的 potence 合在一起,字面意思就是“具有相同的力”。皮尔斯把这个名字给了那些平方或更高次幂之后仍等于自身的代数对象。
(1) 数学定义
指把函数 f 应用两次的结果与应用一次相同,写作 f(f(x)) = f(x)。
(2) 代表性函数
绝对值、舍去小数部分的向下取整函数、向上取整函数,以及原样返回输入的恒等函数,都具有这种性质。
(3) 进入计算机后的含义
即便是有副作用的操作,只要多次调用后的系统状态与调用一次后相同,也称为幂等。
这里要留意的是:标准不是“响应相同”,而是“状态不再进一步改变”。弄错这一点,就会把幂等性和其他概念搅在一起。
用日常按钮分清幂等与非幂等

电梯呼叫按钮或人行横道按钮,第一次按下会进入“请求中”状态,在该请求处理完之前再按也不会有事发生。关门按钮也按既定时间表关门,按多少次都一样。但开门按钮每按一次等待时间就会延长,所以不是幂等。
- 查询(Lookup):读取客户的姓名和地址不会改数据,因此是幂等的。
- 设置(Assignment):把地址改成某个特定值的请求,发多少次最终地址都一样,因此是幂等的。
- 下单(Order Placement):发出下单请求,发几次就可能产生几笔订单,因此不是幂等。
- 取消(Cancellation):取消某笔特定订单的请求,发多少次订单都仍是已取消状态,因此是幂等的。
同是“按一下按钮”,是把状态定死,还是往状态里再加一点,命运由此分开。
HTTP 为各方法定下的约定

作为网络协议的 HTTP 标准文档(RFC 9110)规定:同一请求发送多次时,对服务器产生的预期效果与发送一次相同的方法,称为幂等。
(1) 读取请求
GET、HEAD、OPTIONS、TRACE 只请求读取,属于安全方法,且全部幂等。
(2) PUT 与 DELETE
PUT 把目标状态设为所发送的内容,DELETE 删除目标。无论发送多少次,结束状态都一样,因此被规定为幂等。
(3) POST 与 PATCH
POST 把如何处理内容交给接收方,发送两次就可能产生两笔订单。PATCH 也不保证幂等。
这一区分真正派上用场的地方是重试。连接中断、读不到响应时,幂等请求可以由客户端自动再发;非幂等请求若没有办法确认原请求未生效,标准建议最好不要自动重试。另外,幂等并不是“响应相同”的承诺,所以第二次删除可能返回与第一次不同的“不存在”响应,也没有问题。

处理重复请求的设计与常见误解

那么,不得不重发非幂等的 POST 时该怎么办?广泛使用的办法是给每次请求附上唯一值的幂等键。支付服务 Stripe 允许在请求上加 Idempotency-Key 头。键最长 255 个字符,建议用随机 UUID 一类的值;同一键再次到来时,会原样返回第一次请求的状态码和正文。键至少 24 小时后可能被清除;同一键却带上不同内容,会按错误处理。即使消息到达两次以上也给出相同结果的事件处理系统,也建立在同一思路之上。
- 响应(Response):幂等不是指响应相同,而是指服务器的状态不再进一步改变。
- 副作用(Side Effect):并不把服务器留下请求记录之类也算进去,只以预期效果为准。
- 合成(Composition):把幂等的步骤串起来,也不能保证整体仍是幂等。
- 示例(Example):变量为 3 时,执行一次“读取、改成 5、读取”会得到 3 和 5;第二次则得到 5 和 5。
让人按两次也没关系,靠的不是用户的小心,而是把系统设计得自己就安全。从一开始就按“再做一次也得到同样结果”来设计动作——那就是幂等性的第一步。