最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis超詳細(xì)分析分布式鎖

 更新時(shí)間:2022年07月27日 15:52:04   作者:kaico2018  
在單體應(yīng)用中,如果我們對(duì)共享數(shù)據(jù)不進(jìn)行加鎖操作,會(huì)出現(xiàn)數(shù)據(jù)一致性問(wèn)題,我們的解決辦法通常是加鎖。下面我們一起聊聊使用redis來(lái)實(shí)現(xiàn)分布式鎖

分布式鎖

為了保證一個(gè)方法在高并發(fā)情況下的同一時(shí)間只能被同一個(gè)線程執(zhí)行,在傳統(tǒng)單體應(yīng)用單機(jī)部署的情況下,可以使用Java并發(fā)處理相關(guān)的API(如ReentrantLcok或synchronized)進(jìn)行互斥控制。但是,隨著業(yè)務(wù)發(fā)展的需要,原單體單機(jī)部署的系統(tǒng)被演化成分布式系統(tǒng)后,由于分布式系統(tǒng)多線程、多進(jìn)程并且分布在不同機(jī)器上,這將使原單機(jī)部署情況下的并發(fā)控制鎖策略失效,為了解決這個(gè)問(wèn)題就需要一種跨JVM的互斥機(jī)制來(lái)控制共享資源的訪問(wèn),這就是分布式鎖要解決的問(wèn)題。

應(yīng)用場(chǎng)景

1、處理效率提升:應(yīng)用分布式鎖,可以減少重復(fù)任務(wù)的執(zhí)行,避免資源處理效率的浪費(fèi);

2、數(shù)據(jù)準(zhǔn)確性保障:使用分布式鎖可以放在數(shù)據(jù)資源的并發(fā)訪問(wèn),避免數(shù)據(jù)不一致情況,甚至數(shù)據(jù)損失等。

例如:

分布式任務(wù)調(diào)度平臺(tái)保證任務(wù)的冪等性。

分布式全局id的生成

使用Redis 實(shí)現(xiàn)分布式鎖

思路:Redis實(shí)現(xiàn)分布式鎖基于SetNx命令,因?yàn)樵赗edis中key是保證是唯一的。所以當(dāng)多個(gè)線程同時(shí)的創(chuàng)建setNx時(shí),只要誰(shuí)能夠創(chuàng)建成功誰(shuí)就能夠獲取到鎖。

Set 命令: 每次 set 時(shí),可以修改原來(lái)舊值;

SetNx命令:每次SetNx檢查該 key是否已經(jīng)存在,如果已經(jīng)存在的話不會(huì)執(zhí)行任何操作。返回為0 如果已經(jīng)不存在的話直接新增該key。

1:新增key成功, 0:失敗

獲取鎖的時(shí)候:當(dāng)多個(gè)線程同時(shí)創(chuàng)建SetNx k,只要誰(shuí)能夠創(chuàng)建成功誰(shuí)就能夠獲取到鎖。

釋放鎖:可以對(duì)該key設(shè)置一個(gè)有效期可以避免死鎖的現(xiàn)象。

單機(jī)版Redis實(shí)現(xiàn)分布式鎖

使用原生Jedis實(shí)現(xiàn)

1、增加maven依賴

<dependency>
   <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

2、編寫(xiě)Jedis連接Redis工具類

public class RedisClientUtil {
    //protected static Logger logger = Logger.getLogger(RedisUtil.class);
    private static String IP = "www.kaicostudy.com";
    //Redis的端口號(hào)
    private static int PORT = 6379;
    //可用連接實(shí)例的最大數(shù)目,默認(rèn)值為8;
    //如果賦值為-1,則表示不限制;如果pool已經(jīng)分配了maxActive個(gè)jedis實(shí)例,則此時(shí)pool的狀態(tài)為exhausted(耗盡)。
    private static int MAX_ACTIVE = 100;
    //控制一個(gè)pool最多有多少個(gè)狀態(tài)為idle(空閑的)的jedis實(shí)例,默認(rèn)值也是8。
    private static int MAX_IDLE = 20;
    //等待可用連接的最大時(shí)間,單位毫秒,默認(rèn)值為-1,表示永不超時(shí)。如果超過(guò)等待時(shí)間,則直接拋出JedisConnectionException;
    private static int MAX_WAIT = 3000;
    private static int TIMEOUT = 3000;
    //在borrow一個(gè)jedis實(shí)例時(shí),是否提前進(jìn)行validate操作;如果為true,則得到的jedis實(shí)例均是可用的;
    private static boolean TEST_ON_BORROW = true;
    //在return給pool時(shí),是否提前進(jìn)行validate操作;
    private static boolean TEST_ON_RETURN = true;
    private static JedisPool jedisPool = null;
    /**
     * redis過(guò)期時(shí)間,以秒為單位
     */
    public final static int EXRP_HOUR = 60 * 60; //一小時(shí)
    public final static int EXRP_DAY = 60 * 60 * 24; //一天
    public final static int EXRP_MONTH = 60 * 60 * 24 * 30; //一個(gè)月
    /**
     * 初始化Redis連接池
     */
    private static void initialPool() {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);
            jedisPool = new JedisPool(config, IP, PORT, TIMEOUT, "123456");
        } catch (Exception e) {
            //logger.error("First create JedisPool error : "+e);
            e.getMessage();
        }
    }
    /**
     * 在多線程環(huán)境同步初始化
     */
    private static synchronized void poolInit() {
        if (jedisPool == null) {
            initialPool();
        }
    }
    /**
     * 同步獲取Jedis實(shí)例
     *
     * @return Jedis
     */
    public synchronized static Jedis getJedis() {
        if (jedisPool == null) {
            poolInit();
        }
        Jedis jedis = null;
        try {
            if (jedisPool != null) {
                jedis = jedisPool.getResource();
            }
        } catch (Exception e) {
            e.getMessage();
            // logger.error("Get jedis error : "+e);
        }
        return jedis;
    }
    /**
     * 釋放jedis資源
     *
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        if (jedis != null && jedisPool != null) {
            jedisPool.returnResource(jedis);
        }
    }
    public static Long sadd(String key, String... members) {
        Jedis jedis = null;
        Long res = null;
        try {
            jedis = getJedis();
            res = jedis.sadd(key, members);
        } catch (Exception e) {
            //logger.error("sadd  error : "+e);
            e.getMessage();
        }
        return res;
    }
}

3、編寫(xiě)Redis鎖的工具類

public class RedisLock {
    private static final int setnxSuccss = 1;
    /**
     * 獲取鎖
     *
     * @param lockKey        定義鎖的key
     * @param notLockTimeOut 沒(méi)有獲取鎖的超時(shí)時(shí)間
     * @param lockTimeOut    使用鎖的超時(shí)時(shí)間
     * @return
     */
    public String getLock(String lockKey, int notLockTimeOut, int lockTimeOut) {
        // 獲取Redis連接
        Jedis jedis = RedisClientUtil.getJedis();
        // 定義沒(méi)有獲取鎖的超時(shí)時(shí)間
        Long endTimeOut = System.currentTimeMillis() + notLockTimeOut;
        while (System.currentTimeMillis() < endTimeOut) {
            String lockValue = UUID.randomUUID().toString();
            // 如果在多線程情況下誰(shuí)能夠setnx 成功返回0 誰(shuí)就獲取到鎖
            if (jedis.setnx(lockKey, lockValue) == setnxSuccss) {
                jedis.expire(lockKey, lockTimeOut / 1000);
                return lockValue;
            }
            // 否則情況下 在超時(shí)時(shí)間內(nèi)繼續(xù)循環(huán)
        }
        try {
            if (jedis != null) {
                jedis.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 釋放鎖 其實(shí)就是將該key刪除
     *
     * @return
     */
    public Boolean unLock(String lockKey, String lockValue) {
        Jedis jedis = RedisClientUtil.getJedis();
        // 確定是對(duì)應(yīng)的鎖 ,才刪除
        if (lockValue.equals(jedis.get(lockKey))) {
            return jedis.del(lockKey) > 0 ? true : false;
        }
        return false;
    }
}

4、測(cè)試方法

 private RedisLock redisLock = new RedisLock();
 private String lockKey = "kaico_lock";
 /**
  * 測(cè)試Jedis實(shí)現(xiàn)分布式鎖
  * @return
  */
 @GetMapping("/restLock1")
 public String restLock1(){
     // 1.獲取鎖
     String lockValue = redisLock.getLock(lockKey, 5000, 5000);
     if (StringUtils.isEmpty(lockValue)) {
         System.out.println(Thread.currentThread().getName() + ",獲取鎖失敗!");
         return "獲取鎖失敗";
     }
     // 2.獲取鎖成功執(zhí)行業(yè)務(wù)邏輯
     System.out.println(Thread.currentThread().getName() + ",獲取成功,lockValue:" + lockValue);
     // 3.釋放lock鎖
     redisLock.unLock(lockKey, lockValue);
     return "";
 }

使用Springboot實(shí)現(xiàn)

依賴于之前的項(xiàng)目

1、編寫(xiě)鎖的工具類方法

@Component
public class SpringbootRedisLockUtil {
    @Autowired
    public RedisTemplate redisTemplate;
    //    解鎖原子性操作腳本
    public static final String unlockScript="if redis.call(\"get\",KEYS[1]) == ARGV[1]\n"
            + "then\n"
            + "    return redis.call(\"del\",KEYS[1])\n"
            + "else\n"
            + "    return 0\n"
            + "end";
    /**
     * 加鎖,有阻塞
     * @param name
     * @param expire
     * @param timeout
     * @return
     */
    public String lock(String name, long expire, long timeout) throws UnsupportedEncodingException {
        long startTime=System.currentTimeMillis();
        String token;
        do{
            token=tryLock(name,expire);
            if(token==null){
                //設(shè)置等待時(shí)間,若等待時(shí)間過(guò)長(zhǎng)則獲取鎖失敗
                if((System.currentTimeMillis()-startTime)>(timeout-50)){
                    break;
                }
                try {
                    Thread.sleep(50);//try it again per 50
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }while (token==null);
        return token;
    }
    /**
     * 解鎖
     * @param name
     * @param token
     * @return
     */
    public Boolean unlock(String name, String token) throws UnsupportedEncodingException {
        byte[][] keyArgs=new byte[2][];
        keyArgs[0]= name.getBytes(Charset.forName("UTF-8"));
        keyArgs[1]= token.getBytes(Charset.forName("UTF-8"));
        RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
        RedisConnection connection = connectionFactory.getConnection();
        try{
            Long result = connection.scriptingCommands().eval(unlockScript.getBytes(Charset.forName("UTF-8")), ReturnType.INTEGER, 1, keyArgs);
            if(result!=null&&result>0){
                return true;
            }

        }finally {
            RedisConnectionUtils.releaseConnection(connection,connectionFactory);
        }
        return false;
    }
    /**
     * 加鎖,無(wú)阻塞
     * @param name
     * @param expire
     * @return
     */
    public String tryLock(String name, long expire) throws UnsupportedEncodingException {
        String token= UUID.randomUUID().toString();
        RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
        RedisConnection connection = connectionFactory.getConnection();
        try{
            Boolean result = connection.set(name.getBytes(Charset.forName("UTF-8")), token.getBytes(Charset.forName("UTF-8")),
                    Expiration.from(expire, TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.SET_IF_ABSENT);
            if(result!=null&&result){
                return token;
            }
        }
        finally {
            RedisConnectionUtils.releaseConnection(connection,connectionFactory);
        }
        return null;
    }
}

2、測(cè)試類

		@Autowired
    private SpringbootRedisLockUtil springbootRedisLockUtil;
    @PostMapping("/restLock1")
    public void restLock2() throws UnsupportedEncodingException {
        String token;
        token=springbootRedisLockUtil.lock(Thread.currentThread().getName(),1000,11000);
        if(token!=null){
            System.out.println("我拿到鎖了哦!");
        }
        else{
            System.out.println("我沒(méi)有拿到鎖!");
        }
        springbootRedisLockUtil.unlock(Thread.currentThread().getName(),token);
    }

到此這篇關(guān)于Redis超詳細(xì)分析分布式鎖的文章就介紹到這了,更多相關(guān)Redis分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • redis發(fā)布訂閱_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    redis發(fā)布訂閱_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了redis發(fā)布訂閱,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • Redis 哨兵與集群腦裂問(wèn)題及其解決

    Redis 哨兵與集群腦裂問(wèn)題及其解決

    本文主要介紹了Redis 哨兵與集群腦裂問(wèn)題及其解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • Redis 緩存擊穿問(wèn)題及解決方案

    Redis 緩存擊穿問(wèn)題及解決方案

    緩存擊穿是指在高并發(fā)環(huán)境下,大量請(qǐng)求同時(shí)訪問(wèn)緩存中不存在的數(shù)據(jù),導(dǎo)致這些請(qǐng)求穿透到數(shù)據(jù)庫(kù),本文主要介紹了Redis緩存擊穿問(wèn)題及解決方案
    2023-12-12
  • Redis中Redisson紅鎖(Redlock)使用原理

    Redis中Redisson紅鎖(Redlock)使用原理

    本文主要介紹了Redis中Redisson紅鎖(Redlock)使用原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • macOS上Redis的安裝與測(cè)試操作

    macOS上Redis的安裝與測(cè)試操作

    這篇文章主要介紹了macOS上Redis的安裝與測(cè)試操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-07-07
  • Redis實(shí)現(xiàn)延遲隊(duì)列的項(xiàng)目示例

    Redis實(shí)現(xiàn)延遲隊(duì)列的項(xiàng)目示例

    延遲隊(duì)列是Redis的一個(gè)重要應(yīng)用場(chǎng)景,本文主要介紹了Redis實(shí)現(xiàn)延遲隊(duì)列的項(xiàng)目示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • 詳解Redis如何優(yōu)雅地實(shí)現(xiàn)接口防刷

    詳解Redis如何優(yōu)雅地實(shí)現(xiàn)接口防刷

    這篇文章主要為大家詳細(xì)介紹了Redis優(yōu)雅地實(shí)現(xiàn)接口防刷的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Redis的KEYS 命令千萬(wàn)不能亂用

    Redis的KEYS 命令千萬(wàn)不能亂用

    這篇文章主要介紹了Redis的KEYS 命令千萬(wàn)不能亂用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Redis使用Bitmap的方法實(shí)現(xiàn)

    Redis使用Bitmap的方法實(shí)現(xiàn)

    本文主要介紹了Redis使用Bitmap的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 在CentOS 7環(huán)境下安裝Redis數(shù)據(jù)庫(kù)詳解

    在CentOS 7環(huán)境下安裝Redis數(shù)據(jù)庫(kù)詳解

    Redis是一個(gè)開(kāi)源的、基于BSD許可證的,基于內(nèi)存的、鍵值存儲(chǔ)NoSQL數(shù)據(jù)本篇文章主要介紹了在CentOS 7環(huán)境下安裝Redis數(shù)據(jù)庫(kù)詳解,有興趣的可以了解一下。
    2016-11-11

最新評(píng)論

巴东县| 如皋市| 綦江县| 石泉县| 平顺县| 岚皋县| 乌海市| 星子县| 云浮市| 文山县| 千阳县| 平度市| 大同县| 成都市| 于都县| 崇明县| 彝良县| 乌拉特前旗| 区。| 尉氏县| 会同县| 集安市| 乐都县| 兴海县| 海伦市| 隆安县| 沙河市| 华宁县| 长岭县| 运城市| 广西| 张家口市| 福州市| 图们市| 浦江县| 兖州市| 无极县| 天气| 新田县| 旌德县| 邛崃市|