欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis并發(fā)問題解決方案

 更新時間:2023年11月23日 15:24:35   作者:困知勉行1985  
在當前的互聯(lián)網(wǎng)環(huán)境中,高并發(fā)業(yè)務場景十分常見,本文就來介紹一下Redis并發(fā)問題解決方案,具有一定的參考價值,感興趣的可以了解一下

前言

在多個客戶端并發(fā)訪問Redis的時候,雖然Redis是單線程執(zhí)行指令,但是由于客戶端指令達到Redis的時序無法保證,所以可能出現(xiàn)如下的情況,導致并發(fā)問題。

2個客戶端都執(zhí)行 get, set指令,期望將key的值設置為3,結(jié)果因為并發(fā)問題,導致結(jié)果為2
client1 get x => 1
client2 get x => 1
client1 set x => 2
client2 set x => 2

本文介紹 Redis 并發(fā)方面的解決方案。

Redis 的單個命令是原子的,但是一個業(yè)務操作可能包含多條命令,比如以下場景:客戶端查詢值,并遞增,在高并發(fā)場景下就可能出現(xiàn)并發(fā)問題,導致數(shù)據(jù)不一致。

為了保證并發(fā)訪問的正確性,Redis 提供了三種方法,原子操作、分布式鎖、事務。

1.分布式鎖

與分布式鎖相對的是本地鎖,假如只有一個服務實例,就可以直接在該單應用本地使用鎖變量來控制多個客戶端的訪問。

如果使用的是多實例的分布式系統(tǒng),就需要使用分布式鎖,即將鎖保存在一個第三方的共享存儲系統(tǒng)中,可以被多個客戶端共享訪問和獲取。通常將一個 Redis 實例作為分布式鎖的存儲系統(tǒng)。

實現(xiàn)分布式鎖的關鍵在于:

  • 保證每個加鎖、釋放鎖操作都是原子的;
  • 保證共享存儲系統(tǒng)的可靠性,即鎖的可靠性;
分布式鎖相較于 Lua 腳本,更簡單易用,但是可能存在死鎖問題。分布式鎖的性能不如 Lua 腳本。

1.基于單個節(jié)點

Redis 提供了 SETNX 命令(在 SET 命令后加上 NX 選項也能達到同樣的效果),保證了加鎖操作的原子性。

同時,為了避免客戶端加鎖后不釋放,應該給鎖變量設置過期時間(set NX EX),且在過期釋放鎖時,判斷業(yè)務代碼是否執(zhí)行完成,如果未完成則給鎖續(xù)期。如果多次續(xù)期后,業(yè)務仍然未完成,再釋放鎖。(仍然存在風險)

為了區(qū)分不同客戶端的操作,應該將鎖變量設置為隨機值或唯一值,在釋放鎖時進行驗證。

釋放鎖的邏輯包含了讀取鎖變量、判斷值、刪除鎖變量的多個操作,所以應該使用 Lua 腳本來保證互斥執(zhí)行。

單個節(jié)點可以實現(xiàn)分布式鎖的功能,但是無法保證可靠性。

2.基于多個節(jié)點

為了避免 Redis 實例故障而導致的鎖無法工作的問題,Redis 的開發(fā)者 Antirez 提出了分布式鎖算法 Redlock。

Redlock 算法的基本思路,是讓客戶端和多個獨立的 Redis 實例依次請求加鎖,如果客戶端能夠和半數(shù)以上的實例成功地完成加鎖操作,就認為客戶端成功地獲得分布式鎖了,否則加鎖失敗。這樣一來,即使有單個實例發(fā)生故障,因為鎖變量在其它實例上也有保存,所以客戶端仍然可以正常地進行鎖操作,鎖變量并不會丟失。

加鎖過程:

  • 客戶端獲取當前時間;
  • 客戶端按順序依次向 N 個 Redis 實例執(zhí)行加鎖操作。同樣使用 SETNX 命令,并設置超時時間。如果請求加鎖一直超時,則視為加鎖失敗,向下一個實例執(zhí)行加鎖操作。
  • 客戶端完成所有加鎖操作后,計算整個加鎖過程的總耗時。

客戶端只有在滿足下面的這兩個條件時,才能認為是加鎖成功:

  • 從超過半數(shù)(大于等于 N/2+1)的實例上成功獲取到了鎖;
  • 獲取鎖的總耗時沒有超過鎖的有效時間。

在滿足了這兩個條件后,還需要重新計算這把鎖的有效時間,計算的結(jié)果是鎖的最初有效時間減去客戶端為獲取鎖的總耗時。如果鎖的有效時間已經(jīng)來不及完成共享數(shù)據(jù)的操作了,可以釋放鎖,以免出現(xiàn)還沒完成數(shù)據(jù)操作,鎖就過期了的情況。
如果沒能同時滿足這兩個條件,則視為加鎖失敗,執(zhí)行釋放鎖的過程:客戶端會向所有節(jié)點發(fā)起釋放鎖的操作,執(zhí)行釋放鎖的 Lua 腳本。

判斷是否加鎖時,需要查詢所有節(jié)點,以半數(shù)以上節(jié)點的鎖狀態(tài)來判斷整個分布式鎖的狀態(tài)。在釋放鎖之前,需要先判斷分布式鎖的狀態(tài)。

為了避免 Redis 節(jié)點發(fā)生崩潰重啟后造成鎖丟失,從而影響鎖的安全性,antirez 還提出了延時重啟的概念,即一個節(jié)點崩潰后不要立即重啟,而是等待一段時間后再進行重啟,這段時間應該大于鎖的有效時間。優(yōu)點是保證了鎖不會被多個客戶端獲?。蝗秉c是延長了重啟時間,可能對系統(tǒng)造成影響。

性能和一致性是沖突的,如果為了分布式鎖的高可用性,可以開啟持久化,但是會有額外的性能開銷,需要根據(jù)實際場景進行選擇。

3.watch(樂觀鎖) 

watch通常跟redis事務配合使用,watch某個key在操作過程中有沒有被其他指令改變,進而做出相應的處理。底層利用了CAS操作,后面講Redis事務會講到。

2.原子操作

為了實現(xiàn)并發(fā)控制要求的臨界區(qū)代碼互斥執(zhí)行,Redis 的原子操作采用了兩種方法:單命令操作和 Lua 腳本。

1.單命令操作

Redis 的每個操作都是原子性的。

Redis 是使用單線程來串行處理客戶端的請求操作命令的,所以,當 Redis 執(zhí)行某個命令操作時,其他命令是無法執(zhí)行的,這相當于單個操作是原子的。雖然 Redis 的單個操作是原子的,但是通常修改數(shù)據(jù)是包含多個操作的,至少包括讀數(shù)據(jù)、修改數(shù)據(jù)、寫回數(shù)據(jù)這三個操作,此時仍然可能出現(xiàn)并發(fā)問題。

針對常用的修改數(shù)據(jù)場景,Redis 提供了 INCR/DECR 命令,可以對數(shù)據(jù)進行簡單的遞增/遞減操作,它們本身就是單個命令操作,在執(zhí)行時,具有互斥性。但是如果要執(zhí)行更復雜的操作,Redis 的單命令操作就無法保證互斥執(zhí)行了。

2.Lua 腳本(多命令操作)

Redis 可以將多個操作寫在 Lua 腳本中,然后把整個 Lua 腳本作為一個整體執(zhí)行,在執(zhí)行的過程中不會被其他命令打斷,從而保證了 Lua 腳本中操作的原子性。

為什么是 Lua 腳本,而不是其他語言的腳本?
Lua 是一種高效的輕量級 腳本語言,用標準 C 語言編寫并以源代碼形式開放。其設計目的就是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定制功能。

Lua 腳本可以在服務器端執(zhí)行,不需要將數(shù)據(jù)傳輸?shù)娇蛻舳嗽龠M行處理,可以減少網(wǎng)絡傳輸?shù)拈_銷,因此性能較高。

使用 Lua 腳本不僅可以實現(xiàn)將多個操作原子執(zhí)行,還能夠復用 Lua 腳本。但是使用 Lua 腳本需要額外的語言學習成本,還有調(diào)試困難、可讀性較差的問題。

如果把很多操作都放在 Lua 腳本中原子執(zhí)行,會導致 Redis 執(zhí)行腳本的時間增加,同樣也會降低 Redis 的并發(fā)性能。所以,在編寫 Lua 腳本時,要避免把不需要做并發(fā)控制的操作寫入腳本中。

在 Lua 腳本執(zhí)行過程中崩潰怎么辦?
Redis 會在內(nèi)部維護一個已經(jīng)加載腳本的 哈希表,記錄了每個腳本的 SHA1 值和對應的 Lua 腳本代碼。當 Redis 服務器重啟時,Redis 會自動重新加載這個哈希表中記錄的所有腳本,再重新執(zhí)行,此時可能導致部分修改被應用。所以 Lua 腳本并不能嚴格保證原子性。如果對數(shù)據(jù)一致性非常嚴格,可以使用 Lua 腳本+事務 WATCH 的辦法。

3.事務

Redis 事務的本質(zhì)是一組命令的集合。事務支持一次執(zhí)行多個命令,一個事務中所有命令都會被序列化。在事務執(zhí)行過程,會按照順序串行化執(zhí)行隊列中的命令,其他客戶端提交的命令請求不會插入到事務執(zhí)行命令序列中。

Redis 提供了實現(xiàn)事務的幾個命令:

  • MULTI :開啟事務,redis 會將后續(xù)的命令逐個放入隊列中,然后使用 EXEC 命令來原子化執(zhí)行這個命令系列。
  • EXEC:執(zhí)行事務中的所有操作命令。
  • DISCARD:取消事務,放棄執(zhí)行事務塊中的所有命令。
  • WATCH:在開啟事務之前監(jiān)視一個或多個 key,如果事務在執(zhí)行前,這個 key (或多個 key)被其他命令修改,則事務被中斷,不會執(zhí)行事務中的任何命令(一般需要在 EXEC 執(zhí)行失敗后重新執(zhí)行整個函數(shù))。

UNWATCH:取消 WATCH 對所有 key 的監(jiān)視。

為什么 WATCH 是中斷事務,而不是阻塞其他進程?這樣不會導致并發(fā)量高的時候,被 WATCH 的事務一直得不到執(zhí)行嗎?
這種機制稱為 樂觀鎖,因為在大多數(shù)情況下,碰撞的概率很小,所以選用了更容易實現(xiàn)的方式(且影響不大)。

在使用事務時,可以配合 Pipeline 使用:一次性將所有命令打包好,再全部發(fā)送到服務端。
相比于事務的入隊,同樣是一次性執(zhí)行,這樣不僅能減少網(wǎng)絡 IO,還能保證在開啟 WATCH 時不會被其他操作打斷。

1.執(zhí)行步驟

  • 開啟事務:使用 MULTI 命令開啟事務;
  • 入隊:接收到命令后并不會立即執(zhí)行,而是放到等待執(zhí)行的事務隊列里;
  • 執(zhí)行:由 EXEC 命令觸發(fā)事務執(zhí)行。

當客戶端切換到事務狀態(tài)之后, 服務器會根據(jù)這個客戶端發(fā)來的不同命令執(zhí)行不同的操作:

  • 如果客戶端發(fā)送的命令為 EXEC 、DISCARD、WATCH、MULTI 四個命令的其中一個, 那么服務器立即執(zhí)行這個命令;
  • 如果是其他命令, 那么服務器并不立即執(zhí)行命令, 而是將這個命令放入一個事務隊列里面, 然后向客戶端返回 QUEUED 回復;

2.錯誤處理

在事務執(zhí)行過程中可能遇到兩種不同類型的錯誤,會有不同的應對方案:

  • 編譯器錯誤:命令在編譯時出錯,會導致整個事務提交失敗,即所有命令執(zhí)行不成功;
  • 運行時錯誤:命令在運行時檢測到錯誤,最終會導致事務提交失敗,但是事務并不會回滾,而是跳過錯誤命令繼續(xù)執(zhí)行并保留結(jié)果;
為什么 Redis 不支持 事務回滾?
Redis 命令只會因為錯誤的語法而失敗(并且這些問題不能在入隊時發(fā)現(xiàn)),或是命令用在了錯誤類型的鍵上面:這也就是說,從實用性的角度來說,失敗的命令是由編程錯誤造成的,而這些錯誤應該在開發(fā)的過程中被發(fā)現(xiàn),而不應該出現(xiàn)在生產(chǎn)環(huán)境中。
不需要對回滾進行支持,所以 Redis 的內(nèi)部可以保持簡單且快速。

3.崩潰處理

Redis 在執(zhí)行事務時會使用一個單獨的內(nèi)存空間來保存事務中的所有修改操作,只有當事務成功提交時,這些修改操作才會被應用到 Redis 中。因此,如果事務被中止,所有的修改操作也都會被撤銷,從而保證了數(shù)據(jù)的一致性。

如果開啟了 AOF 持久化,會先將事務中的所有命令寫入 AOF 緩沖區(qū),然后執(zhí)行事務中的命令,再將 AOF 緩沖區(qū)中的數(shù)據(jù)寫入到 AOF 文件。

如果在寫入 AOF 文件前崩潰,則持久化失敗,相當于事務沒有發(fā)生,不會出現(xiàn)數(shù)據(jù)不一致。

另外,RDB 快照不會在事務執(zhí)行途中進行。

總結(jié)

本文介紹了 Redis 應對并發(fā)問題的三種方案,Redis 中的單條命令都是原子操作,而且還有 INCR/DECR 來應對簡單的場景。對于復雜的場景,Redis 可以使用 Lua 腳本、分布式鎖、事務來實現(xiàn)操作的原子性。Lua 腳本是將一系列操作放在一個腳本中原子執(zhí)行。分布式鎖是通過共享的鎖變量來限制客戶端的并發(fā)訪問。事務是將一系列操作放到執(zhí)行隊列中,再按順序原子執(zhí)行。

到此這篇關于Redis并發(fā)問題解決方案的文章就介紹到這了,更多相關Redis并發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性

    如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性

    這篇文章主要介紹了如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性,文中舉了兩個場景例子介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • Redis的BitMap使用操作命令

    Redis的BitMap使用操作命令

    Redis 為我們提供了位圖這一數(shù)據(jù)結(jié)構(gòu),每個用戶每天的登錄記錄只占據(jù)一位,365天就是365位,僅僅需要46字節(jié)就可存儲,極大地節(jié)約了存儲空間,這篇文章主要介紹了Redis的BitMap使用操作命令,需要的朋友可以參考下
    2023-10-10
  • Linux下redis的安裝與使用圖文教程

    Linux下redis的安裝與使用圖文教程

    這篇文章主要介紹了Linux下redis的安裝與使用,結(jié)合圖文形式分析了Linux環(huán)境下redis的下載、編譯、安裝、部署、訪問等相關操作技巧,需要的朋友可以參考下
    2019-08-08
  • Redis序列化轉(zhuǎn)換類型報錯的解決

    Redis序列化轉(zhuǎn)換類型報錯的解決

    本文主要介紹了Redis序列化轉(zhuǎn)換類型報錯的解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04
  • redis-cli常用命令使用詳解

    redis-cli常用命令使用詳解

    這篇文章主要介紹了redis-cli常用命令使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • Redis發(fā)布訂閱和實現(xiàn).NET客戶端詳解

    Redis發(fā)布訂閱和實現(xiàn).NET客戶端詳解

    發(fā)布訂閱在應用級其作用是為了減少依賴關系,通常也叫觀察者模式。主要是把耦合點單獨抽離出來作為第三方,隔離易變化的發(fā)送方和接收方。下面這篇文章主要給大家介紹了關于Redis發(fā)布訂閱和實現(xiàn).NET客戶端的相關資料,需要的朋友可以參考下
    2017-03-03
  • Redis過期刪除策略與內(nèi)存淘汰策略

    Redis過期刪除策略與內(nèi)存淘汰策略

    這篇文章主要介紹了Redis過期刪除策略與內(nèi)存淘汰策略,文章圍繞主題展開詳細的內(nèi)容戒殺,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-09-09
  • redis++的編譯?安裝?使用方案

    redis++的編譯?安裝?使用方案

    這篇文章主要介紹了redis++的編譯?安裝?使用方案的相關資料,需要的朋友可以參考下
    2023-03-03
  • Redis設置密碼的實現(xiàn)步驟

    Redis設置密碼的實現(xiàn)步驟

    本文主要介紹了Redis設置密碼的實現(xiàn)步驟,主要包括兩種方法:臨時密碼和持久密碼,具有一定的參考價值,感興趣的可以了解一下
    2023-08-08
  • Window下對Redis進行開啟與關閉的操作方法

    Window下對Redis進行開啟與關閉的操作方法

    這篇文章主要介紹了Window下對Redis進行開啟與關閉的操作方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-11-11

最新評論