什么是幂等操作?

1149
什么是幂等操作?

1
请查看关于最新幂等操作引发的事件(2022年)的故事:https://threadreaderapp.com/thread/1502947315279187979.html - Qiulang
18个回答

1278

在计算机领域中,幂等操作是指当使用相同的输入参数再次调用该操作时,不会产生任何附加效果。例如,从一个集合中删除一个元素可以被认为是该集合上的幂等操作。

在数学中,幂等操作是指 f(f(x)) = f(x) 的操作。例如,abs() 函数是幂等的,因为对于所有的 xabs(abs(x)) = abs(x)

这些稍有不同的定义可以通过考虑数学定义中的 x 表示对象状态、f 是可能改变该对象的操作来加以协调。例如,考虑Python 中的 set 类及其 discard 方法。 discard 方法从集合中删除一个元素,如果元素不存在,则不执行任何操作。因此,

my_set.discard(x)

执行相同操作两次的效果与执行一次相同操作完全相同:

my_set.discard(x)
my_set.discard(x)

幂等操作常用于网络协议的设计中,其中执行操作的请求保证至少发生一次,但可能会发生多次。如果操作是幂等的,则执行两次或多次操作不会造成任何伤害。

有关更多信息,请参见维基百科上的幂等性文章。


上面的答案先前包含了一些不正确和误导性的例子。在2014年4月之前写下的评论是指较旧版本。


24
由于上面的回答指出“幂等操作经常用于网络协议的设计”,这里有一个相关的例子。**GET 不应该改变服务器上的任何内容,因此 GET 是幂等的。在 HTTP/servlet 上下文中,这意味着相同的请求可以重复发送而没有负面影响。**POST 不是幂等操作。 - KNU
4
“stateless”和“idempotent”是同义词吗? - Michael Osofsky
4
在回答中的 Python set 示例中,set对象显然具有状态,并且还提供了一些幂等操作,如 discard - Greg Hewgill
2
@MichaelOsofsky,“discard”也可以通过将状态包含在返回值中以无状态的方式实现:discard([my_set, x])=[my_new_set, x]。因此,您可以执行discard(discard([my_set, x]))。请注意,[my_new_set, x]只是一个参数,其类型为2元组。 - Pacerier
9
在阳痿的背景下,当使用“同样的效果”一词时,它意味着“结果”相同,而不是“行动”相同。第二次调用discard(x)将产生与第一次调用相同的效果:集合中将不再包含x。 "计算幂等性"是关于系统的鲁棒性。由于事物可能会失败(例如,网络中断),当检测到故障时,如何恢复?最简单的恢复方法是再次尝试,但仅在再次尝试具有幂等性时才有效。例如,discard(x)具有幂等性,但是pop()则没有。这全部关乎错误恢复。 - Andreas
显示剩余11条评论

178

幂等操作可以重复任意次,结果与仅执行一次相同。在算术中,将数字加零是幂等的。

在“RESTful”网络服务上下文中经常谈论幂等性。REST试图最大限度地利用HTTP让程序访问Web内容,并通常与基于SOAP的网络服务形成对比,后者只是在HTTP请求和响应中隧道远程过程调用样式服务。

REST将Web应用程序组织成“资源”(例如Twitter用户或Flickr图像),然后使用POST、PUT、GET和DELETE这些HTTP动词来创建、更新、读取和删除这些资源。

幂等性在REST中扮演着重要角色。如果您获取REST资源的表示形式(例如从Flickr获取jpeg图像),并且操作失败,您可以重复进行多次GET,直到操作成功。对于Web服务而言,获取图像的次数并不重要。同样地,如果您使用RESTful Web服务更新Twitter帐户信息,则可以将新信息作为PUT提交多次,直到得到Web服务的确认为止。把它PUT一千次与PUT一次相同。同样,删除REST资源一千次与删除一次相同。因此,幂等性使得构建一个对通信错误具有弹性的Web服务变得更加容易。

进一步阅读:RESTful Web Services,作者是Richardson和Ruby(幂等性在第103-104页上讨论),以及Roy Fielding的关于REST的博士论文。Fielding是HTTP 1.1,RFC-2616的作者之一,该文档在第9.1.2节中讨论了幂等性。


21
"idempotence"这个词非常具有歧义性,因为它听起来很夸张,而且有足够多的字符可以通过长单词检查。如果Benjamin Peirce选择了一个更简单的词,今天我们甚至不会有这个问题。 - Pacerier
3
如何理解这句话:“同样地,对一个 REST 资源进行一千次 DELETE 操作和进行一次操作是一样的”?如果资源已经被删除,你就无法再次删除它。 - Green
3
@Green 但是你第一次不要删除它,而是发送一个删除请求。重要的是,你可以随意发送多个删除请求。 - Caleth
2
@JimFerrans 我明白了。我原以为PUT可以被重新发送而不用担心,而POST则不能是因为HTTP本身的某些功能相关原因。现在看来,我们只需要遵守HTTP标准,行为完全取决于服务器的实现方式。 - mangusta
1
在算术中,将零加到一个数字上是幂等的。这是一个糟糕的例子,因为添加零不会改变任何东西。幂等意味着它(可能)会改变事物,但以相同的方式应用一次或多次。上面进一步给出的abs()函数是一个更好的例子。 - dret
显示剩余4条评论

150
无论您调用该操作多少次,结果都将相同。

8
我听说过 "幂等" 的定义可以是以下一种或两种:1)对于给定的输入,它将始终返回相同的输出。 2)不产生任何副作用。我的问题是,如果一个函数符合 #1,但不符合 #2,因为它会产生与计算无关的副作用(例如将请求记录到数据存储器中),那么它仍被认为是幂等吗? - Keith Bennett
13
执行一个操作后的结果必须包含系统的状态。如果该操作具有一些累积性副作用,则该操作不是幂等的;但是,如果该副作用无论操作被调用多少次都会使系统保持相同的状态,则该操作可能是幂等的。 - Robert
9
简短明了的回答是我喜欢的那种回答。不确定为什么我不断地需要查找这个术语,它就是无法留在我脑海中的一个词。 - Prancer
1
@KeithBennett,第二个定义是错误的。“无副作用”并不意味着幂等。幂等函数可能会有副作用。例如MySQL的truncatedelete - Pacerier
1
如果数据库中有一个行计数器,用于检查请求输入与数据库状态的数据完整性,那么这个行计数器是否需要考虑在幂等性定义中?该行计数器将针对每个请求增加,但不作为结果的一部分返回。 - swcraft
显示剩余3条评论

64

幂等性意味着应用一次或多次操作具有相同的效果。

例子:

  • 乘以零。无论你做了多少次,结果都是零。
  • 设置布尔标志。无论你做了多少次,标志都会保持设置状态。
  • 使用给定 ID 从数据库中删除行。如果您再试一次,则该行仍然不存在。

对于纯函数(没有副作用的函数),幂等性意味着对于所有 x 的值,f(x) = f(f(x)) = f(f(f(x))) = f(f(f(f(x)))) = ......

对于带有副作用的函数,幂等性还意味着在第一次应用后不会导致任何其他副作用。如果您喜欢,您可以将世界的状态视为函数的另一个“隐藏”参数。

请注意,在存在并发操作的情况下,您可能会发现您认为是幂等的操作不再是幂等的(例如,另一个线程可能会取消设置布尔标志的值)。基本上,每当您拥有并发和可变状态时,您都需要更加仔细地思考幂等性。

幂等性通常是构建强大系统的有用属性。例如,如果存在从第三方接收重复消息的风险,则将消息处理程序作为幂等操作是有帮助的,以便消息效果仅发生一次。


1
如果对于纯函数而言 f(x) = f(f(x)),那么你的意思是 f(x){return x+1;} 不是一个纯函数吗?因为 f(x) != f(f(x))f(1) 的结果为 2 而 f(2) 的结果为 3。 - Pacerier
4
@Pacerier,@mikera的意思是说纯 幂等意味着 f(x) = f(f(x))。但正如@GregHewgill所提到的,为了使这个定义有意义,您必须将 x 视为一个对象,将 f 视为一种操作,该操作会改变对象的状态(即:f 的输出是一个已经改变的 x)。 - Justin J Stark

39

理解幂等操作的一个很好的例子可能是使用遥控钥匙锁定汽车。

log(Car.state) // unlocked

Remote.lock();
log(Car.state) // locked

Remote.lock();
Remote.lock();
Remote.lock();
log(Car.state) // locked

lock 是幂等操作。即使每次运行 lock 都带有一些副作用,比如闪烁,但无论运行多少次,车辆仍然处于相同的已锁定状态。


2
一个好的想法是将其与一些遥控器进行对比,这些遥控器不是有两个按钮 lock()unlock(),而是只有一个按钮 toggleLock()。在这种情况下,点击该按钮不是幂等的 - 每次单击都会更改状态,交替在“解锁”和“锁定”之间。 - Stobor

30

幂等操作是指,无论您调用它多少次,只要传入相同的参数,它都会以相同的状态产生结果。


1
听起来一点也不合乎逻辑。https://dev59.com/vXNA5IYBdhLWcg3wIqPr#-r_mnYgBc1ULPQZFzAfl - Green
3
我认为你可能混淆了“幂等”和“确定性”。 - Suncat2000

21

幂等操作是一种可多次应用而不会改变结果的操作、动作或请求,即系统状态在最初应用后不会发生改变。

示例(Web 应用程序上下文):

幂等: 进行多个相同请求具有与进行单个请求相同的效果。例如,电子邮件消息系统中的一条消息被打开并在数据库中标记为“已打开”。可以多次打开该消息,但此重复操作只会导致该消息处于“已打开”状态。这是一种幂等操作。第一次使用与资源(系统状态)不匹配的信息向资源进行PUT更新时,系统状态会随着资源的更新而更改。如果多次使用相同的信息向资源进行PUT更新,则每次PUT中更新的信息将与系统中已有的信息相匹配,并且不会对系统状态进行任何更改。重复PUT的操作是幂等的:第一次PUT可能会改变系统状态,后续的PUT不应该再改变系统状态。

非幂等: 如果一个操作总是导致状态改变,比如反复向用户POST相同的消息,导致每次都会发送并存储新消息到数据库中,那么我们称该操作是非幂等的。

零副作用: 如果一个操作没有任何副作用,比如在 Web 页面上纯粹显示信息而不更改数据库(换句话说,您只是读取数据库),我们称该操作为零副作用。所有 GET 操作都应该是零副作用的。

在谈论系统状态时,我们显然会忽略类似日志记录和诊断等无害且不可避免的影响。


18

我想举一个真实的用例来展示幂等性。在JavaScript中,假设您正在定义一堆模型类(如MVC模型)。通常情况下,这是通过以下方式实现的,其功能等效于以下基本示例:

function model(name) {
  function Model() {
    this.name = name;
  }

  return Model;
}

您可以按照以下方式定义新类:

var User = model('user');
var Article = model('article');

但是如果您试图通过代码中的其他地方使用model('user')获取User类,则会失败:

var User = model('user');
// ... then somewhere else in the code (in a different scope)
var User = model('user');

这两个User构造函数是不同的。也就是说,

model('user') !== model('user');

为了使其具有幂等性,您只需添加某种缓存机制,例如:

要使其幂等,只需添加某种缓存机制,如下所示:

var collection = {};

function model(name) {
  if (collection[name])
    return collection[name];

  function Model() {
    this.name = name;
  }

  collection[name] = Model;
  return Model;
}

通过添加缓存,每次您执行model('user')时都会得到相同的对象,因此具有幂等性。因此:

model('user') === model('user');

1
这个答案似乎不正确。幂等性是关于调用一个你期望通过输入参数改变一些状态的操作,如果你再次使用相同参数调用该操作,则对状态没有进一步影响。 但在你的示例中,在甚至使用缓存之前,如果我们两次调用model('user'),那么这已经是幂等的,没有状态的改变,只是创建并返回一个新的不同于之前调用的对象给调用者(而不是存储)。您正在描述一个对象身份工厂模式,确保跨调用返回“相同”的对象,这很有用,但并未解释幂等性。 - Ash

11

非常详细和技术性的答案。只需添加一个简单的定义。

幂等 = 可重新运行

例如,Create 操作本身如果执行多次,则不能保证不出现错误。但是,如果存在 CreateOrUpdate 操作,则说明它具有可重新运行性(幂等性)。


5
这是一个具有欺骗性的定义。可重新运行性并不能保证幂等性。一个操作可以是可重新运行的,但每次运行时都可能向结果添加额外效果,因此它就不是幂等的。 - Saeed Mohtasham

10

幂等操作:如果多次执行,没有副作用的操作。


例子:从数据资源中检索值并打印的操作。


非幂等操作:如果多次执行会造成一些伤害的操作。(因为它们会改变某些值或状态)


例子:从银行账户中取款的操作。


6
“实际上这是错误的答案!对于幂等操作而言,声称其“没有副作用”是不正确的。对于非幂等操作而言,声称其“会造成一些伤害”也是一个令人困惑的回答。” - Saeed Mohtasham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接