一,為什么要使用分布式鎖?
如果在並發時鎖定代碼的執行,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)