分佈式鎖Memcached 和 Redis 分佈式鎖方案 收藏其他網頁···

鏈接:http://www.cnblogs.com/zrhai/p/4015989.html

我只是個搬運工0.0


分佈式緩存,能解決單臺服務器內存不能無限擴張的瓶頸。在分佈式緩存的應用中,會遇到多個客戶端同時爭用的問題。這個時候,需要用到分佈式鎖,得到鎖的客戶端纔有操作權限。

Memcached 和 Redis 是常用的分佈式緩存構建方案,下面列舉下基於Memcached 和 Redis 分佈式鎖的實現方法。

Memcached 分佈式鎖

Memcached 可以使用 add 命令,該命令只有KEY不存在時,才進行添加,或者不會處理。Memcached 所有命令都是原子性的,併發下add 同一個KEY ,只會一個會成功。

利用這個原理,可以先定義一個 鎖 LockKEY ,add 成功的認爲是得到鎖。並且設置[過期超時] 時間,保證宕機後,也不會死鎖

在具體操作完後,判斷是否此次操作已超時。如果超時則不刪除鎖,如果不超時則刪除鎖。

僞代碼:

複製代碼
 1          if (mc.Add("LockKey", "Value", expiredtime))
 2             {
 3                 //得到鎖
 4                 try
 5                 {
 6                     //do business  function
 7 
 8                     //檢查超時
 9                     if (!CheckedTimeOut())
10                     {
11                         mc.Delete("LockKey");
12                     }
13                 }
14                 catch (Exception e)
15                 {
16                     mc.Delete("LockKey");
17                 }
18                
19             }
複製代碼

 

Redis 分佈式鎖

Redis  沒有add 命令,但有SETNX(SET if Not eXists)若給定的 key 已經存在,則 SETNX不做任何動作。設置成功,返回 1 。設置失敗,返回 0 。

SETNX 命令不能設置過期時間,需要再使用 EXPIRE 命令設置過期時間。

僞代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int lockResult = rd.SETNX("LockKey""Value");
if (lockResult == 1)
{
    //[1]得到鎖
 
    //[2]設置超時過期時間
    rd.EXPIRE("LockKey", expiredtime);
 
    try
    {
        //do business  function
 
        //檢查超時
        if (!CheckedTimeOut())
        {
            rd.DEL("LockKey");
        }
    }
    catch (Exception e)
    {
        rd.DEL("LockKey");
    }
 
}

   這種做法,有一個很大的潛在風險。[1]得到鎖後,再執行[2] 設置過期時間。如果在這期間出現宕機,則會導致沒有設置過期時間。按Redis 的默認緩存過期策略,這個鎖將不會釋放,產生死鎖。

所以不推薦用這種做法,應該用其它方式來實現鎖的超時過期策略:

     1:SETNX  value 值=當前時間+過期超時時間,返回1 則獲得鎖,返回0則沒有獲得鎖。轉2。

     2:GET 獲取 value 的值 。判斷鎖是否過期超時。如果超時,轉3。

     3:GETSET(將給定 key 的值設爲 value ,並返回 key 的舊值),GETSET  value 值=當前時間+過期超時時間, 判斷得到的value 如果仍然是超時的,那就說明得到鎖,否則沒有得到鎖。

從2併發進到3 的操作,會多次改寫超時時間,但這個不會有什麼影響。

僞代碼:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
string expiredtime = DateTime.Now.AddMinutes(LockTimeoutMinutes).ToString();
int lockResult = rd.SETNX("LockKey", expiredtime);
bool getLock = false;
if (lockResult == 1)
{
    //得到鎖
    getLock = true;
}
else
{
    string curExpiredtime = rd.GET("LockKey");
 
    //檢查鎖超時
    if (CheckedLockTimeOut(expiredtime))
    {
        expiredtime = DateTime.Now.AddMinutes(LockTimeoutMinutes).ToString();
        string newExpiredTime = GETSET(expiredtime);
        if (CheckedLockTimeOut(newExpiredTime))
        {
            //得到鎖
            getLock = true;
        }
    }
}
if (getLock)
{
    try
    {
        //do business  function
 
        //檢查超時
        if (!CheckedTimeOut())
        {
            rd.DEL("LockKey");
        }
    }
    catch (Exception e)
    {
        rd.DEL("LockKey");
    }
}

個人覺得這種做法,還是不完美。 

作者:張日海



發佈了23 篇原創文章 · 獲贊 20 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章