上一篇介紹了Redis基本的使用場景,今天介紹在高并發的場景中,常用到的一種技術手段,那就是鎖機制。
Redis分布式鎖跟其他鎖一樣,Redis的鎖根據鎖機制的不同分為樂觀鎖和悲觀鎖,經常被用于防止出現雪崩效應。下面分别先簡單介紹一下。
樂觀鎖通常基于版本記錄機制實現,不是我們今天要詳細介紹的,我們拿個例子簡單說明一下,假定有A和B兩個事務同時執行:
第一步:A讀取數據,記錄數據當前版本号n(相信沒有人會動我将要改的數據,真樂觀是不是,哈哈);
第二步:B修改數據,此時版本号變為n 1;
第三步:A要改數據了,看了下版本号,跟剛才對不上啊(事務失敗了);
過程說明:
B事務
A事務
結論:當如果監視的key在watch後發生過變化,則返回失敗。
這就是樂觀鎖,适合于讀多寫少并的場景。
悲觀鎖這個很好理解,A每次修改數據之前都會加上鎖,防止被其他事務修改(真夠悲傷的)。今天重點介紹的就是這貨了。
例子
首頁某個頁面訪問量大,所以加了緩存,并設定緩存過期後刷新,如果隻是這樣處理,那麼會有什麼問題?問題是當請求并發量很大的時候,緩存過期的瞬間,大量請求會穿透緩存直達數據庫,造成雪崩效應,數據庫服務器直接宕機。如果有鎖機制,便可以控制隻有單個請求去更新緩存,更新未完成前,其他請求依舊讀取舊緩存。下面給出實現的過程:
先來看一段代碼實現:
版本1
這邏輯很簡單,但是如果請求執行過程意外退出了,沒有删除鎖,那麼以後緩存都不會更新了。
嗯,很好,知道問題所在,我優化一下,給它個過期時間不就行了:
版本2
那麼這樣就沒有問題了嗎,當然沒這麼簡單了,雖然我們保證隻有一個setnx成功,但是expire可以一直刷新啊,導緻鎖一直有效。
還好Redis的作者antirez很早就考慮到這問題了,從Redis 2.6.12版本起,set涵蓋了setnx的功能,并且set本身已經包含了設置過期時間的功能。那麼我們的實現變得簡單了:
版本3
這樣就行了嗎?
也還不行,如果更新時間比鎖的有效期還長,如果不加以判斷直接删除鎖,那麼就會出現誤删其他鎖的情況,針對這種情況,我們再優化一下,變成:
版本4
這下完美了嗎,依然沒有。試想一下,如果del鎖的請求到達過程中出現網絡延遲會依舊會誤删其他鎖。
也許你會問有沒有靠譜點的實現,答案是沒有。這個問題正是基于單Redis節點的分布式鎖的局限性所在,即便如此,隻要實現分布式鎖的時候加以注意,它依然相當有用。
後記假如要實現多節點的分布式鎖,有沒有辦法呢?有的,因為Redis的作者antirez大神已經給出一個更好的實現 - Redlock算法。有興趣的讀者可以Redis官網上了解一番,當然了,前提是你已經理解了基于單Redis節點的分布式鎖是如何實現的。
ps:之後會有MySQL優化系列文章發表,敬請期待!
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!