幂等性

1. 幂等性是什么?

在一个系统中,一个接口,运行多次和运行一次的结果是一致的。

什么情况下需要幂等性?

重复提交、接口重试、前端窗口抖动等

业务场景:

  1. 用户多次点击提交订单,后台应该只生成一个订单。
  2. 支付时,由于发生网络问题重试,应该只扣一次钱。

保证幂等性的核心思想:通过唯一的业务单号保证幂等。

实现:

  • 非并发的情况下,查询业务单号有没有操作过,没有则执行操作
  • 并发的情况下,整个过程加锁

2 具体CRUD操作的幂等性

1. select操作的幂等性

查询操作天然幂等。

2 delete操作的幂等性

  • 有唯一的业务号,如id。
    • 第一次操作:根据唯一业务号删除
    • 第二次操作:由于找不到记录,所以直接返回。可在删除前进行数据的查询。
  • 没有唯一的业务号,比如将审核未通过的商品删除。
    • 第一次操作:将所有审核未通过的商品删除
    • 第二次操作前:又产生了新的审核未通过的商品。这时候需要根据业务需求去处理,新增的审核未通过的商品是否需要删除。

3 update操作的幂等性

update操作需要区分场景:

  • 比如是set去更新,那么多次set不会有什么问题

  • 如果是在当前基础上自增,那么就需要处理 ,分场景:

    • 有唯一的业务号,如id。更新操作传入数据版本号,通过乐观锁实现。

      • 数据库增加版本version字段,用户在查询并展示当前数据时,将version放入隐藏域。当用户点击提交时,将版本号也一同提交。

      • 后台去判断版本号,并且进行版本号+1操作

        1
        update user set xxx=${xxx}, version=${version}+1 where id=xxx and version=${version}
    • 没有唯一的业务号,比如用户登录,注册

      • 采用token机制

4 insert操作的幂等性

  • 有唯一的业务号,如秒杀场景,商品ID+用户ID。

    • 解决方式:分布式锁业务执行完成后,不让锁释放,而是让其过期后释放。比如设置30s,则30s内只会有一个线程获取到锁,当用户点击多次,触发了其他线程,获取不到锁,直接返回。
  • 没有唯一业务单号,使用token保证幂等。

    • 具体流程

      • 当用户进入注册页面时,后端生成一个随机token,客户端将token放在隐藏域下,带着token提交。(分布式场景下,需要类似分布式ID的处理方式,保证token唯一)

      • 根据token去获取分布式锁,完成insert后,不让锁释放,而是让其过期后释放

        1
        2
        3
        4
        5
        6
        7
        8
        public int insertUser(SysUser user, String token) throws Exception {
        InterProcessMutex lock = new InterProcessMutex(zkClient, "/"+token);
        val isLock = lock.acquire(30, TimeUnit.SECONDS);
        if(isLock){
        return sysUserMapper.insert(user);
        }
        return 0;
        }

5 混合操作的幂等性

找到操作的唯一业务单号,有则使用分布式锁,无则通过token保证


幂等性
http://example.com/幂等性/
作者
Panyurou
发布于
2023年4月23日
许可协议