redis中分布式鎖的應用

我們之前講了秒殺模塊的實現,使用了sychronized互斥鎖,但是在集群模式下因為不同服務器有不同jvm,所以synchronized互斥鎖失效了。

redis實現秒殺超賣問題的解決方案:(僅限于單體項目)-CSDN博客

這時就要找到一個多臺服務器都能識別的鎖,即redis中的setNX充當互斥鎖,來控制秒殺的一人一單

在redis緩存擊穿中,使用邏輯過期就用過互斥鎖,這里原理一摸一樣,只不過這里存儲的value為UUID+線程ID

setNX互斥鎖的使用:

場景1:(會導致一個用戶創建多個訂單)

注*線程1和線程2的userID相同,所以創建的redis鎖key值相同,但是value不相同,釋放鎖時如果不進行驗證value值,很有可能會出現場景1的情況。

場景2:

注*在線程1檢查鎖后,發現自己的鎖過期了,該鎖不是自己創建的,說明其他相同userID的線程也在創建訂單,這時應該回滾,撤銷之前數據庫操作。(在調用減庫存創建訂單的方法中回滾)

代碼實現:

鎖工具:

創建后不能注冊為Bean,用的時候new對象即可,如果想注冊為Bean使用keywords和value都要作為參數傳遞,否者會出現多線程隨意修改該值的情況

public class RedisLock {StringRedisTemplate template;String keywords;String value;public RedisLock(StringRedisTemplate template,String keywords){this.keywords=keywords;this.template=template;}//嘗試創建鎖public boolean tryLock(Integer timeOutSecond){//給該線程生成唯一標識,作為valuevalue=UUID.randomUUID().toString().replace("-","")+"-"+Thread.currentThread().getId();return template.opsForValue().setIfAbsent("lock:"+keywords,value,timeOutSecond, TimeUnit.SECONDS);}//嘗試刪除鎖public void delLovk(){//釋放鎖之前先驗證鎖是否過期,是為自己的鎖String result = template.opsForValue().get("lock:" + keywords);if(result!=null || result.equals(value)){template.delete("lock:"+keywords);}}
}

之所以不將該類注冊為bean使用,是因為創建鎖時,要獲取UUID+線程的ID,刪除鎖時也需要該值,所以這個值只能使用一個全局變量來記錄。

如果注冊為bean后,所有線程的操作都使用該對象中value屬性去進行賦值和刪除操作,就會導致value被不斷修改,keywords也會被不斷修改,最終導致程序邏輯錯誤,應該一個線程使用一個獨有的value屬性,所以不能將該工具類注冊為Bean,用的時候new即可

業務邏輯代碼:有原來的synchronized改為分布式鎖控制線程創建訂單

@AutowiredApplicationContext context;//模仿秒殺減庫存,創建訂單@Overridepublic Boolean killInSecond(Integer userID,Integer productID){//檢查庫存是否>0Product product = pm.selectByPrimaryKey(productID);if(product.getSales()<=0){throw new MyExceptionHandler("庫存不足");}//調用2-4步驟方法Boolean result=false;//同步鎖
//        synchronized (userID.toString().intern()){
//            //使用代理對象調用事務方法
//            ProductServiceImpl bean = context.getBean(ProductServiceImpl.class);
//            result=bean.ProductAndOrder(userID,productID);
//        }//分布式鎖RedisLock redisLock = new RedisLock(template, "order:" + userID);//獲取鎖result=redisLock.tryLock(30);if(!result){throw new RuntimeException("該用戶只能創建一個訂單");}//使用代理對象調用事務方法ProductServiceImpl bean = context.getBean(ProductServiceImpl.class);result=bean.ProductAndOrder(userID,productID);//釋放鎖redisLock.delLovk();return result;}

減庫存創建訂單方法:

 @AutowiredOrderMapper om;@AutowiredRedisIdIncrement redisId;//創建訂單,減庫存操作@Transactionalpublic Boolean ProductAndOrder(Integer userID,Integer productID){//檢查數據庫中書否存在該用戶訂單Integer orderCount = om.selectOrderByUserIdAndProductId(userID, productID);if(orderCount>0){throw new MyExceptionHandler("用戶已下單");}//訂單不存在減庫存,寬松樂觀鎖Integer result = pm.updateProductBysale(productID);if(result!=1){throw new MyExceptionHandler("庫存不足");}//創建訂單//獲取redis唯一IDLong orderId = redisId.getRedisID("order");//封裝訂單Order order=new Order(orderId.toString(),userID,"","",productID,"",null,1,0,null,null,null,null,new BigDecimal(100));result = om.insertCompleteOrder(order);if(result!=1){return false;}return true;}

不足:

雖然我們通過檢查鎖的value值判斷該鎖是否為本線程創建的鎖,控制了誤刪鎖的可能,但是這里依然會沒有解決多個相同userID的線程,會創建多個訂單的情況。

情況一:

在線程1檢查鎖后,發現自己的鎖過期了,該鎖不是自己創建的,說明其他相同userID的線程也在創建訂單,這時應該回滾,撤銷之前數據庫操作。(在調用減庫存創建訂單的方法中回滾)

情況2:

刪除鎖時發現自己的鎖過期了,緩存中沒有該鎖,說明

1.沒有其他相同userID用戶執行創建訂單的邏輯,不需回滾直接結束程序即可

2.有其他線程執行了操作,但是已經執行完畢,訂單也已經創建完畢,繼續執行程序即可,因為創建訂單時發現訂單已存在,自會回滾

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918784.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918784.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918784.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【科研繪圖系列】R語言繪制微生物豐度和基因表達值的相關性網絡圖

文章目錄 介紹 加載R包 數據下載 導入數據 數據預處理 畫圖 系統信息 參考 介紹 【科研繪圖系列】R語言繪制微生物豐度和基因表達值的相關性網絡圖 加載R包 library(tidyverse) library(ggsignif) library(RColorBrewer) library(dplyr) library(reshape2) library(grid

Pycharm現有conda環境有對應env,但是添加后沒反應

一、系統環境 二、異常現象 Pycharm現有conda環境有對應env&#xff1a; anaconda3的envs下也確實存在這個環境&#xff1a; 但是添加后沒反應&#xff08;點擊確認后&#xff0c;yolov7環境沒有出現在列表中&#xff09;&#xff1a; 但是我之前在別的機子添加是沒問題的。 …

Git常用指令大全:從入門到精通

Git 的常用指令&#xff0c;分為基礎操作、分支管理、遠程協作、撤銷操作和高級功能五個部分&#xff0c;并附上實用示例&#xff1a;一、基礎操作&#xff08;必會&#xff09;初始化倉庫 git init # 在當前目錄創建新倉庫克隆遠程倉庫 git clone https://github.com/user/rep…

Redis (REmote DIctionary Server) 高性能數據庫

Redis {REmote DIctionary Server} 高性能數據庫1. What is Redis?1.1. 基于內存的數據存儲2. Install Redis on Linux3. Starting and stopping Redis in the background3.1. systemctl3.2. service 4. Connect to Redis5. 退出 Redis 的命令行界面 (redis-cli)6. redis-serv…

MySQL中的DML(二)

DML(Data Manipulation Language) : 數據庫操作語言&#xff0c;對數據庫中表的數據進行增刪改操作。 創建student表&#xff1a; CREATE DATABASE test; use test; CREATE TABLE student (id int,name varchar(255),address varchar(255),city varchar(255) );INSERT INTO stu…

linux 主機驅動(SPI)與外設驅動分離的設計思想

一、 主機驅動與外設驅動分離Linux中的SPI、I2c、USB等子系統都利用了典型的把主機驅動和外設驅動分離的想法&#xff0c;讓主機端負責產生總線上的傳輸波形&#xff0c;而外設端只是通過標準的API來讓主機端以適當的波形訪問自身。因此這里涉及了4個軟件模塊&#xff1…

如何生成.patch?

文章目錄 ??方法 1:使用 `git format-patch`(推薦)? ??步驟?? ?方法 2:使用 `diff`命令(適用于非 Git 項目)? ??方法 3:使用 `git diff`(生成未提交的變更)? ?方法 4:使用 `quilt`(適用于大量補丁管理) ?如何提交補丁給上游項目?? ?總結?? 在 L…

【計算機網絡 | 第6篇】計算機體系結構與參考模型

文章目錄計算機體系結構與參考模型分層思想&#x1f342;常見的3種模型&#xff08;網絡體系結構&#xff09;&#x1f426;?&#x1f525;TCP/IP體系結構各層包含的主要協議&#x1f95d;每層所解決的主要問題&#x1f914;層次間的交互規則&#x1f95d;實體與對等實體協議服…

Autoware Universe 感知模塊詳解 | 第一節 感性認識多源傳感器標定

傳感器與感知模塊 在基于規則的自動駕駛系統中&#xff0c;感知模塊&#xff0c;承擔著理解車體周圍環境信息的重要職責。它通過融合多種傳感器數據&#xff0c;與定位模塊共同為規劃與控制模塊提供準確、系統化的輸入信息。正如人可以通過眼睛觀察周圍的環境&#xff08;盲人也…

docker搭建java運行環境(java或者springboot)

目錄1. 創建測試代碼2. 編譯打包3. 代碼環境運行使用普通運行方式使用docker掛載項目&#xff08;長期運行&#xff09;1. 創建 Dockerfile2. 構建并后臺運行使用docker swram實現零停機更新&#xff08;推薦&#xff09;1. 初始化swarm2. 創建 Dockerfile3. 使用Dockerfile 構…

哈希表特性與unordered_map/unordered_set實現分析

目錄 一、哈希表核心特性總結 1.開放地址法 2.鏈地址法 二、unordered_map/unordered_set實現要點分析 1. 哈希表核心實現(HashTable2.h) (1) 哈希函數處理 (2) 鏈地址法實現 (3) 迭代器設計 (4) hashtable設計 2. unordered_map實現要點 3. unordered_map實現要點 一…

生產環境sudo配置詳細指南

目錄 1. 語法格式 2. 配置示例 3. 使用 /etc/sudoers.d/ 目錄管理&#xff08;推薦&#xff09; 4. 基礎配置&#xff1a;用戶權限管理 4.1 ??添加用戶到sudo組 ??4.2 驗證用戶組信息 5. sudo日志配置 5.1 修改sudoers配置文件 5.2 創建日志目錄與權限設置 6. Su…

CSS動態視口單位:徹底解決移動端適配頑疾,告別布局跳動

你是否曾被這些問題困擾&#xff1a; 移動端頁面滾動時&#xff0c;地址欄收縮導致頁面高度突變&#xff0c;元素錯位&#xff1f;100vh在移動設備上實際高度超出可視區域&#xff1f;全屏彈窗底部總被瀏覽器UI遮擋&#xff1f; 這些痛點背后都是傳統視口單位的局限——無法響應…

【P27 4-8】OpenCV Python——Mat類、深拷貝(clone、copyTo、copy)、淺拷貝,原理講解與示例代碼

P27 4-8 1 Mat結構體2 深拷貝VS淺拷貝3 代碼示例1 Mat結構體 2 深拷貝VS淺拷貝 只拷貝了頭部&#xff0c;header&#xff0c;&#xff0c;但是data部分是共用的&#xff0c;速度非常快&#xff1b; 缺點&#xff0c;任意一個修改&#xff0c;另一個data跟著變&#xff0c;這就是…

容器運行時支持GPU,并使用1panel安裝ollama

前言 安裝Docker請看之前博文&#xff1a;Docker實戰中1panel方式安裝Docker。 安裝 NVIDIA 容器工具包 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html 安裝 先決條件 閱讀有關平臺支持的部分。為您的 Linux 發行版安裝…

高并發內存池 性能瓶頸分析與基數樹優化(9)

文章目錄前言一、性能瓶頸分析操作步驟及其環境配置分析性能瓶頸二、基數樹優化單層基數樹二層基數樹三層基數樹三、使用基數樹來優化代碼總結前言 到了最后一篇嘍&#xff0c;嘻嘻&#xff01; ??終于是要告一段落了&#xff0c;接下來我們將學什么呢&#xff0c;再說吧&…

C#面試題及詳細答案120道(01-10)-- 基礎語法與數據類型

《前后端面試題》專欄集合了前后端各個知識模塊的面試題&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

機器翻譯:回譯與低資源優化詳解

文章目錄一、機器翻譯的瓶頸二、回譯&#xff08;Back-Translation&#xff09;2.1 什么是回譯&#xff1f;2.2 為什么回譯有效&#xff1f;2.3 回譯的缺點與挑戰三、低資源優化詳解3.1 數據層面策略3.2 模型層面策略3.3 架構層面策略四、回譯與低資源優化對比4.1 回譯與低資源…

leetcode-python-344反轉字符串

題目&#xff1a; 編寫一個函數&#xff0c;其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 s 的形式給出。 不要給另外的數組分配額外的空間&#xff0c;你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 示例 1&#xff1a; 輸入&#xff1a;s [“h”,“…

【Python】新手入門:什么是python字符編碼?python標識符?什么是pyhon保留字?

?? 個人主頁:(時光煮雨) ?? 高質量專欄:vulnhub靶機滲透測試 ?? 希望得到您的訂閱和支持~ ?? 創作高質量博文(平均質量分95+),分享更多關于網絡安全、Python領域的優質內容!(希望得到您的關注~) ??文章目錄?? 前言 ??一、編碼 ??二、標識符 ??三、Py…