无处不在的幂等f(f(x)) = f(x)

旧梦发癫

幂等的基本概念

可能我们经常在看网上文章的时候会看到幂等这个概念,那么幂等到底是什么呢?

幂等:在编码中谈的幂等一般对应数学概念中的一元幂等:某一元运算为幂等的时,其作用在任一元素两次后会和其作用一次的结果相同f(f(x)) = f(x)。既对于同一个输入不管进行多少次函数运算都应该都应该得到相同的结果

不堪回首的往事

在一次偶然的bug的发现了幂等这个概念的重要性。实际业务是这样的。

做一个订单销售额的统计:每次有订单付款成功的时候会通知一个统计接口,统计接口中则累计当天的订单总金额。

大概代码如下:

type Order struct {
    Id int
    Price int
    Sn string
    Date time.Time
}
func settle(info Order)  {
    //get day price
    dayPrice := 20
    price := info.Price
    dayPrice += price
    //save to databases
    return
}

上面的代码看起来是没问题的。但是当相同的订单重复调用这个接口的时候就会出现订单金额重复计算的问题。最后就得到一个优化的方案。

type Order struct {
    Id int
    Price int
    Sn string
    Date time.Time
    SettleStatus int
}

func settle(info Order)  {
    //check order settle status
    if info.SettleStatus !=1 {
        return
    }
    //get day price
    dayPrice := 20
    price := info.Price
    dayPrice += price
    //save to databases

    //update order status
    info.SettleStatus = 2
    //save to databases

    return
}

在统计金额修改之前去判断当前订单是否被统计过了,这样就避免了重复统计。

这是一个最简单的幂等思想的应用。

10块钱的麻辣烫

以前在网上看到过这么一个面试题。

这里有3个人共同拥有10块钱,在他们同时使用这十块钱买东西的时候,怎么保证不被超用。
  • 常见的解决方案就是加锁,每次使用扣费之前都去获取一个锁,抢到锁的人优先使用这十块钱。
  • 如果用幂等的思想的话。我们可以在使用之前先获取到当前的余额,在最后扣费的时候判断当前的余额是否是开始获取的那个余额。
func pay()  {
    //查询当前可用金额 select money from table
    money := 3
    //买一只笔花了2块钱
    money = 1

    //update databases
    //修改数据库的时候判断条件是否为开始获取的金额,如果不是,那么消费失败,或者进入重试流程
    // update table set money=1 where money = 3

}

MQ中的重复通知

在MQ通知中我们经常会遇到消息会重复通知的问题。这个时候为了保证数据的准确性,我们也经常会用到幂等的思想。
MQ重复消息处理通常有两种方式。
  • 在消息中加入一个消息唯一标识,在消费的时候判断消息是否被消费过了,如果已经被消费,那么直接确认消费成功,如果没有被消费,那么执行消费的逻辑,然后将消息ID入库,用于下次判断。
  • 在消费端处理业务逻辑的时候使用幂等的处理方式,保证不管来多少次最后处理的结果都是一样的。
这些简单的例子都可以说明幂等的在编码中的重要性,其实还有很多地方都在使用幂等的思想。写这个东西并不是教大家怎么去使用幂等,而是突然觉得我们在编码的时候尽量多思考,也许只是一个很小的改动就能拯救你的代码,多思考,多利用一些成熟的方式来,保证代码的质量,提高自己的思维严谨性。

欢迎一起交流

file

阅读 1.2k

逸梦
一个PHP程序猿的文章栏目

PHP、Golang

678 声望
76 粉丝
0 条评论

PHP、Golang

678 声望
76 粉丝
文章目录
宣传栏