之前在學校作小項目的時候,用到Redis,基本也只是用來看成緩存。如今博主在某金融平臺實習,發現Redis在生產中並不僅是看成緩存這麼簡單。在我接觸到的項目中,Redis起到了一個分佈式鎖的做用,具體狀況是這樣的:html
該項目在金融平臺中負責某塊業務,是一個分佈式系統,線上大概跑着10個左右的實例。其中有一個步驟須要用戶支付必定的費用,Redis分佈式鎖在其中大概處於這麼一個位置:redis
能夠看到在上分佈式鎖以後,系統作了兩個查詢校驗,而後向數據庫中插入了一條訂單記錄,接着才解鎖進入支付流程。算法
從業務的角度考慮分佈式鎖是好理解的,它保證了查詢及插入數據整個流程的原子性,防止查詢校驗的時候查到髒數據,使得支付前訂單信息落表的操做串行化執行。sql
儘管從業務上來講很好理解,但使用Redis做爲分佈式鎖對我來講是個新知識,我打算結合項目中的代碼,深挖一下這個知識點。數據庫
在實際項目中見過度布式鎖後,就不難理解爲何要使用分佈式鎖了:總結來講就是分佈式系統要訪問共享資源,爲了不併發訪問資源帶來錯誤,咱們爲共享資源添加一把鎖,讓各個訪問互斥,保證併發訪問的安全性,這就是使用分佈式鎖的緣由。緩存
redis中使用分佈式鎖很簡單,只要使用setnx指令對某個key上鎖就行:安全
setnx lock test //上鎖 del lock test //解鎖
當某個key沒有被佔用的時候,setnx指令會返回1,不然返回0,這就是Redis中分佈式鎖的使用原理。併發
固然咱們還能夠在上鎖以後使用expire指令給鎖設置過時時間。分佈式
看到這裏你可能會有疑問,若是咱們的程序流程不使用指令解鎖,靠redis設置時間過時來解鎖,貌似會出問題。假如咱們的服務進程在執行setnx以後和執行expire指令以前掛掉了,那這個鎖豈不是永遠都不能被釋放?性能
沒錯,這確實是個問題,當時人們在Redis的開源社區提出了一堆解決方案專門來解決這個問題,可實現方式都極爲複雜。後來Redis的做者在Redis 2.8版本中加入了set指令的擴展參數,使得setnx指令和expire指令可以同時執行,具體使用像下面這個樣子:
set lock test ex 5 nx ex:設置鍵的過時時間 nx:只在鍵不存在時,纔對鍵進行設置操做
今後之後,Redis成爲了分佈式鎖的寵兒。
在學習了Redis中分佈式鎖的使用後,很快咱們便發現了新的問題。在企業中,Redis基本上都是集羣部署的,集羣部署避免不了要面對某個節點宕機的問題。
咱們考慮這麼一種狀況:假設咱們在redis的主節點上添加了一把分佈式鎖,不幸的是主節點掛掉了,並且主節點上的鎖尚未同步到從節點上,若是此時有客戶端來請求得到同一把鎖,那麼它將順利地得到鎖,以前那把鎖會被無情地忽視掉,這就是分佈式鎖在Redis集羣中遇到的麻煩。
Redis的做者爲了解決這個問題提出了一個叫Redlock的算法,它的原理是這樣的:當上鎖的時候,把set指令發送給過半的節點,只要過半的鎖set成功,就認爲此次加鎖成功;當解鎖的時候,會向全部的節點發送del指令。
從這個算法的原理能夠看出,因爲Redlock須要同時對多個節點進行讀寫,所以使用Redlock加分佈式鎖的性能要比單機Redis低不少。由於主從複製出紕漏的機率極低,因此若是對分佈式加鎖過程有必定的容錯率的話,能夠考慮直接使用set指令;若是追求高可用性,能夠考慮使用Redlock算法。
固然,高可用性的分佈式鎖不僅有Redis的Redlock,咱們還能夠用zookeeper或者支持事務的數據庫作分佈式鎖。
簡述zookeeper的分佈式鎖原理:假設zk用某個節點做爲分佈式鎖,當不一樣的客戶端到zk競爭這把鎖的時候,zk會按順序給不一樣的客戶端建立一個子節點,掛在做爲分佈式鎖的節點下面。假設第一個來到的客戶端爲A,第二個來到的是B,分佈式節點下掛的第一個節點就是A,B緊跟着A,且B會監聽着A的生命狀態,當A釋放鎖後A會被刪除,這時B監聽到A被刪除,B接能上位得到分佈式鎖了。
在公司的項目中,雖然Redis是以集羣的方式部署的,但仍是使用最基本的set指令獲取分佈式鎖,由於這種方式的性能遠遠高於Redlock算法,也高於zk,數據庫等分佈式鎖實現方式。
雖然在高性能與低機率的錯誤中選擇了高性能,但項目中仍是作了其餘工做對錯誤狀況進行兜底的,好比在公司的項目中對主從複製時的錯誤狀況會拋出異常,而後根據異常會進行一些重試的操做。
此次對Redis分佈式鎖的探索算是加深了本身對Redis的理解,但我知道Redis的用處還遠遠不止分佈式鎖和緩存,留着後面繼續探索吧。
擴展閱讀:
原文出處:https://www.cnblogs.com/tanshaoshenghao/p/12577100.html