在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 大數(shù)據(jù)/ 分布式鎖
Redis 數(shù)據(jù)淘汰機(jī)制
積分排行榜
小剖 Memcache
Redis 數(shù)據(jù)結(jié)構(gòu) intset
分布式鎖
從哪里開始讀起,怎么讀
Redis 數(shù)據(jù)結(jié)構(gòu) dict
不在浮沙筑高臺(tái)
Redis 集群(上)
Redis 監(jiān)視器
源碼閱讀工具
Redis 日志和斷言
內(nèi)存數(shù)據(jù)管理
Redis 數(shù)據(jù)結(jié)構(gòu)綜述
源碼日志
Web 服務(wù)器存儲(chǔ) session
消息中間件
Redis 與 Lua 腳本
什么樣的源代碼適合閱讀
Redis 數(shù)據(jù)結(jié)構(gòu) sds
Memcached slab 分配策略
訂閱發(fā)布機(jī)制
Redis 是如何提供服務(wù)的
Redis 事務(wù)機(jī)制
Redis 集群(下)
主從復(fù)制
Redis 應(yīng)用
RDB 持久化策略
Redis 數(shù)據(jù)遷移
Redis 事件驅(qū)動(dòng)詳解
初探 Redis
Redis 與 Memcache
AOF 持久化策略
Redis 數(shù)據(jù)結(jié)構(gòu) redisOb
作者簡介
Redis 數(shù)據(jù)結(jié)構(gòu) ziplist
Redis 數(shù)據(jù)結(jié)構(gòu) skiplist
Redis 哨兵機(jī)制

分布式鎖

實(shí)現(xiàn)

*nix系統(tǒng)編程中,遇到多個(gè)進(jìn)程或者線程共享一塊資源的時(shí)候,通常會(huì)使用系統(tǒng)自身提供的鎖,譬如一個(gè)進(jìn)程里的多線程,會(huì)用互斥鎖;多個(gè)進(jìn)程之間,會(huì)用信號(hào)量等。這個(gè)場景中所謂的共享資源僅僅限于本地,倘若共享資源存在于網(wǎng)絡(luò)上,本地的“鎖”就不起作用了?;コ庠L問某個(gè)網(wǎng)絡(luò)上的資源,需要有一個(gè)存在于網(wǎng)絡(luò)上的鎖服務(wù)器,負(fù)責(zé)鎖的申請(qǐng)與回收。Redis 可以充當(dāng)鎖服務(wù)器的角色。首先,Redis 是單進(jìn)程單線程的工作模式,所有前來申請(qǐng)鎖資源的請(qǐng)求都被排隊(duì)處理,能保證鎖資源的同步訪問。

可以借助 Redis 管理鎖資源,來實(shí)現(xiàn)網(wǎng)絡(luò)資源的互斥。

我們可以在 Redis 服務(wù)器設(shè)置一個(gè)鍵值對(duì),用以表示一把互斥鎖,當(dāng)申請(qǐng)鎖的時(shí)候,要求申請(qǐng)方設(shè)置(SET)這個(gè)鍵值對(duì),當(dāng)釋放鎖的時(shí)候,要求釋放方刪除(DEL)這個(gè)鍵值對(duì)。譬如申請(qǐng)鎖的過程,可以用下面的偽代碼表示:

lock = redis.get("mutex_lock");
if(!lock)
    error("apply the lock error.");
else
    -- 確定可以申請(qǐng)鎖
    redis.set("mutex_lock","locking");
    do_something();

這種申請(qǐng)鎖的方法,涉及到客戶端和 Redis 服務(wù)器的多次交互,當(dāng)客戶端確定可以加鎖的時(shí)候,可能這時(shí)候鎖已經(jīng)被其他客戶端申請(qǐng)了,最終導(dǎo)致兩個(gè)客戶端同時(shí)持有鎖,互斥的語意非常容易被打破。在 Redis 官方文檔描述了一些方法并且參看了網(wǎng)上的文章,好些方法都提及了這個(gè)問題。我們會(huì)發(fā)現(xiàn),這些方法的共同特點(diǎn)就是申請(qǐng)鎖資源的整個(gè)過程分散在客戶端和服務(wù)端,如此很容易出現(xiàn)數(shù)據(jù)一致性的問題。

因此,最好的辦法是將“申請(qǐng)/釋放鎖”的邏輯操作都放在服務(wù)器上,Redis Lua 腳本可以勝任。下面給出申請(qǐng)互斥鎖的 Lua 腳本:

-- apply for lock
local key = KEYS[1]
local res = redis.call('get', key)

-- 鎖被占用,申請(qǐng)失敗
if res == '0' then
return -1

-- 鎖可以被申請(qǐng)
else
local setres = redis.call('set', key, 0)
if setres['ok'] == 'OK' then
return 0
end
end
return -1

get 命令不成功返回(nil).
實(shí)驗(yàn)命令:保存lua 腳本redis-cli script load ”$(cat mutex_lock.lua)”

同樣,釋放鎖的操作也可以在 Lua 腳本中實(shí)現(xiàn):

-- releae lock
local key = KEYS[1]
local setres = redis.call('set', key, 1)
if setres['ok'] == 'OK' then
return 0
return -1

如上 Lua 腳本基本的鎖管理的問題,將鎖的管理邏輯放在服務(wù)器端,可見 Lua 能拓展 Redis 服務(wù)器的功能。但上面的鎖管理方案是有問題的。

死鎖的問題

首先是客戶端崩潰導(dǎo)致的死鎖。按照上面的方法,當(dāng)某個(gè)客戶端申請(qǐng)鎖后因崩潰等原因無法釋放鎖,那么其他客戶端無法申請(qǐng)鎖,會(huì)導(dǎo)致死鎖。

一般,申請(qǐng)鎖是為了讓多個(gè)訪問方對(duì)某塊數(shù)據(jù)作互斥訪問(修改),而我們應(yīng)該將訪問的時(shí)間控制在足夠短,如果持有鎖的時(shí)間過長,系統(tǒng)整體的性能肯定是下降的??梢越o定一個(gè)足夠長的超時(shí)時(shí)間,當(dāng)訪問方超時(shí)后尚未釋放鎖,可以自動(dòng)把鎖釋放。

Redis 提供了 TTL 功能,鍵值對(duì)在超時(shí)后會(huì)自動(dòng)被剔除,在 Redis 的數(shù)據(jù)集中有一個(gè)哈希表專門用作鍵值對(duì)的超時(shí)。所以,我們有下面的 Lua 代碼:

-- apply for lock
local key = KEYS[1]
local timeout = KEYS[2]

local res = redis.call('get', key)

-- 鎖被占用,申請(qǐng)失敗
if res == '0' then
return -1
-- 鎖可以被申請(qǐng)
else
local setres = redis.call('set', key, 0)
local exp_res = redis.call('pexpire', key, timeout)
if exp_res == 1 then
return 0
end
end
return -1

如此能夠解決鎖持有者崩潰而鎖資源無法釋放帶來的死鎖問題。

再者是 Redis 服務(wù)器崩潰導(dǎo)致的死鎖。當(dāng)管理鎖資源的 Redis 服務(wù)器宕機(jī)了,客戶端既無法申請(qǐng)也無法釋放鎖,死鎖形成了。一種解決的方法是設(shè)置一個(gè)備份 Redis 服務(wù)器,當(dāng) Redis 主機(jī)宕機(jī)后,可以使用備份機(jī),但這需要保證主備的數(shù)據(jù)是同步的,不允許有延遲。

在同步有延遲的情況下,依舊會(huì)出現(xiàn)兩個(gè)客戶端同時(shí)持有鎖的問題。