redis解決併發問題

2021-09-23 05:58:11 字數 4721 閱讀 8957

用redis處理高併發是個很常見的方式,因為redis的訪問效率很高(直接訪問記憶體),一般我們會用來處理**一瞬間的併發量。

那如果要使用redis來進行高併發問題的解決的話,應注意以下幾點:

1、首先我們要先知道,我們在儲存時,應使用redis的setnx方法,不應該使用set方法,因為setnx擁有原子操作命令(正確點說法應該是使用setnx,根據其屬性可以保證共享資源的原子性操作),當資源鎖存在不能設定值,則返回0,而當鎖不存在,則設定鎖,返回1; 但如果使用set方法,則會出現在高併發情況下,程序同時獲取鎖狀態為null,同時設定,鎖之間相互覆蓋,但是倆程序仍在併發執行業務**的情況。

2、為了防止死鎖,我們不應直接使用jedis.setnx(lock, 1) 來進行簡單的加鎖,這樣會導致當程序執行出現問題,鎖未釋放,則其他程序永遠處於阻塞狀態,出現死鎖。 為了避免死鎖,我們在加鎖時帶上時間戳,setnx(lock, 時間戳+超時時間),當發現鎖超時了,便可以對鎖進行重置,避免死鎖。

接下來,實際操作!

設定鎖:

//其中currenttimemullis為當前時間、validetime為超時時間,key為資源

//對該資源進行鎖獲取,如果存在鎖則會返回false,不存在則設定值並返回true

boolean lock = redisservice.setnx(key, currenttimemullis+validetime);

//如果不存在並設定了值,則可以直接返回,因為已經獲取資源鎖成功

//否則,則代表存在這個鎖,則進行鎖是否超時的判斷。獲取該資源的鎖時間,用於判斷是否超時了

string keytime = redisservice.get(key);

if((long.valueof(currenttimemullis)-long.valueof(keytime))>validetime)else

public redistemplategetredistemplate()

/*** 獲取redis分布式鎖,支援可重入性 setifabsent()與expire()組全並不滿足原子性,所以使用lua指令碼保證原子性

*/public void getlock(string rediskey, int keeplocktimelenght, timeunit keeplocktimeunit) throws exception

defaultredisscriptredisscript = new defaultredisscript<>();

redisscript.setresulttype(long.class);

redisscript.setscripttext(setnx_expire_time_script);

listkeylist = arrays.aslist(rediskey);

long time = timeunit.milliseconds.convert(keeplocktimelenght, keeplocktimeunit);

long executeresult = (long) this.redistemplate.execute(redisscript, keylist, currentthreadqualifiedid, time);

boolean isgettingredislocksuccessful = (executeresult != null && executeresult == 1);

int retriedtimes = 0;

while (!isgettingredislocksuccessful) 獲取rediskey迴圈次數超過100,當前次數:{}", rediskey, retriedtimes);

}if (retriedtimes > 300)

thread.sleep(210);

isgettingredislocksuccessful = this.redistemplate.opsforvalue().setifabsent(rediskey,

currentthreadqualifiedid);

retriedtimes++;

} }/**

* 獲取redis分布式鎖,支援可重入性

* 支援重試

* setifabsent()與expire()組全並不滿足原子性,所以使用lua指令碼保證原子性

*/public void getlockforretry(string rediskey, int keeplocktimelenght, timeunit keeplocktimeunit) throws exception

defaultredisscriptsetneluajscript = new defaultredisscript<>();

setneluajscript.setresulttype(long.class);

setneluajscript.setscripttext(setnx_expire_time_script);

listrediskeylist = arrays.aslist(rediskey);

long timeinmillis = timeunit.milliseconds.convert(keeplocktimelenght, keeplocktimeunit);

// 構造retryer

retryerbuilderretryerbuilder = retryerbuilder.newbuilder()

.withwaitstrategy(waitstrategies.incrementingwait(1, timeunit.seconds, 5, timeunit.seconds));

retryerbuilder.withstopstrategy(stopstrategies.stopafterdelay(3, timeunit.minutes));

retryerbuilder.withretrylistener(new retrylistener() 】次重試,獲取鎖rediskey:{},結果:{},重試時間離第1次間隔為:{}(ms)!", attempt.getattemptnumber(), rediskey,

attempt.getresult(), attempt.getdelaysincefirstattempt());

}});

retryerbuilder.retryifresult(getlocksuccessful -> !getlocksuccessful);

retryerretryer = retryerbuilder.build();

retryer.call(new callable()

});log.info("最後一次,獲取分布式鎖rediskey:{}成功!", rediskey);

} /**

* * 釋放redis鎖

* * @param rediskey

* void

*/public void releaselock(string rediskey)

this.redistemplate.delete(rediskey);

} /**

* 獲取分布式鎖

* * @param rediskey

* @param keeplocktimelenght

* @param keeplocktimeunit

* @return

*/public boolean trylock(string rediskey, int keeplocktimelenght, timeunit keeplocktimeunit)

defaultredisscriptredisscript = new defaultredisscript<>();

redisscript.setresulttype(long.class);

redisscript.setscripttext(setnx_expire_time_script);

listkeylist = arrays.aslist(rediskey);

long time = timeunit.milliseconds.convert(keeplocktimelenght, keeplocktimeunit);

long executeresult = (long) this.redistemplate.execute(redisscript, keylist, currentthreadqualifiedid, time);

boolean isgettingredislocksuccessful = (executeresult != null && executeresult == 1);

if (!isgettingredislocksuccessful) ,已經被占用,無法獲取鎖,不進行重試!", rediskey);

}else,獲取鎖成功", rediskey);

} return isgettingredislocksuccessful;

} public boolean trysetvalue(string rediskey,string value, int keeplocktimelenght, timeunit keeplocktimeunit) ,已經被占用,trysetvalue失敗,不進行重試!", rediskey);

}else,trysetvalue成功,value:{}", rediskey,value);

} return isgettingredislocksuccessful;

} public string getlockowner(string redislockkey)

}

利用Redis鎖解決併發問題

用redis處理高併發是個很常見的方式,因為redis的訪問效率很高 直接訪問記憶體 一般我們會用來處理 一瞬間的併發量。那如果要使用redis來進行高併發問題的解決的話,應注意以下幾點 1 首先我們要先知道,我們在儲存時,應使用redis的setnx方法,不應該使用set方法,因為setnx擁有原...

解決併發問題

一.使用redis鎖 智慧型雲 工單系統 搶單 工單id鎖key public static final string cloud live gain order lock orderid cloud live gain order lock orderid component public cla...

Redis高併發問題

商品搶購秒殺等活動 使用redis列表結構實現佇列資料結構,強拆的用rpush入隊,再用lpop出隊.redis宕機或者連線不上 解決方法 配置主從複製,配置哨兵模式,一旦發現主機宕機,讓下乙個從機當做主機。最壞的情況,只能關閉redis連線,去往資料庫連線。但由於資料量大,這樣sql資料庫也會宕掉...