Redis事務機制和分佈式鎖

Redis事務機制

嚴格意義來說,Redis的事務和咱們理解的傳統數據庫(如mysql)的事務是不同的;Redis的事務實質上是命令的集合,在一個事務中要麼全部命令都被執行,要麼全部事物都不執行。
一個事務從開始到執行會經歷如下三個階段:java

  1. 開始事務。
  2. 命令入隊。
  3. 執行事務。

在MySQL中咱們使用START TRANSACTION 或 BEGIN開啓一個事務,使用COMMIT提交一個事務;而在Redis中咱們使用MULTI 開始一個事務,由 EXEC 命令觸發事務, 一併執行事務中的全部命令。mysql

這裏寫圖片描述

能夠看到,MULTI 開始到 EXEC結束前,中間全部的命令都被加入到一個命令隊列中;當執行 EXEC命令後,將QUEUE中全部的命令執行。redis

此外咱們可使用DISCARD取消事務。sql

這裏寫圖片描述

須要注意的是:
1.Redis的事務沒有關係數據庫事務提供的回滾(rollback),因此開發者必須在事務執行失敗後進行後續的處理;
2.若是在一個事務中的命令出現錯誤,那麼全部的命令都不會執行
3.若是在一個事務中出現運行錯誤,那麼正確的命令會被執行數據庫

WATCH併發

研究過java的J.U.C包的人應該都知道CAS,CAS是一種保證原子性的操做。那麼redis也提供了這樣的一個機制,就是利用watch命令來實現的。分佈式

WATCH命令能夠監控一個或多個鍵,一旦其中有一個鍵被修改(或刪除),以後的事務就不會執行,監控一直持續到EXEC命令。工具

分佈式鎖

什麼是分佈式鎖?spa

分佈式鎖是控制分佈式系統之間同步訪問共享資源的一種方式。若是不一樣的系統或是同一個系統的不一樣主機之間共享了一個或一組資源,那麼訪問這些資源的時候,每每須要互斥來防止彼此干擾來保證一致性,在這種狀況下,便須要使用到分佈式鎖。線程

實現分佈式鎖有不少實現方式和工具,如Zookeeper、Redis等。

使用Redis實現分佈式鎖原理:

Redis爲單進程單線程模式,採用隊列模式將併發訪問變成串行訪問,且多客戶端對Redis的鏈接並不存在競爭關係,基於此,Redis中可使用SETNX命令實現分佈式鎖。

SETNX——SET if Not eXists(若是不存在,則設置):

setnx key value

將 key 的值設爲 value ,當且僅當 key 不存在
若給定的 key 已經存在,則 SETNX 不作任何動做。

這裏寫圖片描述

若是須要解鎖,使用 del key 命令就能釋放鎖:

這裏寫圖片描述

左圖首先使用setnx對鍵加鎖成功返回1,右圖再次使用setnx命令對鍵加鎖失敗返回0,說明有客戶端持有鎖。使用del釋放鎖之後,右圖就可使用setnx命令對鍵加鎖。

解決死鎖

若是一個持有鎖的客戶端失敗或崩潰了不能釋放鎖,該怎麼解決?

答:給鎖設置一個過時時間,能夠經過兩種方法實現:經過命令 「setnx 鍵名 過時時間 「;或者經過設置鎖的expire時間,讓Redis去刪除鎖。

第一種實現方式:
使用 setnx key 「當前系統時間+鎖持有的時間」和getset key 「當前系統時間+鎖持有的時間」組合的命令就能夠實現。
具體作法以下:

客戶端2發送SETNX lock.test 想要得到鎖,因爲以前的客戶端1還持有鎖,因此Redis返回一個0
客戶端2發送GET lock.test 以檢查鎖是否超時了,若是沒超時,則等待或重試。
反之,若是已超時,客戶端2經過下面的操做來嘗試得到鎖:
GETSET lock.test 過時的時間
經過GETSET,客戶端2拿到的時間戳若是仍然是超時的,那就說明,客戶端2如願以償拿到鎖了。
若是在客戶端2以前,有個客戶端3比客戶端2快一步執行了上面的操做,那麼客戶端2拿到的時間戳是個未超時的值,這時,說明客戶端2沒有如期得到鎖,須要再次等待或重試。
儘管客戶端2沒拿到鎖,但它改寫了客戶端3設置的鎖的超時值,不過這一點很是微小的偏差帶來的影響能夠忽略不計。

第二種就很是簡單了:
經過Redis中expire()給鎖設定最大持有時間,若是超過,則Redis來幫咱們釋放鎖。

1.客戶端1使用setnx得到了鎖,而且使用expire設定一個過時時間,假定是10ms

2.過了4ms後,客戶端1不幸運的宕機了,此時客戶端2想要經過setnx嘗試得到鎖,可是鎖尚未過時,任然被客戶端1所持有。

3.到了11ms時,鎖過時了,Redis幫咱們刪除了鎖,此時客戶端2想要經過setnx嘗試得到鎖,此時就能成功得到鎖。

在實際過程當中,咱們能夠設定一個時間T,用來表示客戶端在初次嘗試得到鎖失敗之後,在屢次嘗試得到鎖所花的時間。若是次時間爲0,表示除此嘗試得到鎖失敗之後就不會再去嘗試得到鎖了。

相關文章
相關標籤/搜索