新聞中心
redis如何防止并發(fā)?
Redis是當前炙手可熱的NoSQL數(shù)據(jù)庫,幾乎已經(jīng)成為高并發(fā)、高可用系統(tǒng)的標配了。對Redis響應(yīng)快的認知不能僅僅停留在基于內(nèi)存和單線程的層面。

創(chuàng)新互聯(lián)公司是專業(yè)的固始網(wǎng)站建設(shè)公司,固始接單;提供網(wǎng)站設(shè)計、做網(wǎng)站,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行固始網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
在一些對高并發(fā)請求有限制的系統(tǒng)或者功能里,比如說秒殺活動,或者一些網(wǎng)站返回的當前用戶過多,請稍后嘗試。這些都是通過對同一時刻請求數(shù)量進行了限制,一般用作對后臺系統(tǒng)的保護,防止系統(tǒng)因為過大的流量沖擊而崩潰。對于系統(tǒng)崩潰帶來的后果,顯然還是拒絕一部分請求更能被維護者所接受。
而在各種限流中,除了系統(tǒng)自身設(shè)計的帶鎖機制的計數(shù)器外,利用Redis實現(xiàn)顯然是一種既高效安全又便捷方便的方式。
客戶端加鎖(ReentrantLock或synchronized)
但此方式只限于單機加鎖,無法解決分布式系統(tǒng)的并發(fā)競爭問題。
樂觀鎖(redis 的命令 watch)
當執(zhí)行多鍵值事務(wù)操作時,Redis 不僅要求這些鍵值需要落在同一個 Redis 實例上,還要求落在同一個 slot 上,所以 redis 的事務(wù)比較雞肋,不過可以想辦法遵循 redis 內(nèi)部的分片算法把設(shè)計到的所有 key 分到同一個 slot。
redis 的 setnx 實現(xiàn)分布式鎖
要設(shè)置超時時間,防止搶占到鎖的客戶端因失敗、崩潰或其他原因沒有辦法釋放鎖而造成死鎖。
如有不同觀點,歡迎發(fā)表評論。如果喜歡我的回答,歡迎“點贊、分享”。
1. 問題描述
并發(fā)競爭key這個問題簡單講就是:
同時有多個客戶端去set一個key。
示例場景 1
例如有多個請求一起去對某個商品減庫存,通常操作流程是:
假設(shè)當前庫存值為 20,現(xiàn)在有2個連接都要減 5,結(jié)果庫存值應(yīng)該是 10 才對,但存在下面這種情況:
示例場景 2
比如有3個請求有序的修改某個key,按正常順序的話,數(shù)據(jù)版本應(yīng)該是 1->2->3,最后應(yīng)該是 3。
但如果第二個請求由于網(wǎng)絡(luò)原因遲到了,數(shù)據(jù)版本就變?yōu)榱?1->3->2,最后值為 2,出問題了。
redis 本身是單進程、單線程的模型,就是說一個時刻就只能有一個東東在執(zhí)行,不管是多少個命令,只能是串行執(zhí)行,因此從這個意義上保證了單個命令執(zhí)行的多線程(多個客戶端操作)的安全,也就是不管有多少個客戶端在發(fā)請求,redis每次只能執(zhí)行一個客戶端的命令,不存在多線程。
但是redis對于多個客戶端的多個命令,并不能保證其線程安全性,比如有一個值x=1;如果ClientA 獲取x的值,x=x+1,然后再設(shè)置回去;在此期間,有一個ClientB做同樣的操作;如果ClientA、ClientB的操作被串行了,那么x=3;但是多個命令之間不能保證(除非是增加了所謂的鎖之類的東東),從而x的值就不一定是3了,這個時候就存在了并發(fā)操作的問題。
當然redis也考慮到了相關(guān)的情況,提供了incr之類的原子操作命令,保證了多線程并發(fā)操作的安全性;
對于同一個客戶的多次點擊操作,如果不做區(qū)分,可能就存在問題,比如支付寶的種樹澆水這個操作,如果不做控制,一個用戶快速的多次點擊,可能就會超過3次(支付寶限制一天只能幫某個好友澆水3次),這個時候其實有簡單的解決方法:比如每次澆水有一個澆水ID,第1、2、3次都有一個不同的ID,從第4次開始的操作其ID還是設(shè)置為3(由客戶端來限制),那么后臺只要判斷ID是否重復,就可以做過濾; 同樣的問題在第三方API對接的時候也存在(比如調(diào)用支付寶付款,有時候網(wǎng)絡(luò)不好,是否會存在多次付費問題?),此時我想每個支付的請求也帶了一個唯一的ID,保證了支付的唯一性(當然可能還會有對用戶名、支付款項的驗證--銀行支付的時候往往會提醒你,你支付了一筆同樣的款項,需要確認--具體的場景是你需要支付某人1000塊,你分成500、500兩筆,此時網(wǎng)銀操作是會提醒你的);
因此對于相同的2個請求,如果是一種冪等的操作--比如都是get某個值(不會變的),那么其實處不處理看策略,至少不會造成不一致;對于支付這樣的行為,肯定需要做判斷確定是同一個請求,對另外一個請求做過濾。
對于很多系統(tǒng)而言,都有集群處理。在集群中使用quartz或者task處理任務(wù)的時候,一般有三種選擇:
1. 多實例順序執(zhí)行
2. 單實例執(zhí)行
3. 多實例并行執(zhí)行
創(chuàng)保目前考慮的是1情況。
redis對于并發(fā)較小的情況下,進行鎖控制,效果是較為理想的。目前創(chuàng)保只有八臺實例,適用于該方案。
1. 首先,redis提供了一個setnx方法,該方法執(zhí)行后會返回key值在設(shè)置之前是否存在。
這是進行并發(fā)控制的基礎(chǔ),但并不夠。
2. 在利用 setnx 進行 key 值設(shè)定后,如果instance發(fā)生異常(不僅僅是exception)會導致該鎖無法正常釋放。
所以,我們需要為鎖設(shè)置一個失效時間,即expire命令。
Redis分布式鎖的原理是什么?如何續(xù)期?
在傳統(tǒng)單體應(yīng)用單機部署的情況下,并發(fā)問題可以通過使用Java并發(fā)相關(guān)的鎖如synchronized,但是當規(guī)模上升到分布式集群的情況下,要控制共享資源訪問,就需要通過分布式鎖來實現(xiàn)。常見的分布式鎖方案如數(shù)據(jù)庫樂觀鎖,Redis鎖,zk鎖等。
Redis分布式鎖可以有多種方式實現(xiàn)但是其核心就是通過以下三個Redis命令組合實現(xiàn)。
上面為Redis的一個最簡單的鎖實現(xiàn)原理,實際中還需要考慮更多具體的情況作出相應(yīng)的調(diào)整。如
實際開發(fā)環(huán)境中不確定的因素有很多,需要慢慢地去調(diào)整實踐達到理想狀態(tài),可以考慮使用redisson框架來實現(xiàn)。
這個情況比較獨特,出現(xiàn)這個問題的根本原因在于鎖失效的時間小于業(yè)務(wù)處理的時間導致業(yè)務(wù)還沒處理完畢鎖就釋放了。那么解決方案是合理地結(jié)合業(yè)務(wù)去設(shè)置鎖失效的時間。
但是也有更好的方案就如前文提到的redisson,其中的可重入鎖概念。
默認情況下,加鎖的時間是30秒.如果加鎖的業(yè)務(wù)沒有執(zhí)行完,那么到 30-10 = 20秒的時候,就會進行一次續(xù)期,把鎖重置成30秒。
分布式鎖的需求產(chǎn)生
分布式鎖的需求是伴隨著應(yīng)用分布式部署而來的,在單體應(yīng)用,且只部署一臺服務(wù)器的情況下,通過java的同步鎖即可實現(xiàn)。同步鎖,即是一個原子性的操作。
那么當應(yīng)用進行了分布式部署,應(yīng)用有多個服務(wù),這個時候應(yīng)用服務(wù)端就沒有一個可提供原子性操作的地方了,Redis性能高,且是單線程,因此可提供一個原子性操作的地方,利用它,就可以實現(xiàn)分布式鎖。
用場景說話,使用Redis分布式鎖的場景如下圖所示:
如下圖所示,隨后會根據(jù)場景說明分布式鎖及續(xù)期相關(guān)問題的來龍去脈。
- 圖中序號1:進來一個請求,這個請求要求我們保存一個“訂單A”;
- 圖中序號2:2.1 步,請求進來,首先去嘗試設(shè)置一個Redis 值,他的鍵就是訂單號“訂單A”,如果嘗試成功,則代表我這個線程是第一次設(shè)置,相當于我拿到了這個鎖;如果嘗試失敗,那么,可以拋出異?;蛘叩却欢螘r候后再次重試,這里可以根據(jù)業(yè)務(wù)場景的不同采取不同的策略。這里的關(guān)鍵是在Redis中的操作是單線程的,因此該操作是原子性的。2.2步,為了防止應(yīng)用服務(wù)意外中斷,Redis中的數(shù)據(jù)一直存活,消耗資源,需要設(shè)置一個超時時間。(如果為了嚴謹,可以將2.1, 2.2 兩步封裝成一個lua腳本部署在Redis服務(wù)器上)
- 圖中序號3:情況A,這個時候是當Redis的key還未失效,程序就已經(jīng)執(zhí)行完成,且刪除了Redis中的數(shù)據(jù),一切正常;情況B:就是需要續(xù)期的場景,如果要避免這個場景的出現(xiàn),可以將Redis key的失效時間設(shè)置長一點,可以應(yīng)對大多數(shù)業(yè)務(wù)。如果要徹底解決,可以在應(yīng)用端添加一個Redis鎖的注冊中心,然后起一個監(jiān)聽線程去監(jiān)聽這個注冊中心,發(fā)現(xiàn)有鎖還在被持有,但是Redis 已經(jīng)快過期了,則修改相應(yīng)key的失效時間,進行續(xù)期。
到此,以上就是小編對于redis 并發(fā)鎖的問題就介紹到這了,希望這2點解答對大家有用。
新聞標題:Redis分布式鎖的原理是什么?如何續(xù)期?
本文地址:http://m.fisionsoft.com.cn/article/dhijjcj.html


咨詢
建站咨詢
