在计算机领域中,幂等操作是指当使用相同的输入参数再次调用该操作时,不会产生任何附加效果。例如,从一个集合中删除一个元素可以被认为是该集合上的幂等操作。
在数学中,幂等操作是指 f(f(x)) = f(x) 的操作。例如,abs()
函数是幂等的,因为对于所有的 x
,abs(abs(x)) = abs(x)
。
这些稍有不同的定义可以通过考虑数学定义中的 x 表示对象状态、f 是可能改变该对象的操作来加以协调。例如,考虑Python 中的 set
类及其 discard
方法。 discard
方法从集合中删除一个元素,如果元素不存在,则不执行任何操作。因此,
my_set.discard(x)
执行相同操作两次的效果与执行一次相同操作完全相同:
my_set.discard(x)
my_set.discard(x)
幂等操作常用于网络协议的设计中,其中执行操作的请求保证至少发生一次,但可能会发生多次。如果操作是幂等的,则执行两次或多次操作不会造成任何伤害。
有关更多信息,请参见维基百科上的幂等性文章。
上面的答案先前包含了一些不正确和误导性的例子。在2014年4月之前写下的评论是指较旧版本。
set
示例中,set
对象显然具有状态,并且还提供了一些幂等操作,如 discard
。 - Greg Hewgilldiscard([my_set, x])=[my_new_set, x]
。因此,您可以执行discard(discard([my_set, x]))
。请注意,[my_new_set, x]
只是一个参数,其类型为2元组。 - Pacerierdiscard(x)
将产生与第一次调用相同的效果:集合中将不再包含x
。 "计算幂等性"是关于系统的鲁棒性。由于事物可能会失败(例如,网络中断),当检测到故障时,如何恢复?最简单的恢复方法是再次尝试,但仅在再次尝试具有幂等性时才有效。例如,discard(x)
具有幂等性,但是pop()
则没有。这全部关乎错误恢复。 - Andreas幂等操作可以重复任意次,结果与仅执行一次相同。在算术中,将数字加零是幂等的。
在“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节中讨论了幂等性。
truncate
和delete
。 - Pacerier幂等性意味着应用一次或多次操作具有相同的效果。
例子:
对于纯函数(没有副作用的函数),幂等性意味着对于所有 x 的值,f(x) = f(f(x)) = f(f(f(x))) = f(f(f(f(x)))) = ......
对于带有副作用的函数,幂等性还意味着在第一次应用后不会导致任何其他副作用。如果您喜欢,您可以将世界的状态视为函数的另一个“隐藏”参数。
请注意,在存在并发操作的情况下,您可能会发现您认为是幂等的操作不再是幂等的(例如,另一个线程可能会取消设置布尔标志的值)。基本上,每当您拥有并发和可变状态时,您都需要更加仔细地思考幂等性。
幂等性通常是构建强大系统的有用属性。例如,如果存在从第三方接收重复消息的风险,则将消息处理程序作为幂等操作是有帮助的,以便消息效果仅发生一次。
f(x) = f(f(x))
,那么你的意思是 f(x){return x+1;}
不是一个纯函数吗?因为 f(x) != f(f(x))
:f(1)
的结果为 2 而 f(2)
的结果为 3。 - Pacerierf(x) = f(f(x))
。但正如@GregHewgill所提到的,为了使这个定义有意义,您必须将 x
视为一个对象,将 f
视为一种操作,该操作会改变对象的状态(即:f
的输出是一个已经改变的 x
)。 - Justin J Stark理解幂等操作的一个很好的例子可能是使用遥控钥匙锁定汽车。
log(Car.state) // unlocked
Remote.lock();
log(Car.state) // locked
Remote.lock();
Remote.lock();
Remote.lock();
log(Car.state) // locked
lock
是幂等操作。即使每次运行 lock
都带有一些副作用,比如闪烁,但无论运行多少次,车辆仍然处于相同的已锁定状态。
lock()
和 unlock()
,而是只有一个按钮 toggleLock()
。在这种情况下,点击该按钮不是幂等的 - 每次单击都会更改状态,交替在“解锁”和“锁定”之间。 - Stobor幂等操作是指,无论您调用它多少次,只要传入相同的参数,它都会以相同的状态产生结果。
幂等操作是一种可多次应用而不会改变结果的操作、动作或请求,即系统状态在最初应用后不会发生改变。
示例(Web 应用程序上下文):
幂等: 进行多个相同请求具有与进行单个请求相同的效果。例如,电子邮件消息系统中的一条消息被打开并在数据库中标记为“已打开”。可以多次打开该消息,但此重复操作只会导致该消息处于“已打开”状态。这是一种幂等操作。第一次使用与资源(系统状态)不匹配的信息向资源进行PUT更新时,系统状态会随着资源的更新而更改。如果多次使用相同的信息向资源进行PUT更新,则每次PUT中更新的信息将与系统中已有的信息相匹配,并且不会对系统状态进行任何更改。重复PUT的操作是幂等的:第一次PUT可能会改变系统状态,后续的PUT不应该再改变系统状态。
非幂等: 如果一个操作总是导致状态改变,比如反复向用户POST相同的消息,导致每次都会发送并存储新消息到数据库中,那么我们称该操作是非幂等的。
零副作用: 如果一个操作没有任何副作用,比如在 Web 页面上纯粹显示信息而不更改数据库(换句话说,您只是读取数据库),我们称该操作为零副作用。所有 GET 操作都应该是零副作用的。
在谈论系统状态时,我们显然会忽略类似日志记录和诊断等无害且不可避免的影响。
我想举一个真实的用例来展示幂等性。在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');
非常详细和技术性的答案。只需添加一个简单的定义。
幂等 = 可重新运行
例如,Create
操作本身如果执行多次,则不能保证不出现错误。但是,如果存在 CreateOrUpdate
操作,则说明它具有可重新运行性(幂等性)。
幂等操作:如果多次执行,没有副作用的操作。
例子:从数据资源中检索值并打印的操作。
非幂等操作:如果多次执行会造成一些伤害的操作。(因为它们会改变某些值或状态)
例子:从银行账户中取款的操作。