幂等性设计

什么是幂等设计?

        在编程场景指的是:使用相同参数来调用同一接口,调用多次的结果跟单次产生的结果是一致的。

        企业级项目的一些关键接口都需要幂等设计,比如支付扣款、发货等等。

        设想以下,因为网络问题我们调用扣款接口超时了,并且没有进行重试,这样有可能给用户发货了,但是实际没扣款。因此这种情况下通常要重试扣款。但是如果重试了,假设之前超时的那次调用实际是成功了,只是响应结果的时候接口超时了,这样不是重复扣两次款了?

        如果你是那个用户,买一个东西,平台竟然扣了两次钱,你肯定会抓狂并且投诉。

        那如果我们是平台会怎么样?一直被用户投诉,渐渐的流失用户直到倒闭。

        所以幂等设计在一些必须要保证业务一致性的情况下,非常关键,因为这种场景往往需要重试,重试就需要幂等。

        当然,还有一些情况是用户误触的,比如多次点击按钮导致多次提交等。

        需要注意,虽然前端可以通过将按钮置为灰防止重复点击,但是纯前端无法完美实现幂等性!比如前端调用后端接口超时,有可能后端已经存储了数据,此时前端的按钮已经可点击,用户再次点击会生成两条数据。

实现幂等性的方案选型

1、数据库唯一索引

利用数据库唯一索引的一致性保证幂等性。

比如将数据库订单表中的订单号字段配置成唯一索引,用户生成订单会执行insert语句,MySQL根据唯一所以天然阻止相同订单号数据的插入,我们可以catch住报错,让接口正常返回插入成功结果。

try{
    insertOrder({id:xxx});
}catch(DuplicateKeyException e){
    return true;
}

 对应的订单插入语句为:

insert into order(id,order,xx,updateTime) values(1,2,3,"2024-07-24 15:55:18")
    on dupicate key update updateTime = now();

这样同一笔订单,不论调用几次,结果都不会新增重复的订单记录。 

2、数据库乐观锁

利用乐观锁在某些场景下也能实现幂等性。

比如需要对一个配置进行修改,同时记录修改的时间、旧配置、新配置、操作人等日志信息到操作记录表中,方便后面追溯。

// update sys_config set config = "a" where id = 1;
updateConfig();

// insert opreation_log(createTime, oldConfig, newConfig, userId)
//        value("2024-05-28 15:55:18","b","a",1L)
addOpreationLog();

这个场景就很适合采用乐观锁来实现幂等。

乐观锁并不是真的加锁,而是可以给配置表加一个version版本号字段,每次修改需要验证版本号是否等于修改前的(没被别人同时修改),然后才能给版本号加1。

如果配置表修改成功(通过影响行数来判断1表示成功,0表示失败,才能添加操作日志。

因此,进行如下改造:

// update sys_config set config = "a" where id = 1;
int updateEffect = updateConfig();

// insert opreation_log(createTime, oldConfig, newConfig, userId)
//        value("2024-05-28 15:55:18","b","a",1L)
if(updateEffect == 1){
    addOpreationLog();
}

3、天然幂等操作

比如一些delete操作,这种是天然幂等的,因为删除一次和多次都是一样的。

还有一些更新操作,例如:

update sys_config set conifg = "a" where id = 1;

这样的SQL不论执行几遍,结果都是一样的。

如果接口里面仅包含上述的这些天然幂等的行为,那么对外就可以标记当前接口为幂等接口,不需要任何其他操作。

4、分布式锁

导致数据错乱的元凶很多时候都是”并发修改“。

很多时候的业务场景是这样的:

1、查找数据

2、if(不包含这个数据){

        3、插入这条数据

}

在没有并发的情况下,这样的逻辑没任何问题,但是一旦出现并发,就会导致数据不一致的情况。

因为同时可能出现多个线程在同一时刻到达第2步的判断,这时候其实数据都没有插入,因此它们都能通过这个判断到达第3步,这就导致重复插入一样的数据。

针对这种场景,可以上一把分布式锁,杜绝了并发问题。

分布式锁{

        1、查找数据

        2、if(不包含这个数据){

                3、插入这条数据

        }

}

多个线程需要先抢占锁才能进行后续的业务操作,因此1、2、3这三个步骤在同一时刻仅会有一个线程执行,所以不会存在数据不一致的情况。

也因此加了锁之后,这个业务代码可以进行多次调用,因为除了第一次的调用,后续通过if判断,都不会插入数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值