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

使用Redis實(shí)現(xiàn)分布式鎖與緩存策略方式

 更新時(shí)間:2025年11月25日 09:39:40   作者:百思可瑞教育  
文章介紹了Redis在分布式系統(tǒng)中實(shí)現(xiàn)分布式鎖和緩存策略的優(yōu)勢(shì),并詳細(xì)闡述了SETNX+EXPIRE、使用Lua腳本、SETEXPXNX命令和Redisson框架等幾種常見(jiàn)的分布式鎖實(shí)現(xiàn)方案,同時(shí),文章還探討了旁路緩存、緩存穿透、緩存雪崩和緩存擊穿等幾種緩存策略

一、引言

在分布式系統(tǒng)中,多個(gè)進(jìn)程或服務(wù)常常需要訪問(wèn)共享資源,為了保證數(shù)據(jù)的一致性和系統(tǒng)的穩(wěn)定性,需要引入分布式鎖機(jī)制。同時(shí),為了提高系統(tǒng)的性能和響應(yīng)速度,緩存策略也是必不可少的。

Redis憑借其原子性操作、內(nèi)存存儲(chǔ)、過(guò)期機(jī)制和分布式特性,成為實(shí)現(xiàn)分布式鎖和緩存策略的理想選擇。

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

(一)分布式鎖的意義

在單服務(wù)環(huán)境下,使用synchronized關(guān)鍵字可以保證線程安全,但在分布式系統(tǒng)中,多個(gè)節(jié)點(diǎn)訪問(wèn)同一個(gè)公共資源時(shí),synchronized就無(wú)法發(fā)揮作用。

分布式鎖能夠確保在任意時(shí)刻,只有一個(gè)客戶(hù)端能持有鎖,防止多個(gè)客戶(hù)端同時(shí)對(duì)共享資源進(jìn)行操作,從而保證數(shù)據(jù)的一致性。

(二)Redis實(shí)現(xiàn)分布式鎖的常見(jiàn)方案

SETNX + EXPIRE方案

原理

  • SETNXSET IF NOT EXISTS的簡(jiǎn)寫(xiě),即當(dāng)指定的鍵不存在時(shí),為其設(shè)置值。
  • 先使用SETNX命令嘗試獲取鎖,如果返回1,表示獲取成功,再使用EXPIRE命令為鎖設(shè)置一個(gè)過(guò)期時(shí)間,防止鎖忘記釋放導(dǎo)致死鎖。

代碼示例(Java)

import redis.clients.jedis.Jedis;

public class RedisLockExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        String key = "resource_lock";
        String value = "lock_value";
        int expireTime = 100; // 過(guò)期時(shí)間,單位秒

        if (jedis.setnx(key, value) == 1) {
            jedis.expire(key, expireTime);
            try {
                // 業(yè)務(wù)代碼
                System.out.println("獲取鎖成功,執(zhí)行業(yè)務(wù)邏輯");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jedis.del(key);
                System.out.println("釋放鎖");
            }
        } else {
            System.out.println("獲取鎖失敗");
        }
        jedis.close();
    }
}
- **缺點(diǎn)**:`SETNX`和`EXPIRE`兩個(gè)命令不是原子操作,如果在執(zhí)行完`SETNX`后,進(jìn)程崩潰或重啟,`EXPIRE`命令未執(zhí)行,鎖將無(wú)法釋放,導(dǎo)致其他客戶(hù)端永遠(yuǎn)無(wú)法獲取鎖。

SETNX + value(系統(tǒng)時(shí)間 + 過(guò)期時(shí)間)方案

原理

  • 把過(guò)期時(shí)間放在SETNXvalue值里。如果加鎖失敗,拿出value值校驗(yàn)是否過(guò)期。
  • 加鎖成功時(shí),將系統(tǒng)時(shí)間加上設(shè)置的過(guò)期時(shí)間作為value存入Redis。
  • 如果鎖已存在,獲取鎖的過(guò)期時(shí)間,若過(guò)期時(shí)間小于系統(tǒng)當(dāng)前時(shí)間,表示鎖已過(guò)期,通過(guò)getSet命令嘗試獲取鎖。

代碼示例(Java)

import redis.clients.jedis.Jedis;

public class RedisLockWithTimeExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        String key = "resource_lock";
        long expireTime = 10000; // 過(guò)期時(shí)間,單位毫秒

        long expires = System.currentTimeMillis() + expireTime;
        String expiresStr = String.valueOf(expires);

        if (jedis.setnx(key, expiresStr) == 1) {
            System.out.println("獲取鎖成功");
        } else {
            String currentValueStr = jedis.get(key);
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                String oldValueStr = jedis.getSet(key, expiresStr);
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    System.out.println("獲取鎖成功");
                } else {
                    System.out.println("獲取鎖失敗,其他線程已更新鎖");
                }
            } else {
                System.out.println("獲取鎖失敗,鎖未過(guò)期");
            }
        }
        jedis.close();
    }
}
- **缺點(diǎn)**:過(guò)期時(shí)間是客戶(hù)端自己生成的,依賴(lài)系統(tǒng)時(shí)間,要求分布式環(huán)境下每個(gè)客戶(hù)端的時(shí)間必須同步。鎖過(guò)期時(shí),并發(fā)多個(gè)客戶(hù)端同時(shí)請(qǐng)求,都執(zhí)行`getSet`,最終只有一個(gè)客戶(hù)端加鎖成功,但該客戶(hù)端鎖的過(guò)期時(shí)間可能被別的客戶(hù)端覆蓋。且該鎖沒(méi)有保存持有者的唯一標(biāo)識(shí),可能被別的客戶(hù)端釋放。

使用Lua腳本方案

原理

  • Lua腳本可以將一組Redis命令放在一次請(qǐng)求里完成,Redis會(huì)將腳本作為一個(gè)整體執(zhí)行,保證了原子性。
  • 通過(guò)Lua腳本實(shí)現(xiàn)SETNXEXPIRE兩條指令的原子操作。

代碼示例(Java)

import redis.clients.jedis.Jedis;
import java.util.Collections;

public class RedisLockWithLuaExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        String key = "resource_lock";
        String value = "lock_value";
        int expireTime = 100; // 過(guò)期時(shí)間,單位秒

        String luaScript = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
                "redis.call('expire', KEYS[1], ARGV[2]) " +
                "else " +
                "return 0 " +
                "end";

        Object result = jedis.eval(luaScript, Collections.singletonList(key),
                Collections.singletonList(value + "," + expireTime));

        if (result.equals(1L)) {
            System.out.println("獲取鎖成功");
            try {
                // 業(yè)務(wù)代碼
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jedis.del(key);
                System.out.println("釋放鎖");
            }
        } else {
            System.out.println("獲取鎖失敗");
        }
        jedis.close();
    }
}

SET的擴(kuò)展命令(SET EX PX NX)方案

原理

  • Redis的SET指令有擴(kuò)展參數(shù)[EX seconds][PX milliseconds][NX|XX],其中EX表示設(shè)置鍵的過(guò)期時(shí)間,單位為秒;PX表示設(shè)置鍵的過(guò)期時(shí)間,單位為毫秒;NX表示只有鍵不存在時(shí)才能設(shè)置成功。
  • 使用該命令可以原子性地完成設(shè)置鍵值和過(guò)期時(shí)間的操作。

代碼示例(Java)

import redis.clients.jedis.Jedis;

public class RedisSetLockExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        String key = "resource_lock";
        String value = "lock_value";
        int expireTime = 100; // 過(guò)期時(shí)間,單位秒

        String result = jedis.set(key, value, "NX", "EX", expireTime);

        if ("OK".equals(result)) {
            System.out.println("獲取鎖成功");
            try {
                // 業(yè)務(wù)代碼
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jedis.del(key);
                System.out.println("釋放鎖");
            }
        } else {
            System.out.println("獲取鎖失敗");
        }
        jedis.close();
    }
}

開(kāi)源框架Redisson方案

原理

  • Redisson是一個(gè)在Redis的基礎(chǔ)上實(shí)現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In - Memory Data Grid)框架,它提供了多種分布式鎖的實(shí)現(xiàn)。
  • 當(dāng)一個(gè)線程獲得鎖后,會(huì)開(kāi)啟一個(gè)定時(shí)守護(hù)線程,每隔一段時(shí)間檢查鎖是否還存在,若存在則延長(zhǎng)鎖的過(guò)期時(shí)間,防止鎖過(guò)期提前釋放。

代碼示例(Java)

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonLockExample {
    public static void main(String[] args) {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        RedissonClient redissonClient = Redisson.create(config);

        RLock lock = redissonClient.getLock("resource_lock");

        try {
            boolean isLocked = lock.tryLock(10, 30, java.util.concurrent.TimeUnit.SECONDS);
            if (isLocked) {
                System.out.println("獲取鎖成功");
                // 業(yè)務(wù)代碼
            } else {
                System.out.println("獲取鎖失敗");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
            redissonClient.shutdown();
        }
    }
}

三、Redis緩存策略

(一)旁路緩存(Cache - Aside)策略

工作原理

  • 由應(yīng)用層負(fù)責(zé)緩存和數(shù)據(jù)庫(kù)的交互邏輯。
  • 讀取數(shù)據(jù)時(shí),先查詢(xún)緩存,命中則直接返回;未命中則查詢(xún)數(shù)據(jù)庫(kù),將結(jié)果寫(xiě)入緩存并返回。
  • 更新數(shù)據(jù)時(shí),先更新數(shù)據(jù)庫(kù),再刪除緩存(或更新緩存)。

代碼示例(Java)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

@Service
public class UserServiceCacheAside {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private UserRepository userRepository;
    private static final String CACHE_KEY_PREFIX = "user:";
    private static final long CACHE_EXPIRATION = 30; // 緩存過(guò)期時(shí)間(分鐘)

    public User getUserById(Long userId) {
        String cacheKey = CACHE_KEY_PREFIX + userId;
        // 1. 查詢(xún)緩存
        User user = (User) redisTemplate.opsForValue().get(cacheKey);
        // 2. 緩存命中,直接返回
        if (user != null) {
            return user;
        }
        // 3. 緩存未命中,查詢(xún)數(shù)據(jù)庫(kù)
        user = userRepository.findById(userId).orElse(null);
        // 4. 將數(shù)據(jù)庫(kù)結(jié)果寫(xiě)入緩存(設(shè)置過(guò)期時(shí)間)
        if (user != null) {
            redisTemplate.opsForValue().set(cacheKey, user, CACHE_EXPIRATION, java.util.concurrent.TimeUnit.MINUTES);
        }
        return user;
    }

    public void updateUser(User user) {
        // 1. 先更新數(shù)據(jù)庫(kù)
        userRepository.save(user);
        // 2. 再刪除緩存
        String cacheKey = CACHE_KEY_PREFIX + user.getId();
        redisTemplate.delete(cacheKey);
    }
}

優(yōu)缺點(diǎn)分析

  • 優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,控制靈活;適合讀多寫(xiě)少的業(yè)務(wù)場(chǎng)景;只緩存必要的數(shù)據(jù),節(jié)省內(nèi)存空間。
  • 缺點(diǎn):首次訪問(wèn)會(huì)有一定延遲(緩存未命中);存在并發(fā)問(wèn)題,如果先刪除緩存后更新數(shù)據(jù)庫(kù),可能導(dǎo)致數(shù)據(jù)不一致;需要應(yīng)用代碼維護(hù)緩存一致性,增加了開(kāi)發(fā)復(fù)雜度。

適用場(chǎng)景

  • 讀多寫(xiě)少的業(yè)務(wù)場(chǎng)景;對(duì)數(shù)據(jù)一致性要求不是特別高的應(yīng)用;分布式系統(tǒng)中需要靈活控制緩存策略的場(chǎng)景。

(二)緩存穿透解決方案

  1. 緩存空值:當(dāng)查詢(xún)的數(shù)據(jù)在數(shù)據(jù)庫(kù)中不存在時(shí),也在Redis中存入一個(gè)空值,并設(shè)置一個(gè)較短的過(guò)期時(shí)間。這樣下次再查詢(xún)?cè)摂?shù)據(jù)時(shí),直接從緩存中返回空值,避免訪問(wèn)數(shù)據(jù)庫(kù)。
  2. 布隆過(guò)濾器:布隆過(guò)濾器是一種概率型數(shù)據(jù)結(jié)構(gòu),用于快速判斷一個(gè)元素是否存在于集合中。在訪問(wèn)緩存前,先通過(guò)布隆過(guò)濾器判斷數(shù)據(jù)是否存在,若不存在則直接返回,避免訪問(wèn)緩存和數(shù)據(jù)庫(kù)。

(三)緩存雪崩解決方案

  1. 設(shè)置不同的過(guò)期時(shí)間:為不同的緩存數(shù)據(jù)設(shè)置不同的過(guò)期時(shí)間,或者在過(guò)期時(shí)間上加上一個(gè)隨機(jī)數(shù),避免大量緩存數(shù)據(jù)同時(shí)過(guò)期。
  2. 部署高可用的Redis集群:通過(guò)主從節(jié)點(diǎn)的方式構(gòu)建Redis高可靠集群,如果主節(jié)點(diǎn)故障,從節(jié)點(diǎn)可以切換為主節(jié)點(diǎn)繼續(xù)提供服務(wù),同時(shí)完善監(jiān)控報(bào)警體系。

(四)緩存擊穿解決方案

  1. 互斥鎖:當(dāng)緩存失效時(shí),通過(guò)互斥鎖保證同一時(shí)間只有一個(gè)請(qǐng)求去構(gòu)建緩存,其他請(qǐng)求等待鎖釋放后再讀取緩存。
  2. 邏輯過(guò)期:將緩存數(shù)據(jù)的過(guò)期時(shí)間存儲(chǔ)在緩存中,當(dāng)緩存過(guò)期時(shí),不立即刪除緩存,而是啟動(dòng)一個(gè)后臺(tái)線程異步更新緩存。在讀取緩存時(shí),判斷緩存是否過(guò)期,若過(guò)期則返回舊數(shù)據(jù),并異步更新緩存。

四、總結(jié)

Redis在實(shí)現(xiàn)分布式鎖和緩存策略方面具有顯著的優(yōu)勢(shì)。通過(guò)多種分布式鎖實(shí)現(xiàn)方案,可以滿(mǎn)足不同場(chǎng)景下對(duì)鎖的要求,保證共享資源的原子性訪問(wèn)。同時(shí),合理的緩存策略能夠提高系統(tǒng)的性能和響應(yīng)速度,解決緩存穿透、雪崩和擊穿等問(wèn)題。在實(shí)際應(yīng)用中,應(yīng)根據(jù)具體的業(yè)務(wù)需求和系統(tǒng)特點(diǎn),選擇合適的分布式鎖實(shí)現(xiàn)方案和緩存策略,以構(gòu)建高效、穩(wěn)定的分布式系統(tǒng)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • redis-benchmark并發(fā)壓力測(cè)試的問(wèn)題解析

    redis-benchmark并發(fā)壓力測(cè)試的問(wèn)題解析

    這篇文章主要介紹了redis-benchmark并發(fā)壓力測(cè)試的問(wèn)題解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • 利用Redis?lua實(shí)現(xiàn)高效讀寫(xiě)鎖的代碼實(shí)例

    利用Redis?lua實(shí)現(xiàn)高效讀寫(xiě)鎖的代碼實(shí)例

    這篇文章給大家介紹了如何利用Redis?lua實(shí)現(xiàn)高效的讀寫(xiě)鎖,讀寫(xiě)鎖的好處就是能幫助客戶(hù)讀到的數(shù)據(jù)一定是最新的,寫(xiě)鎖是排他鎖,而讀鎖是一個(gè)共享鎖,需要的朋友可以參考下
    2024-01-01
  • Redis分布式鎖之紅鎖的實(shí)現(xiàn)

    Redis分布式鎖之紅鎖的實(shí)現(xiàn)

    本文主要介紹了Redis分布式鎖之紅鎖的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • Redis連接超時(shí)異常的處理方法

    Redis連接超時(shí)異常的處理方法

    這篇文章主要給大家介紹了關(guān)于Redis連接超時(shí)異常的處理方法,文中通過(guò)示例代碼以及圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 使用Spring?Boot實(shí)現(xiàn)Redis鍵過(guò)期回調(diào)功能示例詳解

    使用Spring?Boot實(shí)現(xiàn)Redis鍵過(guò)期回調(diào)功能示例詳解

    這篇文章主要介紹了使用Spring?Boot實(shí)現(xiàn)Redis鍵過(guò)期回調(diào)功能,就是一個(gè)實(shí)現(xiàn)Redis鍵過(guò)期回調(diào)功能的Spring?Boot應(yīng)用的示例,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • Linux環(huán)境下升級(jí)redis的詳細(xì)步驟記錄

    Linux環(huán)境下升級(jí)redis的詳細(xì)步驟記錄

    這篇文章主要給大家介紹了關(guān)于Linux環(huán)境下升級(jí)redis的詳細(xì)步驟,描述了如何從舊版本升級(jí)到新版本Redis,包括備份舊數(shù)據(jù)、下載和安裝新版本、復(fù)制配置文件和數(shù)據(jù)、停止舊版本并啟動(dòng)新版本的過(guò)程,需要的朋友可以參考下
    2024-12-12
  • Redis 的查詢(xún)很快的原因解析及Redis 如何保證查詢(xún)的高效

    Redis 的查詢(xún)很快的原因解析及Redis 如何保證查詢(xún)的高效

    由于redis是內(nèi)存數(shù)據(jù)庫(kù),歸功于它的數(shù)據(jù)結(jié)構(gòu)所以查詢(xún)效率非常高,今天通過(guò)本文給大家介紹下Redis 的查詢(xún)很快的原因解析及Redis 如何保證查詢(xún)的高效,感興趣的朋友一起看看吧
    2022-03-03
  • Redis 實(shí)現(xiàn)“附近的人”功能

    Redis 實(shí)現(xiàn)“附近的人”功能

    Redis基于geohash和有序集合提供了地理位置相關(guān)功能。這篇文章主要介紹了Redis 實(shí)現(xiàn)“附近的人”功能,需要的朋友可以參考下
    2019-11-11
  • Redis Cluster集群動(dòng)態(tài)擴(kuò)容的實(shí)現(xiàn)

    Redis Cluster集群動(dòng)態(tài)擴(kuò)容的實(shí)現(xiàn)

    本文主要介紹了Redis Cluster集群動(dòng)態(tài)擴(kuò)容的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07
  • redis全局命令set、get、keys使用方法

    redis全局命令set、get、keys使用方法

    這篇文章主要介紹了redis全局命令set、get、keys使用方法的相關(guān)資料,詳細(xì)講解了Redis的基本概念和常用命令,強(qiáng)調(diào)了在生產(chǎn)環(huán)境中避免使用keys命令的重要性,需要的朋友可以參考下
    2025-03-03

最新評(píng)論

汽车| 景谷| 稷山县| 霍邱县| 邵东县| 师宗县| 昌宁县| 莱阳市| 汽车| 西乌| 尼木县| 龙南县| 舒兰市| 巴楚县| 盐源县| 全椒县| 满洲里市| 平山县| 黄龙县| 洛宁县| 延庆县| 武定县| 黄石市| 柞水县| 漳平市| 疏勒县| 丰县| 雅安市| 松原市| 吉隆县| 襄垣县| 宁乡县| 哈巴河县| 昭通市| 壶关县| 日照市| 沙湾县| 盘山县| 那坡县| 贡山| 洞头县|