日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

本篇文章給大家?guī)砹岁P(guān)于redis的相關(guān)知識(shí),其中主要介紹了關(guān)于分布式鎖是什么?Redis又是怎么實(shí)現(xiàn)分布式鎖的?需要滿足什么條件?下面一起來看一下吧,希望對(duì)需要的朋友有幫助。


聊聊分布式鎖原理及Redis如何實(shí)現(xiàn)分布式鎖


一、分布式鎖基本原理

分布式鎖:滿足分布式系統(tǒng)或集群模式下多進(jìn)程可見并且互斥的鎖。

分布式鎖應(yīng)該滿足的條件:

可見性:多個(gè)線程都能看到相同的結(jié)果,注意:這個(gè)地方說的可見性并不是并發(fā)編程中指的內(nèi)存可見性,只是說多個(gè)進(jìn)程之間都能感知到變化的意思

互斥:互斥是分布式鎖的最基本的條件,使得程序串行執(zhí)行

高可用:程序不易崩潰,時(shí)時(shí)刻刻都保證較高的可用性

高性能:由于加鎖本身就讓性能降低,所有對(duì)于分布式鎖本身需要他就較高的加鎖性能和釋放鎖性能

安全性:安全也是程序中必不可少的一環(huán)

常見的分布式鎖有三種:

Mysql:mysql本身就帶有鎖機(jī)制,但是由于mysql性能本身一般,所以采用分布式鎖的情況下,其實(shí)使用mysql作為分布式鎖比較少見

Redis:redis作為分布式鎖是非常常見的一種使用方式,現(xiàn)在企業(yè)級(jí)開發(fā)中基本都使用redis或者zookeeper作為分布式鎖,利用setnx這個(gè)方法,如果插入key成功,則表示獲得到了鎖,如果有人插入成功,其他人插入失敗則表示無法獲得到鎖,利用這套邏輯來實(shí)現(xiàn)分布式鎖

Zookeeper:zookeeper也是企業(yè)級(jí)開發(fā)中較好的一個(gè)實(shí)現(xiàn)分布式鎖的方案


聊聊分布式鎖原理及Redis如何實(shí)現(xiàn)分布式鎖


二、基于Redis實(shí)現(xiàn)分布式鎖

實(shí)現(xiàn)分布式鎖時(shí)需要實(shí)現(xiàn)的兩個(gè)基本方法:

獲取鎖:

互斥:確保只能有一個(gè)線程獲取鎖

非阻塞:嘗試一次,成功返回true,失敗返回false

釋放鎖:

手動(dòng)釋放

超時(shí)釋放:獲取鎖時(shí)添加一個(gè)超時(shí)時(shí)間

基于Redis實(shí)現(xiàn)分布式鎖原理:

SET resource_name my_random_value NX PX 30000

resource_name:資源名稱,可根據(jù)不同的業(yè)務(wù)區(qū)分不同的鎖

my_random_value:隨機(jī)值,每個(gè)線程的隨機(jī)值都不同,用于釋放鎖時(shí)的校驗(yàn)

NX:key不存在時(shí)設(shè)置成功,key存在則設(shè)置不成功

PX:自動(dòng)失效時(shí)間,出現(xiàn)異常情況,鎖可以過期失效

利用NX的原子性,多個(gè)線程并發(fā)時(shí),只有一個(gè)線程可以設(shè)置成功,設(shè)置成功表示獲得鎖,可以執(zhí)行后續(xù)的業(yè)務(wù)處理;如果出現(xiàn)異常,過了鎖的有效期,鎖自動(dòng)釋放;


版本一

1、定義ILock接口

public interface ILock extends AutoCloseable {
    /**
     * 嘗試獲取鎖
     *
     * @param timeoutSec 鎖持有的超時(shí)時(shí)間,過期后自動(dòng)釋放
     * @return true代表獲取鎖成功;false代表獲取鎖失敗
     */
    boolean tryLock(long timeoutSec);
 
    /**
     * 釋放鎖
     * @return
     */
    void unLock();
}

2、基于Redis實(shí)現(xiàn)分布式鎖—RedisLock

public class SimpleRedisLock {
    private final StringRedisTemplate stringRedisTemplate;
    private final String name;
 
    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.name = name;
    }
 
    private static final String KEY_PREFIX = "lock:";
 
    @Override
    public boolean tryLock(long timeoutSec) {
        //獲取線程標(biāo)識(shí)
        String threadId = Thread.currentThread().getId();
        //獲取鎖
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
 
    @Override
    public void unLock() {
        //通過del刪除鎖
        stringRedisTemplate.delete(KEY_PREFIX + name);
    }
 
    @Override
    public void close() {
        unLock();
    }
}


鎖誤刪問題

問題說明:

持有鎖的線程1在鎖的內(nèi)部出現(xiàn)了阻塞,這時(shí)鎖超時(shí)自動(dòng)釋放,這時(shí)線程2嘗試獲得鎖,然后線程2在持有鎖執(zhí)行過程中,線程1反應(yīng)過來,繼續(xù)執(zhí)行,走到了刪除鎖邏輯,此時(shí)就會(huì)把本應(yīng)該屬于線程2的鎖進(jìn)行刪除,這就是鎖誤刪的情況。

解決方案:

在存入鎖時(shí),放入自己線程的標(biāo)識(shí),在刪除鎖時(shí),判斷當(dāng)前這把鎖的標(biāo)識(shí)是不是自己存入的,如果是,則進(jìn)行刪除,如果不是,則不進(jìn)行刪除。


版本二:解決鎖誤刪問題

public class SimpleRedisLock {
    private final StringRedisTemplate stringRedisTemplate;
    private final String name;
 
    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.name = name;
    }
 
    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
 
    @Override
    public boolean tryLock(long timeoutSec) {
        //獲取線程標(biāo)識(shí)
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //獲取鎖
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
 
    @Override
    public void unLock() {
        // 獲取線程標(biāo)示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 獲取鎖中的標(biāo)示
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        // 判斷標(biāo)示是否一致
        if(threadId.equals(id)) {
            // 釋放鎖
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }
    }
 
    @Override
    public void close() {
        unLock();
    }
}


鎖釋放的原子性問題

問題分析:

上述釋放鎖的代碼依然存在鎖誤刪問題,當(dāng)線程1獲取鎖中的線程標(biāo)識(shí),并根據(jù)標(biāo)識(shí)判斷是自己的鎖,這時(shí)鎖到期自動(dòng)釋放,恰好線程2嘗試獲取鎖,并拿到了鎖,此時(shí)線程1依然執(zhí)行釋放鎖的操作,就導(dǎo)致誤刪了線程2持有的鎖。

原因在于,由java代碼實(shí)現(xiàn)的釋放鎖流程不是原子操作,存在線程安全問題。

解決方案:

Redis提供了Lua腳本功能,在一個(gè)腳本中編寫多條Redis命令,可以確保多條命令執(zhí)行時(shí)的原子性。


版本三:調(diào)用Lua腳本改造分布式鎖

public class SimpleRedisLock implements ILock {
    private final StringRedisTemplate stringRedisTemplate;
    private final String name;
 
    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.name = name;
    }
 
    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
 
    @Override
    public boolean tryLock(long timeoutSec) {
        //獲取線程標(biāo)識(shí)
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //獲取鎖
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
 
    @Override
    public void unLock() {
        String script = "if redis.call("get",KEYS[1]) == ARGV[1] then\n" +
                " return redis.call("del",KEYS[1])\n" +
                "else\n" +
                " return 0\n" +
                "end";
        //通過執(zhí)行l(wèi)ua腳本實(shí)現(xiàn)鎖刪除,可以校驗(yàn)隨機(jī)值
        RedisScript<Boolean> redisScript = RedisScript.of(script, Boolean.class);
        stringRedisTemplate.execute(redisScript,
                Collections.singletonList(KEY_PREFIX + name),
                ID_PREFIX + Thread.currentThread().getId());
    }
 
    @Override
    public void close() {
        unLock();
    }
}


分享到:
標(biāo)簽:Redis分布式鎖 Redis如何實(shí)現(xiàn)分布式鎖
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定