spring boot:用redis+redisson實現分布式鎖(redisson3.11.1/spring boot 2.2)


一,為什么要使用分布式鎖? 

如果在並發時鎖定代碼的執行,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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM