redis key存在则删除_Redis加锁的几种实现
我們經(jīng)常在工作中會(huì)碰到一些重復(fù)請(qǐng)求、并發(fā)等問(wèn)題,而給資源加鎖是一種不錯(cuò)的手段。我們今天就整理下使用redis作為分布式鎖的幾種實(shí)現(xiàn)。
redis可以用于幾個(gè)命令是:INCR、SETNX和SET。
1. 使用INCR加鎖
這種加鎖的思路是, key 不存在,那么 key 的值會(huì)先被初始化為 0 ,然后再執(zhí)行 INCR 操作進(jìn)行加一。然后其它用戶在執(zhí)行 INCR 操作進(jìn)行加一時(shí),如果返回的數(shù)大于 1 ,說(shuō)明這個(gè)鎖正在被使用當(dāng)中。
/***1、 客戶端A請(qǐng)求服務(wù)器獲取key的值為1表示獲取了鎖2、 客戶端B也去請(qǐng)求服務(wù)器獲取key的值為2表示獲取鎖失敗3、 客戶端A執(zhí)行代碼完成,刪除鎖4、 客戶端B在等待一段時(shí)間后在去請(qǐng)求的時(shí)候獲取key的值為1表示獲取鎖成功5、 客戶端B執(zhí)行代碼完成,刪除鎖 **/ $res = $redis->incr($key); // 自增1 $redis->expire($key, $ttl); // 設(shè)置鎖的有效期 if($res == 1){ // 獲取資源成功 }else{// 資源被其他請(qǐng)求占用 }2. 使用SETNX加鎖
這種加鎖的思路是,如果 key 不存在,將 key 設(shè)置為 value,如果 key 已存在,則 SETNX 不做任何動(dòng)作。
/***1、 客戶端A請(qǐng)求服務(wù)器設(shè)置key的值,如果設(shè)置成功就表示加鎖成功2、 客戶端B也去請(qǐng)求服務(wù)器設(shè)置key的值,如果返回失敗,那么就代表加鎖失敗3、 客戶端A執(zhí)行代碼完成,刪除鎖4、 客戶端B在等待一段時(shí)間后在去請(qǐng)求設(shè)置key的值,設(shè)置成功5、 客戶端B執(zhí)行代碼完成,刪除鎖 **/ $res = $redis->setNX($key, $value); // 當(dāng)key不存在時(shí)設(shè)置key=value $redis->expire($key, $ttl); // 設(shè)置鎖的有效期 if($res){ // 獲取資源成功 }else{// 資源被其他請(qǐng)求占用 }上面兩種方法都有一個(gè)問(wèn)題,會(huì)發(fā)現(xiàn),都需要設(shè)置 key 過(guò)期時(shí)間。那么為什么要設(shè)置key過(guò)期時(shí)間呢?如果請(qǐng)求執(zhí)行因?yàn)槟承┰蛞馔馔顺隽?#xff0c;導(dǎo)致創(chuàng)建了鎖但是沒有刪除鎖,那么這個(gè)鎖將一直存在(redis不設(shè)置key的過(guò)期時(shí)間,默認(rèn)是永久的),以至于一直處于加鎖狀態(tài)。于是乎我們需要給鎖加一個(gè)過(guò)期時(shí)間以防不測(cè)。
但是借助 Expire 來(lái)設(shè)置就不是原子性操作了。所以還可以通過(guò)redis事務(wù)來(lái)確保原子性。那上面的代碼就要優(yōu)化成:
// 第一種方式的加鎖 $redis->multi(); // 標(biāo)記一個(gè)事務(wù)塊的開始 $res = $redis->incr($key); $redis->expire($key, $ttl); $redis->exec(); // 提交事務(wù) if($res == 1){ // 獲取資源成功 }else{// 資源被其他請(qǐng)求占用 }// 第二種方式的加鎖 $redis->multi(); // 標(biāo)記一個(gè)事務(wù)塊的開始 $res = $redis->setNX($key, $value); $redis->expire($key, $ttl); $redis->exec(); // 提交事務(wù) if($res){ // 獲取資源成功 }else{// 資源被其他請(qǐng)求占用 }上面代碼看起來(lái)是不是很繁瑣。好在redis官方從版本 2.6.12 開始 SET 命令本身已經(jīng)包含了設(shè)置過(guò)期時(shí)間的功能。
3. 使用SET加鎖
/*** 1、 客戶端A請(qǐng)求服務(wù)器設(shè)置key的值,如果設(shè)置成功就表示加鎖成功2、 客戶端B也去請(qǐng)求服務(wù)器設(shè)置key的值,如果返回失敗,那么就代表加鎖失敗3、 客戶端A執(zhí)行代碼完成,刪除鎖4、 客戶端B在等待一段時(shí)間后在去請(qǐng)求設(shè)置key的值,設(shè)置成功5、 客戶端B執(zhí)行代碼完成,刪除鎖 **/ $res = $redis->set($key, $value, ['nx', 'ex' => $ttl]); //nx代表當(dāng)key不存在時(shí)設(shè)置 ex代表設(shè)置過(guò)期時(shí)間 if($res){ // 獲取資源成功 }else{// 資源被其他請(qǐng)求占用 }4. 其他問(wèn)題
雖然上面一步已經(jīng)滿足了我們的需求,但是還是要考慮其它問(wèn)題? - 1、 redis發(fā)現(xiàn)鎖失敗了要怎么辦?中斷請(qǐng)求還是循環(huán)請(qǐng)求? - 2、 循環(huán)請(qǐng)求的話,如果有一個(gè)獲取了鎖,其它的在去獲取鎖的時(shí)候,是不是容易發(fā)生搶鎖的可能? - 3、 鎖提前過(guò)期后,客戶端A還沒執(zhí)行完,然后客戶端B獲取到了鎖,這時(shí)候客戶端A執(zhí)行完了,會(huì)不會(huì)在刪鎖的時(shí)候把B的鎖給刪掉?
5. 解決辦法
- 針對(duì)問(wèn)題1:使用循環(huán)請(qǐng)求,循環(huán)請(qǐng)求去獲取鎖
- 針對(duì)問(wèn)題2:針對(duì)第二個(gè)問(wèn)題,在循環(huán)請(qǐng)求獲取鎖的時(shí)候,加入睡眠功能,等待幾毫秒在執(zhí)行循環(huán)
- 針對(duì)問(wèn)題3:在加鎖的時(shí)候存入的key是隨機(jī)的。這樣的話,每次在刪除key的時(shí)候判斷下存入的key里的value和自己存的是否一樣
就這么多了,喜歡就點(diǎn)個(gè)贊吧~
參考:
- http://ukagaka.github.io/php/2017/09/21/redisLock.html
總結(jié)
以上是生活随笔為你收集整理的redis key存在则删除_Redis加锁的几种实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: django mysql api_Dja
- 下一篇: 电脑性能飙升!游戏体验爽到飞起!影驰内存