什么是幂等性? |
幂等性就是当用户对于同一操作发起一次或多次请求时,得到的结果都是一样的。比如购物下单时,即使用户可能由于手机卡顿等原因点了好几次“提交订单”,那也只能扣一次费和生成一个订单。
什么情况下需要做幂等性处理? |
读取和删除,操作一次和多次的结果都是一样的,所以不用考虑幂等处理。下面两种情况需要考虑:
(1)insert,多次请求时可能会产生重复数据
(2)update,分情况,如果像 update order set status=2 where id=1
这种,不用考虑。像update order set status=status+1 where id=1
就需要考虑了。
实现幂等性的方式 |
下面列举一下在分布式情景下实现幂等性的几种方式。
1、给数据库中的某个字段设置唯一索引
比如订单表,给订单id设置唯一索引,如果库中已经有此订单了,就会insert失败。
使用时需要注意:这个接口可能会报重复键异常,所以我们应做个捕获异常的处理。如果捕获到DuplicateKeyException和MySQLIntegrityConstraintViolationException异常后,依旧返回请求成功。
2、给数据库的某两个字段设置联合唯一索引
这条是1的延伸。有些库不是物理删除,而是逻辑删除,还有一个is_delete字段(是否删除:0否、1是)。
那就可以将订单id和is_delete字段设置个联合唯一索引。
3、新建个防重表
不搞订单表,而是新建个防重表,表里有id和订单id两个字段,然后给orderId建个唯一索引。
使用时需要注意:防重表需要和订单表在同一个数据库且逻辑代码需要在同一个事务中。
4、行锁+事务——悲观锁
使用for update,for update的作用就是对这条数据加行锁,等本事务执行完才会释放锁。在此期间,其它事务可以任意读,如果想修改的话得排队。
第一步:
select * from order where id=1 for update;
第二步:
处理业务代码,对这条数据进行修改。两步需要在同一个事务里。
使用时需要注意:
(1)需要选择mysql默认的InnoDB搜索引擎,因为MyIsam不支持事务。
(2)查询条件要带主键,要不然会锁表
5、新增版本号字段——乐观锁
在数据库里加个version字段,如果想修改数据,先select再update,当update时判断条件中需要有version
select id,amount,version from order_id=1;
update user set amount=amount+100,version=version+1 where id=123 and version=1;
6、根据状态字段来判断
比如订单表的订单状态分为几种,status:0下单未支付、1已支付、2订单完成、3订单取消。update的判断条件加个status:
update order set status=1 where order_id=1 and status=0
7、分布式锁
可以使用redis或zk做分布式锁,其中redis分布锁的实现方式有3种:
(1)setNx
(2)set
(3)Redission