1.防重表的问题
对于防止数据重复提交,还有一种解决方案就是通过防重表实现。防重表的实现思路也非常简单。首先创建一张表作为防重表,同时在该表中建立一个或多个字段的唯一索引作为防重字段,用于保证并发情况下,数据只有一条。在向业务表中插入数据之前先向防重表插入,如果插入失败则表示是重复数据
对于防重表的解决方案,可能有人会说为什么不使用悲观锁。悲观锁在使用的过程中也是会发生死锁的。悲观锁是通过锁表的方式实现的。 假设现在一个用户A访问表A(锁住了表A),然后试图访问表B; 另一个用户B访问表B(锁住了表B),然后试图访问表A。 这时对于用户A来说,由于表B已经被用户B锁住了,所以用户A必须等到用户B释放表B才能访问。 同时对于用户B来说,由于表A已经被用户A锁住了,所以用户B必须等到用户A释放表A才能访问。此时死锁就已经产生了
2.解决方案(select+insert防重提交)
并发量并不高的情况下,对于幂等的实现非常简单,通过select+insert思想即可完成幂等控制。在业务执行前,先判断是否已经操作过,如果没有则执行,否则判断为重复操作
3.实现思路
在并发下访问时,因为是基于id进行判断,那id值就必须要保证在多次提交时,需要唯一。访问流程如下
代码如下:
@Override
@Transactional(rollbackFor = Exception.class)
public String addOrder(Order order) {
order.setCreateTime(new Date());
order.setUpdateTime(new Date());
//查询
Order orderResult = orderMapper.selectByPrimaryKey(order.getId());
Optional<Order> orderOptional = Optional.ofNullable(orderResult);
if (orderOptional.isPresent()) {
return "数据存在";
}
int result = orderMapper.insert(order);
if (result != 1) {
return "失败";
}
return "成功";
}
对于上面的方案,在并发下,并不能完成幂等性控制。通过jemeter测试,模拟50个并发,可以发现,插入了重复数据。产生了脏数据。要解决这个问题,非常简单,在数据库层面添加唯一索引即可,将id设置为唯一索引,也是最容易想到的方式,一旦id出现重复,就会出现异常,避免了脏数据的发生也可以解决永久性幂等。但该方案无法用于分库分表情况,其只适用于单表情况