单线程

四季春风,不厌冬

  1. 应用层
    1. 全局唯一ID
  2. 数据库层
    1. 唯一约束
    2. 悲观锁
    3. 乐观锁
    4. 状态机
  3. 参考

  HTTP的幂等性指的是一次和多次请求某一个资源应该具有相同的副作用。

  接口幂等可以在应用层数据库层进行控制,   

应用层

全局唯一ID

  后端根据操作和内容生成唯一ID,可以存储至数据库、redis、jvm内存。后端每接收一次请求,便判断该请求是否存在,如果存在,则说明是同一次请求正在进行中,反之,给唯一ID加分布式锁,保证同一时刻只能有同一请求执行成功,执行完成后释放锁。   

数据库层

  如果应用层没有做幂等控制,那么可以由数据库做兜底方案。   

唯一约束

  若表中存在业务型的唯一标识,可以把它作为唯一索引。若重复插入一条数据,程序会抛异常,此时说明该记录已经存在。利用唯一索引可以防止脏数据。
  
  如果涉及到的去重的地方特别多,每一种业务单据都需要去重,可以建一张去重表,把业务表的唯一标识作为去重表的唯一索引。新增记录时候,把写入业务表和写入去重表,放在一个事务中,如果重复创建,数据库会抛出唯一约束异常,操作就会回滚。   

悲观锁

  获取数据时加上排他锁 select for updatewhere条件必须是主键或者唯一索引,否则可能发生表锁。悲观锁方案不利于数据库的并发,很容易使数据库成为高并发场景下的瓶颈。

乐观锁

  针对悲观锁而言,乐观锁效率更高,因为它只在更新数据的一刹那加行锁。乐观锁的实现方式有多种:version或者其他状态条件。

  • 版本控制
      每条记录都一个version字段,用来记录该记录的更新次数,每更新一次加一:   

    1
    update table set value=${newValue}, version = version + 1 where version < ${newVersion};
  • 条件控制
      比如更新商品库存,库存不能小于0:   

    1
    update goods_stock set stock_num = stock_num - ${updateNum} where id=${id} and stock_num >= ${updateNum};

状态机

  使用状态机的前提是:业务的状态是有限的,并且状态不能逆转。状态通常可以用数字表示:1,2,3……那么更新记录为:   

1
update table set status=${newStatus} where id=${id} and status < ${status};

参考

本文作者 : pengqin.zhou
本文使用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议
本文链接 : https://www.zhoupq.com/blog/%E6%8E%A5%E5%8F%A3%E5%B9%82%E7%AD%89%E7%AD%96%E7%95%A5/

本文最后更新于 天前,文中所描述的信息可能已发生改变