Redission實現(xiàn)分布式鎖

命令實現(xiàn)

首先我們看下redis普通的set命令,如下圖:
redis的set命令

我們可以看到,set命令后面出了key和value之外,還可以選擇EX或PX還有NX和XX,這分別代表什么意思呢,我們來看一下效果。

#ex和px代表過期時間,EX單位是秒,PX則是毫秒
>set test ceshi PX 1000
>set test ceshi EX 1
#NX表示互斥,鍵不存在則設(shè)置成功
>set test ceshi NX
>(nil)
#XX則是key存在則設(shè)置成功,key不存在設(shè)置失敗
>set test 1111 XX
>ok
#那么我們可以組合使用,實現(xiàn)分布式鎖的目的
>set key value EX 5 NX
>ok
#再次設(shè)置相同key時,會返回nil
>set key value EX 5 NX
>(nil)
#那么已經(jīng)設(shè)置的key是否可以追加時間呢,答案是可以的
>expire key 5
>pexpire key 5

這里需要提醒一下,redis經(jīng)常說的分布式鎖就是上面的方式實現(xiàn)的,而redis還提供了一個setnx命令,可以設(shè)置key,但是無法設(shè)置超時時間,是不推薦使用的,面試的時候也會經(jīng)常問到,需要大家避免踩坑。

#redis提供的setnx命令
>setnx test programmer
>(integer) 1
#如果key不存在會設(shè)置成功,并返回個1
>setnx test code
>(integer) 0

代碼實現(xiàn)

了解了代碼實現(xiàn)的原理,那我們來看下代碼如何實現(xiàn)分布式鎖的,話不多說上代碼。

    /**
     * 獲取redis鎖
     * @param lockKey      redis的key,就是鎖
     * @param requestId    redis的value,保證唯一性,例如訂單號、手機號
     * @param expireTime   過期時間,
     * @return
     */
    public boolean getLock(String lockKey,String requestId,int expireTime) {
        // ex表示過期時間是秒,nx保證key互斥,一條set命令保證原子性,防止并發(fā)
         String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
         if("OK".equals(result)) {
             return true;
         }
         return false;
    }

獲取到鎖之后,使用完成需要釋放鎖,當然也可以等待超時時間到期,自動釋放。這其中會涉及到一個問題,就是鎖超時釋放,但業(yè)務(wù)邏輯還未執(zhí)行完成。下一個業(yè)務(wù)進程已經(jīng)獲得了鎖,如果直接執(zhí)行del操作,會造成釋放掉了別人的鎖。那么我們就需要考慮使用lua腳本,來保證釋放鎖的并發(fā)。

    /**
     * 使用lua腳本釋放鎖
     * @param lockKey    redis的key
     * @param requestId  redis的value
     * @return
     */
    public static boolean releaseLock(String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (result.equals(1L)) {
            return true;
        }
        return false;
    }

問題

redis在單機的情況下是無法保證高可用的,在生產(chǎn)環(huán)境中我們一般會搭建redis的集群,而redis是AP模型,那么就會存在主從數(shù)據(jù)不同步,導(dǎo)致redis鎖重復(fù)獲得的問題。
當然正常情況下,在不需要保證業(yè)務(wù)數(shù)據(jù)強一致性時,就可以使用redis的分布式鎖;如果需要保證強一致性,則考慮使用其它方式,比如zookeeper等。

看門狗(Redission分布式鎖)

“看門狗”基于NIO的Netty框架實現(xiàn)的分布式鎖,多在生產(chǎn)環(huán)境中使用,接下來看代碼。
1、引入jar包

<dependency> 
    <groupId>org.redisson</groupId> 
    <artifactId>redisson</artifactId> 
    <version>2.7.0</version> 
</dependency>

2、配置Redisson

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonManager {
    
    @Bean
    public RedissonClient redisson(){
        Config config = new Config();
        ClusterServersConfig clusterServersConfig = config.useClusterServers()
                .setScanInterval(2000) // 集群狀態(tài)掃描,時間是毫秒
                .addNodeAddress("redis://127.0.0.1:6379")
                .addNodeAddress("redis://127.0.0.1:6380")
                .addNodeAddress("redis://127.0.0.1:6381")
                .addNodeAddress("redis://127.0.0.1:6382")
                .addNodeAddress("redis://127.0.0.1:6383")
                .addNodeAddress("redis://127.0.0.1:6384");
        return Redisson.create(config);
    }
}

3、使用

public void test(){
    RLock key = redissonClient.getLock("key");
    key.lock();
    // 業(yè)務(wù)代碼
    key.unlock();
}

總結(jié)

以上便是使用redis實現(xiàn)分布式鎖的過程,基本上可以滿足大部分業(yè)務(wù)需求,也希望大家了解redis分布式鎖的優(yōu)缺點。
最后,Redisson還有很多玩法,目前是一個比較成熟的redis分布式鎖框架,奉上git連接,供大家繼續(xù)學(xué)習(xí)。
https://github.com/redisson/redisson/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容