CAS和MySql乐观锁实现下单

CAS和MySql乐观锁实现下单

准备

建表t_order:

CREATE TABLE `t_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `version` int(255) DEFAULT NULL,
  `stock` int(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

初始化数据:

INSERT INTO `spboot`.`t_order` (`id`, `version`, `stock`) VALUES ('1', '1', '35');

mysql> select * from t_order;
+----+---------+-------+
| id | version | stock |
+----+---------+-------+
|  1 |       1 |    35 |
+----+---------+-------+

OrderDo.java:

public class OrderDo {
    private Integer id;

    private Integer version;

    private Integer stock;

    //ommited getter & setter
}

OrderDoMapper.java:

public interface OrderDoMapper {

    OrderDo selectByPrimaryKey(Integer id);

    int desStockByCas(@Param("orderId") int orderId, 
    			@Param("oldStock") int oldStock, @Param("desStock") int desStock);

    int desStockByOptimistic(@Param("orderId") int orderId, @Param("oldVersion") int oldVersion,
                                    @Param("desStock") int desStock);

    int desStockByLarge(@Param("orderId") int orderId,@Param("desStock") int desStock);

}

OrderManager.java:

@Component(value = "orderManager")
public class OrderManager {


    @Autowired
    private OrderDoMapper orderDoMapper;

    public OrderDoMapper getDao(){
        return this.orderDoMapper;
    }


    public int desStockByCas(int orderId, int oldStock, int desStock){
        return orderDoMapper.desStockByCas(orderId,oldStock, desStock);
    }


    public int desStockByOptimistic(int orderId, int oldVersion, int desStock){
        return orderDoMapper.desStockByOptimistic(orderId, oldVersion, desStock);
    }


    public int desStockByLarge(int orderId, int desStock){
        return orderDoMapper.desStockByLarge(orderId, desStock);
    }

}

模拟业务OrderService.java:

@Service
public class OrderService {

    private static final Logger LOGGER = LoggerFactory.getLogger(OrderService.class);

    @Autowired
    private OrderManager orderManager;

    @Transactional
    public void downOrder(){

        int orderId = 1;
        int desStock = 1;
        OrderDo orderDo = orderManager.getDao().selectByPrimaryKey(orderId);
        if (orderDo.getStock() <=0 ){
            LOGGER.info(Thread.currentThread().getName()+" :无库存.....");
            return;
        }

          //CAS
//        int result = orderManager.desStockByCas(orderId, orderDo.getStock(), desStock);
          //乐观锁
//        int result = orderManager.desStockByOptimistic(orderId, orderDo.getVersion(), desStock);
//      
//      //大于
        int result = orderManager.desStockByLarge(orderId, desStock);
        if (result > 0){
            LOGGER.info(Thread.currentThread().getName()+ " 下单成功...");
        }else {
            LOGGER.info(Thread.currentThread().getName()+ " 下单失败...");
        }

    }
}

测试案例

用线程OrderRunnable.java模拟去发起下单操作:

public class OrderRunnable implements Runnable {

    private CountDownLatch latch;
    private OrderService orderService;

    public OrderRunnable(CountDownLatch latch, OrderService orderService) {
        this.latch = latch;
        this.orderService = orderService;
    }

    @Override
    public void run() {
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        orderService.downOrder();
    }
}

测试案例,用50个线程模拟50个用户去下单:

@Test
public void downOrderTest() throws InterruptedException {

	CountDownLatch latch = new CountDownLatch(1);
	for (int i=0; i < 50; i++){
		Thread t = new Thread(new OrderRunable(latch, orderService));
		t.start();
	}
	latch.countDown();
	Thread.sleep(10000);
}

CAS方式

orderDoMapper.xml:

<update id="desStockByCas">
     UPDATE t_order SET stock=stock-#{desStock} WHERE id=#{orderId} AND stock=#{oldStock}
</update>

有地方说扣减库存不是幂等的,需要改成设值的方式,之后的方式也一样,如:

# newStock = oldStock-desStock;
<update id="desStockByCas">
     UPDATE t_order SET stock=#{newStock} WHERE id=#{orderId} AND stock=#{oldStock}
</update>

打开注解:

int result = orderManager.desStockByCas(orderId, orderDo.getStock(), desStock);

测试结果:

2018-11-26 21:30:33 [Thread-3] INFO  com.xxx.service.OrderService -Thread-3 下单成功...
2018-11-26 21:30:33 [Thread-7] INFO  com.xxx.service.OrderService -Thread-7 下单失败...
2018-11-26 21:30:33 [Thread-36] INFO  com.xxx.service.OrderService -Thread-36 下单失败...
2018-11-26 21:30:33 [Thread-5] INFO  com.xxx.service.OrderService -Thread-5 下单失败...
2018-11-26 21:30:33 [Thread-14] INFO  com.xxx.service.OrderService -Thread-14 下单失败...
2018-11-26 21:30:33 [Thread-9] INFO  com.xxx.service.OrderService -Thread-9 下单失败...
2018-11-26 21:30:33 [Thread-43] INFO  com.xxx.service.OrderService -Thread-43 下单失败...
2018-11-26 21:30:33 [Thread-29] INFO  com.xxx.service.OrderService -Thread-29 下单成功...
2018-11-26 21:30:33 [Thread-42] INFO  com.xxx.service.OrderService -Thread-42 下单失败...
2018-11-26 21:30:33 [Thread-34] INFO  com.xxx.service.OrderService -Thread-34 下单成功...
2018-11-26 21:30:33 [Thread-21] INFO  com.xxx.service.OrderService -Thread-21 下单失败...
2018-11-26 21:30:33 [Thread-39] INFO  com.xxx.service.OrderService -Thread-39 下单成功...
2018-11-26 21:30:33 [Thread-22] INFO  com.xxx.service.OrderService -Thread-22 下单失败...
2018-11-26 21:30:33 [Thread-8] INFO  com.xxx.service.OrderService -Thread-8 下单成功...
2018-11-26 21:30:33 [Thread-20] INFO  com.xxx.service.OrderService -Thread-20 下单失败...
// ommitted some...

查看数据库:

mysql> select * from t_order;
+----+---------+-------+
| id | version | stock |
+----+---------+-------+
|  1 |       1 |    20 |
+----+---------+-------+

可以发现,本来35个库存,现在变成了20个,也就是说成功了15个

MySql乐观锁方式

MySql乐观锁是基于CAS原理的一种实现.

<update id="desStockByOptimistic">
    UPDATE t_order SET stock=stock-#{desStock}, version=version+1 
    		WHERE id=#{orderId} AND version=#{oldVersion}
</update>

打开注解:

int result = orderManager.desStockByOptimistic(orderId, orderDo.getVersion(), desStock);

测试结果:

2018-11-26 21:34:26 [Thread-20] INFO  com.xxx.service.OrderService -Thread-20 下单成功...
2018-11-26 21:34:26 [Thread-25] INFO  com.xxx.service.OrderService -Thread-25 下单失败...
2018-11-26 21:34:26 [Thread-11] INFO  com.xxx.service.OrderService -Thread-11 下单失败...
2018-11-26 21:34:26 [Thread-34] INFO  com.xxx.service.OrderService -Thread-34 下单失败...
2018-11-26 21:34:26 [Thread-26] INFO  com.xxx.service.OrderService -Thread-26 下单失败...
2018-11-26 21:34:26 [Thread-24] INFO  com.xxx.service.OrderService -Thread-24 下单失败...
2018-11-26 21:34:26 [Thread-40] INFO  com.xxx.service.OrderService -Thread-40 下单失败...
2018-11-26 21:34:26 [Thread-14] INFO  com.xxx.service.OrderService -Thread-14 下单成功...
2018-11-26 21:34:26 [Thread-22] INFO  com.xxx.service.OrderService -Thread-22 下单失败...
2018-11-26 21:34:26 [Thread-21] INFO  com.xxx.service.OrderService -Thread-21 下单成功...
2018-11-26 21:34:26 [Thread-49] INFO  com.xxx.service.OrderService -Thread-49 下单失败...
2018-11-26 21:34:26 [Thread-4] INFO  com.xxx.service.OrderService -Thread-4 下单成功...
2018-11-26 21:34:26 [Thread-7] INFO  com.xxx.service.OrderService -Thread-7 下单失败...

查看数据库:

mysql> select * from t_order;
+----+---------+-------+
| id | version | stock |
+----+---------+-------+
|  1 |      19 |    17 |
+----+---------+-------+
1 row in set

可以发现,本来35个库存,现在变成了17个,也就是说成功了18个

大于方式

<update id="desStockByLarge">
   UPDATE t_order SET stock=stock-#{desStock} WHERE id=#{orderId} and stock-#{desStock}>=0
</update>

打开注解:

int result = orderManager.desStockByLarge(orderId, desStock);

测试结果:

2018-11-26 21:37:32 [Thread-3] INFO  com.xxx.service.OrderService -Thread-3 下单成功...
2018-11-26 21:37:32 [Thread-5] INFO  com.xxx.service.OrderService -Thread-5 下单成功...
2018-11-26 21:37:32 [Thread-47] INFO  com.xxx.service.OrderService -Thread-47 下单成功...
2018-11-26 21:37:32 [Thread-23] INFO  com.xxx.service.OrderService -Thread-23 下单成功...
2018-11-26 21:37:32 [Thread-16] INFO  com.xxx.service.OrderService -Thread-16 下单成功...
2018-11-26 21:37:32 [Thread-45] INFO  com.xxx.service.OrderService -Thread-45 下单成功...
2018-11-26 21:37:32 [Thread-52] INFO  com.xxx.service.OrderService -Thread-52 下单成功...
2018-11-26 21:37:32 [Thread-49] INFO  com.xxx.service.OrderService -Thread-49 下单失败...
2018-11-26 21:37:32 [Thread-11] INFO  com.xxx.service.OrderService -Thread-11 下单失败...
2018-11-26 21:37:32 [Thread-31] INFO  com.xxx.service.OrderService -Thread-31 下单失败...
2018-11-26 21:37:32 [Thread-6] INFO  com.xxx.service.OrderService -Thread-6 :无库存.....
2018-11-26 21:37:32 [Thread-44] INFO  com.xxx.service.OrderService -Thread-44 :无库存.....
2018-11-26 21:37:32 [Thread-30] INFO  com.xxx.service.OrderService -Thread-30 :无库存.....
2018-11-26 21:37:32 [Thread-33] INFO  com.xxx.service.OrderService -Thread-33 :无库存.....

查看数据库:

mysql> select * from t_order;
+----+---------+-------+
| id | version | stock |
+----+---------+-------+
|  1 |       1 |     0 |
+----+---------+-------+
1 row in set

可以发现,本来35个库存,现在变成了0个,也就是说全部成功

mysql悲观锁方式

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值