❶ Redis分布式鎖的原理是什麼如何續期
在傳統單體應用單機部署的情況下,並發問題可以通過使用java並發相關的鎖如synchronized,但是當規模上升到分布式集群的情況下,要控制共享資源訪問,就需要通過分布式鎖來實現。常見的分布式鎖方案如資料庫樂觀鎖,Redis鎖,zk鎖等。
Redis分布式鎖的原理
Redis分布式鎖可以有多種方式實現但是其核心就是通過以下三個Redis命令組合實現。
SETNX SETNX key val 當且僅當key不存在時,set一個key為val的字元串,返回1;若key存在,則什麼都不做,返回0。
Expire expire key timeout 為key設置一個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。
Delete delete key 刪除key
核心思想
使用setnx獲取鎖。如果成功取到鎖,則使用expire命令為鎖添加一個超時時間,超過該時間則自動釋放鎖。
獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
注意
上面為Redis的一個最簡單的鎖實現原理,實際中還需要考慮更多具體的情況作出相應的調整。如
上面的demo中,當集群系統時間不一致時會有問題
當伺服器異常關閉或是重啟,加鎖後沒來得急設置鎖超時時間,如何避免死鎖
實際開發環境中不確定的因素有很多,需要慢慢地去調整實踐達到理想狀態,可以考慮使用redisson框架來實現。
如何續期?
這個情況比較獨特,出現這個問題的根本原因在於鎖失效的時間小於業務處理的時間導致業務還沒處理完畢鎖就釋放了。那麼解決方案是合理地結合業務去設置鎖失效的時間。
但是也有更好的方案就如前文提到的redisson,其中的可重入鎖概念。
默認情況下,加鎖的時間是30秒.如果加鎖的業務沒有執行完,那麼到 30-10 = 20秒的時候,就會進行一次續期,把鎖重置成30秒。
以上就是redis鎖的原理及續期的方式,希望我的回答能對你有所幫助。
❷ 怎樣實現redis分布式鎖
使用分布式鎖要滿足的幾個條件:系統是一個分布式系統(關鍵是分布式,單機的可以使用ReentrantLock或者synchronized代碼塊來實現)共享資源(各個系統訪問同一個資源,資源的載體可能是傳統關系型資料庫或者NoSQL)。
映射(Map)、多值映射(Multimap)、集(Set)、列表(List)、有序集(SortedSet)、計分排序集(ScoredSortedSet)、字典排序集(LexSortedSet)、列隊(Queue)、雙端隊列(Deque)、阻塞隊列(Blocking Queue)。
有界阻塞列隊(Bounded Blocking Queue)、阻塞雙端列隊(Blocking Deque)、阻塞公平列隊(Blocking Fair Queue)、延遲列隊(Delayed Queue)、優先隊列(Priority Queue)和優先雙端隊列(Priority Deque)。
❸ 使用redis實現的分布式鎖原理是什麼
一、寫在前面
現在面試,一般都會聊聊分布式系統這塊的東西。通常面試官都會從服務框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事務、分布式鎖、ZooKeeper等知識。
所以咱們這篇文章就來聊聊分布式鎖這塊知識,具體的來看看Redis分布式鎖的實現原理。
說實話,如果在公司里落地生產環境用分布式鎖的時候,一定是會用開源類庫的,比如Redis分布式鎖,一般就是用Redisson框架就好了,非常的簡便易用。
大家如果有興趣,可以去看看Redisson的官網,看看如何在項目中引入Redisson的依賴,然後基於Redis實現分布式鎖的加鎖與釋放鎖。
下面給大家看一段簡單的使用代碼片段,先直觀的感受一下:
大家看到了吧,那個myLock的hash數據結構中的那個客戶端ID,就對應著加鎖的次數
(5)釋放鎖機制
如果執行lock.unlock(),就可以釋放分布式鎖,此時的業務邏輯也是非常簡單的。
其實說白了,就是每次都對myLock數據結構中的那個加鎖次數減1。
如果發現加鎖次數是0了,說明這個客戶端已經不再持有鎖了,此時就會用:
「del myLock」命令,從redis里刪除這個key。
然後呢,另外的客戶端2就可以嘗試完成加鎖了。
這就是所謂的分布式鎖的開源Redisson框架的實現機制。
一般我們在生產系統中,可以用Redisson框架提供的這個類庫來基於redis進行分布式鎖的加鎖與釋放鎖。
(6)上述Redis分布式鎖的缺點
其實上面那種方案最大的問題,就是如果你對某個redis master實例,寫入了myLock這種鎖key的value,此時會非同步復制給對應的master slave實例。
但是這個過程中一旦發生redis master宕機,主備切換,redis slave變為了redis master。
接著就會導致,客戶端2來嘗試加鎖的時候,在新的redis master上完成了加鎖,而客戶端1也以為自己成功加了鎖。
此時就會導致多個客戶端對一個分布式鎖完成了加鎖。
這時系統在業務語義上一定會出現問題,導致各種臟數據的產生。
所以這個就是redis cluster,或者是redis master-slave架構的主從非同步復制導致的redis分布式鎖的最大缺陷:在redis master實例宕機的時候,可能導致多個客戶端同時完成加鎖。
❹ java 鏈接redis 怎麼加鎖
我介紹一下Redis分布式鎖吧:
一、定義redis實現分布式鎖的介面
[java]viewplainprint?
packagecom.iol.common.util.concurrent.locks;
importjava.io.Serializable;
/**
*Description:定義redis實現分布式鎖的演算法<br/>
*_SMALL_TAIL.<br/>
*ProgramName:IOL_SMALL_TAIL<br/>
*Date:2015年11月8日
*
*@author王鑫
*@version1.0
*/
{
/**
*加鎖演算法<br/>
*@paramkey
*@return
*/
publicbooleanlock(Stringkey);
/**
*解鎖演算法<br/>
*@paramkey
*@return
*/
publicbooleanunLock(Stringkey);
}
二、redis分布式鎖基礎演算法實現
[java]viewplainprint?
packagecom.iol.common.util.concurrent.locks.arithmetic;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importcom.iol.common.util.concurrent.locks.IRedisComponent;
importcom.iol.common.util.concurrent.locks.IRedisLockArithmetic;
/**
*Description:redis分布式鎖基礎演算法實現<br/>
*_SMALL_TAIL.<br/>
*ProgramName:IOL_SMALL_TAIL<br/>
*Date:2015年11月9日
*
*@author王鑫
*@version1.0
*/
{
/**
*serialVersionUID
*/
=-8333946071502606883L;
privateLoggerlogger=LoggerFactory.getLogger(RedisLockBaseArithmetic.class);
/**
*redis操作方法
*/
;
/**
*超時時間,以毫秒為單位<br/>
*默認為5分鍾
*/
privatelongovertime=5*60*1000L;
/**
*休眠時長,以毫秒為單位<br/>
*默認為100毫秒
*/
privatelongsleeptime=100L;
/**
*當前時間
*/
privatelongcurrentLockTime;
/**
*@
*/
publicvoidsetRedisComp(IRedisComponentredisComp){
this.redisComp=redisComp;
}
/**
*@paramovertimetheovertimetoset
*/
publicvoidsetOvertime(longovertime){
this.overtime=overtime;
}
/**
*@
*/
publicvoidsetSleeptime(longsleeptime){
this.sleeptime=sleeptime;
}
/*(non-Javadoc)
*@seecom.iol.common.util.concurrent.locks.IRedisLockArithmetic#lock(java.lang.String,java.lang.Long)
*/
@Override
publicbooleanlock(Stringkey){
while(true){
//當前加鎖時間
currentLockTime=System.currentTimeMillis();
if(redisComp.setIfAbsent(key,currentLockTime)){
//獲取鎖成功
logger.debug("直接獲取鎖{key:{},currentLockTime:{}}",key,currentLockTime);
returntrue;
}else{
//其他線程佔用了鎖
logger.debug("檢測到鎖被佔用{key:{},currentLockTime:{}}",key,currentLockTime);
LongotherLockTime=redisComp.get(key);
if(otherLockTime==null){
//其他系統釋放了鎖
//立刻重新嘗試加鎖
logger.debug("檢測到鎖被釋放{key:{},currentLockTime:{}}",key,currentLockTime);
continue;
}else{
if(currentLockTime-otherLockTime>=overtime){
//鎖超時
//嘗試更新鎖
logger.debug("檢測到鎖超時{key:{},currentLockTime:{},otherLockTime:{}}",key,currentLockTime,otherLockTime);
LongotherLockTime2=redisComp.getAndSet(key,currentLockTime);
if(otherLockTime2==null||otherLockTime.equals(otherLockTime2)){
logger.debug("獲取到超時鎖{key:{},currentLockTime:{},otherLockTime:{},otherLockTime2:{}}",key,currentLockTime,otherLockTime,otherLockTime2);
returntrue;
}else{
sleep();
//重新嘗試加鎖
logger.debug("重新嘗試加鎖{key:{},currentLockTime:{}}",key,currentLockTime);
continue;
}
}else{
//鎖未超時
sleep();
//重新嘗試加鎖
logger.debug("重新嘗試加鎖{key:{},currentLockTime:{}}",key,currentLockTime);
continue;
}
}
}
}
}
/*(non-Javadoc)
*@seecom.iol.common.util.concurrent.locks.IRedisLockArithmetic#unLock(java.lang.String)
*/
@Override
publicbooleanunLock(Stringkey){
logger.debug("解鎖{key:{}}",key);
redisComp.delete(key);
returntrue;
}
/**
*休眠<br/>
*@paramsleeptime
*/
privatevoidsleep(){
try{
Thread.sleep(sleeptime);
}catch(InterruptedExceptione){
thrownewLockException("線程異常中斷",e);
}
}
}
❺ java怎麼實現redis分布式鎖
Redis有一系列的命令,特點是以NX結尾,NX是Not eXists的縮寫,如SETNX命令就應該理解為:SET if Not eXists。這系列的命令非常有用,這里講使用SETNX來實現分布式鎖。
用SETNX實現分布式鎖
利用SETNX非常簡單地實現分布式鎖。例如:某客戶端要獲得一個名字foo的鎖,客戶端使用下面的命令進行獲取:
SETNX lock.foo <current Unix time + lock timeout + 1>
如返回1,則該客戶端獲得鎖,把lock.foo的鍵值設置為時間值表示該鍵已被鎖定,該客戶端最後可以通過DEL lock.foo來釋放該鎖。
如返回0,表明該鎖已被其他客戶端取得,這時我們可以先返回或進行重試等對方完成或等待鎖超時。
解決死鎖
上面的鎖定邏輯有一個問題:如果一個持有鎖的客戶端失敗或崩潰了不能釋放鎖,該怎麼解決?我們可以通過鎖的鍵對應的時間戳來判斷這種情況是否發生了,如果當前的時間已經大於lock.foo的值,說明該鎖已失效,可以被重新使用。
發生這種情況時,可不能簡單的通過DEL來刪除鎖,然後再SETNX一次,當多個客戶端檢測到鎖超時後都會嘗試去釋放它,這里就可能出現一個競態條件,讓我們模擬一下這個場景:
C0操作超時了,但它還持有著鎖,C1和C2讀取lock.foo檢查時間戳,先後發現超時了。
C1 發送DEL lock.foo
C1 發送SETNX lock.foo 並且成功了。
C2 發送DEL lock.foo
C2 發送SETNX lock.foo 並且成功了。
這樣一來,C1,C2都拿到了鎖!問題大了!
幸好這種問題是可以避免D,讓我們來看看C3這個客戶端是怎樣做的:
C3發送SETNX lock.foo 想要獲得鎖,由於C0還持有鎖,所以Redis返回給C3一個0
C3發送GET lock.foo 以檢查鎖是否超時了,如果沒超時,則等待或重試。
反之,如果已超時,C3通過下面的操作來嘗試獲得鎖:
GETSET lock.foo <current Unix time + lock timeout + 1>
通過GETSET,C3拿到的時間戳如果仍然是超時的,那就說明,C3如願以償拿到鎖了。
如果在C3之前,有個叫C4的客戶端比C3快一步執行了上面的操作,那麼C3拿到的時間戳是個未超時的值,這時,C3沒有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒拿到鎖,但它改寫了C4設置的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。
注意:為了讓分布式鎖的演算法更穩鍵些,持有鎖的客戶端在解鎖之前應該再檢查一次自己的鎖是否已經超時,再去做DEL操作,因為可能客戶端因為某個耗時的操作而掛起,操作完的時候鎖因為超時已經被別人獲得,這時就不必解鎖了。
示例偽代碼
根據上面的代碼,我寫了一小段Fake代碼來描述使用分布式鎖的全過程:
# get lock
lock = 0
while lock != 1:
timestamp = current Unix time + lock timeout + 1
lock = SETNX lock.foo timestamp
if lock == 1 or (now() > (GET lock.foo) and now() > (GETSET lock.foo timestamp)):
break;
else:
sleep(10ms)
# do your job
do_job()
# release
if now() < GET lock.foo:
DEL lock.foo
是的,要想這段邏輯可以重用,使用python的你馬上就想到了Decorator,而用Java的你是不是也想到了那誰?AOP + annotation?行,怎樣舒服怎樣用吧,別重復代碼就行。
❻ redis 分布式鎖為什麼比synchronized 快
從redis獲取值N,對數值N進行邊界檢查,自加1,然後N寫回redis中。
這種應用場景很常見,像秒殺,全局遞增ID、IP訪問限制等。
以IP訪問限制來說,惡意攻擊者可能發起無限次訪問,並發量比較大,分布式環境下對N的邊界檢查就不可靠,因為從redis讀的N可能已經是臟數據。
傳統的加鎖的做法(如java的synchronized和Lock)也沒用,因為這是分布式環境,這個同步問題的救火隊員也束手無策。在這危急存亡之秋,分布式鎖終於有用武之地了。
❼ Redis 分布式鎖有什麼缺陷
Redis 分布式鎖不能解決超時的問題,分布式鎖有一個超時時間,程序的執行如果超出了鎖的超時時間就會出現問題。
❽ Redis的主要功能
緩存:這應該是 Redis 最主要的功能了,也是大型網站必備機制,合理地使用緩存不僅可以加 快數據的訪問速度,而且能夠有效地降低後端數據源的壓力。共享Session:對於一些依賴 session 功能的服務來說,如果需要從單機變成集群的話,可以選擇 redis 來統一管理 session。消息隊列系統:消息隊列系統可以說是一個大型網站的必備基礎組件,因為其具有業務 解耦、非實時業務削峰等特性。Redis提供了發布訂閱功能和阻塞隊列的功 能,雖然和專業的消息隊列比還不夠足夠強大,但是對於一般的消息隊列功 能基本可以滿足。比如在分布式爬蟲系統中,使用 redis 來統一管理 url隊列。分布式鎖:在分布式服務中。可以利用Redis的setnx功能來編寫分布式的鎖,雖然這個可能不是太常用。 當然還有諸如排行榜、點贊功能都可以使用 Redis 來實現,但是 Redis 也不是什麼都可以做,比如數據量特別大時,不適合 Redis,我們知道 Redis 是基於內存的,雖然內存很便宜,但是如果你每天的數據量特別大,比如幾億條的用戶行為日誌數據,用 Redis 來存儲的話,成本相當的高。