关于幂等性的总结 幂等性 幂等性是数学概念,即f(x)=f(f(x))。在计算机领域,则是意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。在调用接口时,总有一些特殊情况会导致接口进行重复的调用,如果不对这些情况做出处理,就可能导致脏数据,甚至是业务流程上的问题。 比如,后台管理员在新 ..

关于幂等性的总结

关于幂等性的总结

幂等性

幂等性是数学概念,即f(x)=f(f(x))。在计算机领域,则是意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的。在调用接口时,总有一些特殊情况会导致接口进行重复的调用,如果不对这些情况做出处理,就可能导致脏数据,甚至是业务流程上的问题。

比如,后台管理员在新增数据时,由于特殊原因,比如按钮抖动,而导致目标数据插入两条;用户下单时,没有做逻辑校验,导致用户下了多笔相同的订单。这些情况都是不被允许的,我们要保证这些操作无论执行多少次,最终产生的结果都是相同的,这类业务通常需要拥有较强的一致性。幂等性就是描述这一接口特性的名词。

幂等性的保证需要从两个方面下手:

关于永久性的幂等性校验:

并发安全性问题

幂等问题出现的场景:

解决方案

select + insert

解决永久性幂等问题的一种解决方案。就是在执行操作前,判断之前是否已经操作过了,比如下单之前检查是否具有相同的订单 ID 已经存在,这样就可以避免重复提交的问题。永久性幂等问题通常与业务逻辑具有强关联,所以校验通常放置在参数校验上。

if (!order exists) {
    addOrder();
} else {
    throw OrderExistsException();
}

针对并发问题带来的脏数据的问题无法解决,需要通过下面几种方式来实现。

唯一索引

这是最容易想到的方式。页面的数据通常只能被提交一次,多次提交可能会产生脏数据。比如,同一名称的商品只能被创建一次,为了防止创建多次,可以给商品名称添加唯一索引。当在添加一个已有名称的商品时,数据库插入操作就会因为唯一索引而引发异常,避免了脏数据的产生。类似的案例还有博客点赞,订单创建等场景。

唯一索引不仅可以解决并发下的脏数据问题,也可以解决永久性幂等的问题。

缺点: 无法适用分布式存储系统,需要维护数据库的唯一索引,多的情况下不容易管理

分布式锁

如果是分布式系统,全局的唯一索引就很难构建,此时可以使用分布式锁的方式解决此类问题。我们可以在执行第操作时先获取分布式锁,做完操作后,再将分布式锁释放。这样可以解决高并发性下的幂等性问题。

@Controller
String addOrder(order){
    redisLock.lock();
    addOrder(order);
    redisLock.unLock();
}

需要配合 select+insert 使用,如果不配合,无法解决表单重复提交的问题。

token 机制

token 机制是一种比较常用的机制,核心原理在于给每个操作执行前,需要去服务中获取一个 token,执行操作时需要携带 token 进行操作。如果发现 token 存在,则使用 token,并将其置为已使用,并执行操作,执行完毕后并且会将 token 对应的执行结果存储起来。否则将会检查是否存在执行结果,直接取出。

比如下单操作需要一次进行添加订单、更改库存、更改优惠券三个操作。每个操作执行前都去使用 token 验证该操作是否已经执行,从而防止重复执行的问题。并且,缓存的结果也可以用于事务控制(如果下单失败,增加记录的库存和优惠券)。

Token getToken() {
    return tokenPool.getToken();
}
addOrder(order, token) {
    if (tokenPool.removeToken(token) == 1) { // 说明池中有token,token是有效的
        addOrder()
    } else {
        throw Exception
    }
}

// 客户端调用 
token = ajax.get("/token");
result = ajax.post("/addOrder", token order)

注:这种方式也有缺点,使用 token 机制,那么就会意味着在需要保证幂等性的接口在被调用前,必须先调用接口获取 token

MVCC 机制

MVCC(Multi-Version Concurrency Control) 多版本并发控制。在数据更新时需要去比较持有数据的版本号,版本号不一致的操作无法进行更新,更新成功后版本号将会发生变化。

updateStock(int num, int version) {
    if (version != currentVersion) {
        throw Exception;
    } else {
        stockDao.minStock();
    }
}

注意,只适用于更新接口

状态机幂等

所谓状态机,就是任务或者业务在执行的过程中,拥有的状态以及状态的变更图。在执行某个操作前,需要先对当前状态进行验证,如果状态不是该有的状态,则拒绝操作。

String pay() {
    if (order.status == "待支付") {
    	doPay();
	}
}

我们也可以将请求放入到缓冲区中,并去除不符合状态的请求。

参考资料

  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    67 引用 • 342 回帖 • 1 关注
  • 幂等性
    2 引用
  • 总结
    99 引用 • 906 回帖 • 1 关注
回帖
请输入回帖内容...