一,为什么要使用分布式锁?
如果在并发时锁定代码的执行,java中用synchronized锁保证了线程的原子性和可见性
但java锁只在单机上有效,如果是多台服务器上的并发访问,则需要使用分布式锁,
例如:两台机器上同时各有一个进程查询同一件商品的库存,此时商品库存数为1,
数据库给两台机器返回的都是1,
然后这两台机器同时下单,两个订单就超出了商品的库存数,
所以此时要使用分布式锁
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,使用redisson
redisson是对redis用java语言的封装
1,redisson的官网:
https://redisson.org/
2,redisson的官方文档:
https://github.com/redisson/redisson/wiki
3,使用redisson做分布式锁和mysql悲观锁(for update)的区别:
本质上没有区别,
但redis性能更强
三,演示项目的相关信息
1,地址
https://github.com/liuhongdi/distributedlock
2,原理:
在减库存之前,先加锁,
在减库存完成后,解锁
这样避免高并发时查询到相同的库存数而导致超卖情况
3,结构
4,数据表:
CREATE TABLE `goods` ( `goods_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id', `goods_name` varchar(500) NOT NULL DEFAULT '' COMMENT 'name', `stock` int(11) NOT NULL DEFAULT '0' COMMENT 'stock', PRIMARY KEY (`goods_id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表'
四,java代码的说明
OrderServiceImpl.java
@Service public class OrderServiceImpl implements OrderService { @Resource private RedissonClient redissonClient; @Resource private GoodsMapper goodsMapper; /* * 加锁减库存 * */ @Override public boolean decrementProductStoreLock(int goodsId, int buyNum) { String key = "dec_store_lock_" + goodsId; //生成锁对象 RLock lock = redissonClient.getLock(key); try { //2, TimeUnit.MINUTES lock.lock(2, TimeUnit.MINUTES); boolean upRes = updateGoodsStock(goodsId, buyNum); if (upRes == false) { return false; } } catch (Exception e) { System.out.println(e.getMessage()); return false; } finally { //解锁 if (lock.isHeldByCurrentThread()){ System.out.println("----------------release lock"); lock.unlock(); } } return true; } /* * 减库存 * */ @Transactional(isolation = Isolation.REPEATABLE_READ) public boolean updateGoodsStock(int goodsId, int buyNum) { Goods goodsOne = goodsMapper.selectOneGoods(goodsId); System.out.println("-------------------------当前库存:"+goodsOne.getStock()+"-------购买数量:"+buyNum); if (goodsOne.getStock() < buyNum || goodsOne.getStock() <= 0) { System.out.println("------------------------fail:buy fail,return"); return false; } int upStock = goodsOne.getStock()-buyNum; goodsOne.setStock(upStock); int upNum = goodsMapper.updateOneGoodsStock(goodsOne); System.out.println("-------------------------success:成交订单数量:"+upNum); return true; } /* * 不加锁减库存 * */ @Override public boolean decrementProductStoreNoLock(int goodsId, int buyNum) { return updateGoodsStock(goodsId, buyNum); } }
java代码说明:
String key = "dec_store_lock_" + goodsId: 加锁时加入商品id,这样不影响其他商品
RLock是redisson中定义的可重入锁
lock.lock(2, TimeUnit.MINUTES) 加锁,时长两分钟
lock.unlock(); 解锁
五,测试高并发时加锁的效果:
1,数据库的数据:
mysql> update goods set stock=3; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from goods; +----------+------------+-------+ | goods_id | goods_name | stock | +----------+------------+-------+ | 3 | green cup2 | 3 | +----------+------------+-------+ 1 row in set (0.00 sec)
我们设置商品的库存数为3
2,不加锁的情况
#-c:请求并发数
#-n:请求总数
[root@localhost ~]# ab -c 20 -n 20 http://127.0.0.1:8080/lock/buynolock
查看代码的打印输出:
-------------------------当前库存:3-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:2-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return
共成交5单,超出了库存数量
3,加锁的情况:
#-c:请求并发数
#-n:请求总数
[root@localhost ~]# ab -c 20 -n 20 http://127.0.0.1:8080/lock/buylock
查看代码的打印输出:
-------------------------当前库存:3-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:2-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:1-------购买数量:1 -------------------------success:成交订单数量:1 -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return -------------------------当前库存:0-------购买数量:1 ------------------------fail:buy fail,return
只成功了3个订单,说明分布式锁有效
六,查看spring boot的版本:
. ____ _ __ _ _ /\ / ___'_ __ _ _(_)_ __ __ _ ( ( )\___ | '_ | '_| | '_ / _` | \/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.0.RELEASE)