掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢(xún)/運(yùn)營(yíng)咨詢(xún)/技術(shù)建議/互聯(lián)網(wǎng)交流
Redis 簡(jiǎn)簡(jiǎn)單單的幾種數(shù)據(jù)類(lèi)型,一個(gè) key/value 數(shù)據(jù)庫(kù),現(xiàn)在又是分布式鎖、又是限流工具、又是消息隊(duì)列......,感覺(jué)都要被玩壞了。不過(guò)話說(shuō)回來(lái),Redis 在這么多場(chǎng)合被開(kāi)發(fā)者們喜歡,還是得益于它極高的性能與使用的簡(jiǎn)潔性。

創(chuàng)新互聯(lián)公司總部坐落于成都市區(qū),致力網(wǎng)站建設(shè)服務(wù)有成都網(wǎng)站制作、做網(wǎng)站、網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃、網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站維護(hù)、公眾號(hào)搭建、微信小程序定制開(kāi)發(fā)、軟件開(kāi)發(fā)等為企業(yè)提供一整套的信息化建設(shè)解決方案。創(chuàng)造真正意義上的網(wǎng)站建設(shè),為互聯(lián)網(wǎng)品牌在互動(dòng)行銷(xiāo)領(lǐng)域創(chuàng)造價(jià)值而不懈努力!
在面試的時(shí)候,說(shuō)到 Redis ,很多人第一反應(yīng)就是緩存,其實(shí)除了緩存,Redis 還有非常多豐富的使用場(chǎng)景,這些使用場(chǎng)景,松哥在未來(lái)都會(huì)和大家一一分享。
今天就先來(lái)看一個(gè)簡(jiǎn)單的,用 Redis 做分布式鎖。
首先我們來(lái)看一個(gè)問(wèn)題場(chǎng)景:
例如一個(gè)簡(jiǎn)單的用戶(hù)操作,一個(gè)線程去修改用戶(hù)的狀態(tài),首先從數(shù)據(jù)庫(kù)中讀出用戶(hù)的狀態(tài),然后在內(nèi)存中進(jìn)行修改,修改完成后,再存回去。在單線程中,這個(gè)操作沒(méi)有問(wèn)題,但是在多線程中,由于讀取、修改、存 這是三個(gè)操作,不是原子操作,所以在多線程中,這樣會(huì)出問(wèn)題。
解決這個(gè)問(wèn)題,我們就需要鎖,對(duì)于鎖,大家應(yīng)該不會(huì)陌生,在 Java 中的 synchronized 以及 ReentrantLock 可重入鎖都是我們比較常見(jiàn)的,但是這種鎖都是本地鎖,現(xiàn)在微服務(wù)、分布式系統(tǒng)思想大行其道,在這樣的系統(tǒng)中,本地鎖顯然是不夠用的,于是大家紛紛想辦法,如何在分布式環(huán)境下解決鎖的問(wèn)題。想出來(lái)的辦法很多,我們可以通過(guò) MySQL、可以通過(guò) ZK、也可以通過(guò) Redis ,都可以用來(lái)解決分布式鎖的問(wèn)題,這里我們主要來(lái)看看如何通過(guò) Redis 解決分布式鎖問(wèn)題。
分布式鎖實(shí)現(xiàn)的思路很簡(jiǎn)單,就是進(jìn)來(lái)一個(gè)線城先占位,當(dāng)別的線城進(jìn)來(lái)操作時(shí),發(fā)現(xiàn)已經(jīng)有人占位了,就會(huì)放棄或者稍后再試。
在 Redis 中,占位一般使用 setnx 指令,先進(jìn)來(lái)的線程先占位,線程的操作執(zhí)行完成后,再調(diào)用 del 指令釋放位子。同時(shí)為了防止死鎖,我們一般還要給鎖加一個(gè)過(guò)期時(shí)間,到期了自動(dòng)釋放。
基于這樣的思路,我們來(lái)看兩種不同的實(shí)現(xiàn)方式:
基于我們前面所說(shuō)的思路,可以使用 setnx 和 expire 實(shí)現(xiàn)分布式鎖,但是 setnx 和設(shè)置過(guò)期時(shí)間 expire 這是兩個(gè)操作,這兩個(gè)操作一起的話就不具備原子性(除非自己寫(xiě) Lua 腳本),為了解決這個(gè)問(wèn)題,從 Redis2.8 開(kāi)始,setnx 和 expire 可以通過(guò)一個(gè)命令一起來(lái)執(zhí)行了,這個(gè)命令就是 set,set 中多了一個(gè)參數(shù):
從圖中大家可以看到,在 key/value 之后,還有一個(gè) EX 5 表示以秒計(jì)的過(guò)期時(shí)間(PX 表示以毫秒計(jì)的過(guò)期時(shí)間),最后的 NX 就是說(shuō)如果 k1 不存在,這條命令執(zhí)行成功,否則執(zhí)行失敗,這就相當(dāng)于 setnx 的效果了。
因此,我們封裝的鎖如下:
public class LockTest {
public static void main(String[] args) {
Redis redis = new Redis();
redis.execute(jedis->{
String set = jedis.set("k1", "v1", new SetParams().nx().ex(5));
if (set !=null && "OK".equals(set)) {
//沒(méi)人占位
jedis.set("name", "javaboy");
String name = jedis.get("name");
System.out.println(name);
jedis.del("k1");//釋放資源
}else{
//有人占位,停止/暫緩 操作
}
});
}
}對(duì)于上面這段代碼,大家重點(diǎn)看思路,不必深究代碼細(xì)節(jié):
但是這樣的封裝,又帶來(lái)了一個(gè)新的問(wèn)題,那就是超時(shí)問(wèn)題,關(guān)于超時(shí)問(wèn)題,松哥通過(guò)一個(gè)??視頻??教程來(lái)和大家分享。
上面的代碼寫(xiě)著還是蠻長(zhǎng)的,那么有沒(méi)有簡(jiǎn)單一點(diǎn)的辦法呢?當(dāng)然是有的!那就是 Redisson。
相對(duì)于 Jedis 這種原生態(tài)的應(yīng)用,Redisson 對(duì) Redis 請(qǐng)求做了較多的封裝,對(duì)于鎖,也提供了對(duì)應(yīng)的方法可以直接使用:
Config config = new Config();
//配置 Redis 基本連接信息
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123");
//獲取一個(gè) RedissonClient 對(duì)象
RedissonClient redisson = Redisson.create(config);
//獲取一個(gè)鎖對(duì)象實(shí)例
RLock lock = redisson.getLock("lock");
try {
//獲取鎖
boolean b = lock.tryLock(500, 1000, TimeUnit.MILLISECONDS);
if (b) {
//獲取到鎖了,開(kāi)始寫(xiě)業(yè)務(wù)
RBucket
在這段代碼中,核心的就是 lock.tryLock(500, 1000, TimeUnit.MILLISECONDS);,第一個(gè)參數(shù)是嘗試加鎖的等待時(shí)間為 500 毫秒,第二個(gè)參數(shù)表示鎖的超時(shí)時(shí)間為 1000 毫秒,也就是這個(gè)鎖在 1000 毫秒后會(huì)自動(dòng)失效。
小伙伴們發(fā)現(xiàn),這和我們?cè)诜桨敢焕镞吪渲玫膮?shù)是一樣的,其實(shí)思路是不變的,Redisson 只不過(guò)是將我們寫(xiě)的和鎖相關(guān)的方法封裝起來(lái)了而已。
當(dāng)然,這里我只是先簡(jiǎn)單介紹下加鎖的思路以及在 Redis 單機(jī)中如何加鎖,后面松哥再和大家分享 Redis 集群中如何加鎖。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢(xún)/運(yùn)營(yíng)咨詢(xún)/技術(shù)建議/互聯(lián)網(wǎng)交流