分布式鎖簡(jiǎn)介
同一時(shí)間只允許一個(gè)住戶使用狰贯。
避免重復(fù)工作劫瞳,避免破壞數(shù)據(jù)正確性呕童。
分布式鎖一些條件
- 互斥:僅一個(gè)客戶端持鎖。
- 防止死鎖:保證客戶端持鎖崩潰等其他情況漫雕,也可主動(dòng)釋放滨嘱。
死鎖段子:在異地需要買火車票回老家,但是身份證丟了無(wú)法購(gòu)票浸间,補(bǔ)辦身份證又需要本人坐火車回老家戶籍管理處太雨,就這樣生活太難。
- 加鎖魁蒜、解鎖必須同一客戶端囊扳。
- 容錯(cuò):大部分Redis節(jié)點(diǎn)正常,客戶端就可獲取和釋放鎖兜看。
常見(jiàn)實(shí)現(xiàn)方式:
- 基于Redis
- 基于Mysql
- 基于Zookeeper
Redis
1. setnx和expire的非原子性
setnx剛執(zhí)行成功锥咸,還未執(zhí)行expire,節(jié)點(diǎn)1 掛掉细移,出現(xiàn)死鎖搏予。
2. set
- set命令要用set key value px milliseconds nx;
- value要具有唯一性弧轧;
- 釋放鎖時(shí)要驗(yàn)證value值雪侥,不能誤解鎖;
set()得到2種結(jié)果:a,無(wú)鎖精绎,進(jìn)行加鎖操作速缨,設(shè)置有效期。b,已鎖不操作代乃。
滿足3個(gè)條件:互斥性:NX參數(shù)保證鎖存在旬牲,不會(huì)二次調(diào)用。死鎖:設(shè)置過(guò)期時(shí)間搁吓。容錯(cuò)性:暫考慮單機(jī)原茅。
const IF_NOT_EXISTS = 'NX';
const MILLISECOND_EXPIRE_TIME = 'PX';
const LOCK_VALUE = 1;
//加鎖
$redis->set($key, self::LOCK_VALUE , [self::IF_NOT_EXISTS, self::MILLISECOND_EXPIRE_TIME => $expire_time]);
$lua =<<<EOT
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
EOT;
$result = $redis->eval($lua, array($key, self::LOCK_VALUE), 1);
Redis分布式鎖常見(jiàn)問(wèn)題
最大的缺點(diǎn)-Redis主從復(fù)制: 鎖丟失
- 案例:主從發(fā)生切換
- Redis在master拿到鎖
- 加鎖key沒(méi)同步至slave
- master故障,主從遷移堕仔。slave升級(jí)為master
- 鎖丟失
- Redlock算法
- 腦裂
- 系統(tǒng)計(jì)時(shí)