【设计】幂等性[1] 概念分析

149 阅读3分钟

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

幂等性

前言

面试被问到了:

  • 幂等性是啥?线程安全是啥?
  • 如何设计幂等性?
  • 幂等性字段如何设计?

一脸懵逼,今天学习一下。

定义

幂等性本来是一个数学上的定义:

  • f(x) = f(f(x))

即,对一个方法的多次调用,和对这个方法的单次调用,结果相同,那么这个方法就又幂等性。

拿到编程中,就相当于:

任意多次执行所产生的影响均与一次执行的影响相同

常用的场景就比如:

  • 最经典的就是和钱相关的,不能多次付款,也不能多次发货等

推广一点说,幂等性其实和数据库中的唯一索引这一概念很接近,我们必须保证在某些限制条件下某些事件只能发生一次。

分析

哪些方法需要保证幂等?

代码中的逻辑,如果没有数据层面、外部等消息的实质变更时,一般来说并不具有幂等性的分析价值。

对于数据、外部消息等变更(落到这一步其实就是CRUD了),其中的幂等关系如下:

  • 查询:查询并不涉及实质性的变更,因此对于查询无需保证幂等性,但需要保证在其他操作下查询结果是符合预期的,也就是说查询实际上是我们对于幂等性结果的一种检查手段

  • 更新、新增:根据最原本数学上的定义,其实在某些场景下更新本身是幂等的,例如:

    update table set column = 'A' where id = 1;

    这里无论更新多少次,我们从输入到预期其实都没什么问题:

    • 希望让id=1的记录字段变成A,执行多少次那都是这个结果。

    而插入本身和幂等关系大吗?其实也没什么关系,无非就是一条记录嘛。

    但如果更新是根据已有条件的话,那么就得加上幂等性了:

    update score set score = score+100;

    执行多少次分数就加多少了,那么我们就得严格控制执行次数来实现幂等,否则:

    如果我们期望执行1次,分数一人加100,但是实际执行了2次,那么分数一人加了200,和预期就不符合了。

    插入也是相同的逻辑:

    如果我们期望某个限定条件下只有一条记录,但插了2次表且正常插入了,那么就不符合对于表记录的逾期了。

  • 删除:如果是确定范围的删除,那么其实删除操作天然具有幂等性:

    delete from score where id = 1;

    这无可厚非嘛,删了之后上哪里找呢?删除的记录又不可能再删一次。

    但如果是动态条件的删除呢?

    例如,我们要删除表中id最大的那条记录

    那么,如果我们发了1次请求但执行了2次,那么就删了2条,就和预期不符了,因此删除在某些条件下,也是需要保证幂等性的。

根据上面的分析,发现:

  • 固定条件即:无论我们如何查,scope都是相同的下,其实CRUD天然幂等;
  • 动态条件下,删除、更新、新增,都需要保证幂等性。

解决思路

不难看出来,幂等性和线程安全中加锁的情形,某种程度上是等价的问题:

  • 幂等性要求某次请求中,无论因为重试而执行了多少次方法,都需要保证:即使执行了若干次,都和方法执行了1次的结果相同。
  • 线程安全中,我们需要保证某块代码中,同一时刻只能有一个线程在执行上锁的线程,其部分目的是:维护临界区的数据变更是线程希望的结果。