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

Redis鎖與DB鎖的使用與區(qū)別小結(jié)

 更新時(shí)間:2026年03月06日 09:44:55   作者:AlbenXie  
本文主要介紹了Redis鎖與DB鎖的使用與區(qū)別小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、Redis 鎖與 DB 鎖的對(duì)比

Redis 鎖(分布式鎖)和 DB 鎖(數(shù)據(jù)庫(kù)鎖)是分布式場(chǎng)景下控制并發(fā)的核心手段,二者在實(shí)現(xiàn)方式、性能、可靠性、適用場(chǎng)景上差異顯著:

維度

Redis 鎖(如 SETNX/Redlock)

DB 鎖(如行鎖 / 表鎖 / 唯一索引)

實(shí)現(xiàn)方式

1. 基礎(chǔ)版:SETNX key value EX 過(guò)期時(shí)間(單節(jié)點(diǎn))

2. 高級(jí)版:Redlock(多節(jié)點(diǎn) Redis 集群)

1. 行鎖:SELECT FOR UPDATE(悲觀鎖)、版本號(hào)(樂(lè)觀鎖)

2. 表鎖:LOCK TABLES

3. 唯一索引:通過(guò)唯一約束實(shí)現(xiàn)分布式鎖

性能

極高(內(nèi)存操作,QPS 可達(dá) 10 萬(wàn) +),加鎖 / 解鎖耗時(shí)微秒級(jí)

較低(磁盤(pán) IO + 事務(wù)開(kāi)銷(xiāo)),加鎖 / 解鎖耗時(shí)毫秒級(jí),高并發(fā)下易成為瓶頸

可靠性

1. 單節(jié)點(diǎn):Redis 宕機(jī)則鎖失效(需設(shè)置過(guò)期時(shí)間兜底)

2. Redlock:多節(jié)點(diǎn)降低宕機(jī)風(fēng)險(xiǎn),但仍存在時(shí)鐘漂移問(wèn)題

高(數(shù)據(jù)庫(kù)事務(wù) ACID 保障,宕機(jī)后恢復(fù)數(shù)據(jù)不丟失),鎖由數(shù)據(jù)庫(kù)事務(wù)機(jī)制保障

鎖粒度

粗粒度(按 key 鎖,可自定義粒度,如用戶 ID、訂單號(hào))

細(xì)粒度(行鎖可鎖定單條記錄,表鎖粒度最粗)

過(guò)期機(jī)制

支持自動(dòng)過(guò)期(EX 參數(shù)),可防止死鎖

1. 悲觀鎖:依賴事務(wù)提交 / 回滾釋放,若事務(wù)卡住則死鎖

2. 樂(lè)觀鎖:無(wú)過(guò)期,靠版本號(hào)控制

分布式支持

天然支持分布式(Redis 集群),跨服務(wù) / 跨機(jī)器

支持分布式(數(shù)據(jù)庫(kù)主從 / 集群),但跨庫(kù)鎖需額外處理(如 XA 事務(wù))

死鎖風(fēng)險(xiǎn)

低(過(guò)期時(shí)間自動(dòng)釋放),但可能出現(xiàn)鎖過(guò)期導(dǎo)致并發(fā)問(wèn)題(如業(yè)務(wù)處理時(shí)間超過(guò)過(guò)期時(shí)間)

高(悲觀鎖),需依賴數(shù)據(jù)庫(kù)死鎖檢測(cè)機(jī)制自動(dòng)解除,或人工干預(yù)

適用場(chǎng)景

1. 高并發(fā)場(chǎng)景(如秒殺、支付)

2. 非核心數(shù)據(jù)的并發(fā)控制

3. 短期鎖(業(yè)務(wù)處理時(shí)間短)

1. 低并發(fā)場(chǎng)景

2. 核心數(shù)據(jù)的并發(fā)控制(如資金變動(dòng))

3. 長(zhǎng)期鎖(業(yè)務(wù)處理時(shí)間長(zhǎng))

4. 需要事務(wù)保障的場(chǎng)景

實(shí)現(xiàn)復(fù)雜度

中等(需處理鎖過(guò)期、重入、釋放別人的鎖等問(wèn)題,可使用 Redisson 框架)

低(悲觀鎖直接用 SELECT FOR UPDATE,唯一索引只需建表)

二、舉例說(shuō)明

我們通過(guò)電商秒殺場(chǎng)景金融資金扣減場(chǎng)景兩個(gè)典型案例,結(jié)合代碼實(shí)現(xiàn)和問(wèn)題分析,深入對(duì)比 Redis 鎖與 DB 鎖的差異、適用場(chǎng)景及坑點(diǎn)。

先明確核心概念

  • Redis 鎖:基于 Redis 的內(nèi)存操作實(shí)現(xiàn)的分布式鎖,核心是SETNX(SET if Not Exists)指令,本質(zhì)是非持久化的分布式鎖(單節(jié)點(diǎn)),可通過(guò) Redlock/Redisson 實(shí)現(xiàn)高可用。
  • DB 鎖:基于數(shù)據(jù)庫(kù)的鎖機(jī)制,常見(jiàn)的有悲觀行鎖(SELECT FOR UPDATE)、樂(lè)觀鎖(版本號(hào))唯一索引鎖,本質(zhì)是持久化的鎖,依賴數(shù)據(jù)庫(kù)事務(wù) ACID 保障。

案例 1:電商秒殺場(chǎng)景(高并發(fā)、短事務(wù))

業(yè)務(wù)背景

某電商平臺(tái)秒殺 iPhone,庫(kù)存只有 100 臺(tái),每秒有 10 萬(wàn) + 請(qǐng)求,需要控制并發(fā)下單,防止超賣(mài)。

方案 1:使用 Redis 鎖實(shí)現(xiàn)

1. 核心實(shí)現(xiàn)(基于 Redisson,解決原生 Redis 鎖的坑)

Redisson 是 Redis 的 Java 客戶端,封裝了分布式鎖的實(shí)現(xiàn),自動(dòng)處理鎖過(guò)期、重入、釋放別人的鎖、集群高可用等問(wèn)題。

@Service
public class SeckillService {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private SeckillMapper seckillMapper;
    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;
 
    // 秒殺核心方法
    public String seckill(String productId, String userId) {
        // 1. 定義Redis鎖key(粒度:商品ID,確保同一商品的秒殺串行)
        String lockKey = "seckill_lock:" + productId;
        RLock lock = redissonClient.getLock(lockKey);
 
        try {
            // 2. 獲取鎖(等待時(shí)間10秒,鎖自動(dòng)過(guò)期30秒,防止死鎖)
            boolean lockSuccess = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (!lockSuccess) {
                return "秒殺太火爆了,請(qǐng)稍后重試!";
            }
 
            // 3. 業(yè)務(wù)邏輯:先查Redis庫(kù)存(緩存),再扣減,最后同步到DB
            Integer stock = redisTemplate.opsForValue().get("seckill_stock:" + productId);
            if (stock == null || stock <= 0) {
                return "秒殺已結(jié)束!";
            }
            // 扣減Redis庫(kù)存
            redisTemplate.opsForValue().decrement("seckill_stock:" + productId);
            // 生成訂單(異步寫(xiě)入DB,提升性能)
            seckillMapper.createOrder(productId, userId);
            return "秒殺成功!";
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "秒殺失敗,請(qǐng)重試!";
        } finally {
            // 4. 釋放鎖(只有持有鎖的線程才能釋放)
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

2. Redis 鎖在秒殺場(chǎng)景的優(yōu)勢(shì)

  • 性能極高:Redis 是內(nèi)存數(shù)據(jù)庫(kù),tryLock/unlock操作耗時(shí)微秒級(jí),能支撐 10 萬(wàn) + QPS 的并發(fā)請(qǐng)求,而 DB 鎖在高并發(fā)下會(huì)因磁盤(pán) IO 和事務(wù)開(kāi)銷(xiāo)導(dǎo)致性能瓶頸。
  • 鎖粒度靈活:按商品 ID 作為鎖 key,只鎖定當(dāng)前秒殺的商品,其他商品的秒殺不受影響(細(xì)粒度鎖),而 DB 表鎖會(huì)鎖定整個(gè)庫(kù)存表,導(dǎo)致所有商品秒殺阻塞。
  • 自動(dòng)過(guò)期防死鎖:設(shè)置鎖過(guò)期時(shí)間,即使秒殺服務(wù)宕機(jī),鎖也會(huì)自動(dòng)釋放,不會(huì)導(dǎo)致死鎖;而 DB 悲觀鎖若事務(wù)卡住,會(huì)一直持有鎖,直到數(shù)據(jù)庫(kù)超時(shí)。

3. Redis 鎖的坑點(diǎn)及解決

  • 鎖過(guò)期問(wèn)題:若秒殺業(yè)務(wù)處理時(shí)間超過(guò) 30 秒(如 DB 寫(xiě)入卡頓),鎖會(huì)提前過(guò)期,導(dǎo)致其他線程獲取鎖,可能出現(xiàn)超賣(mài)。
    • 解決:Redisson 的自動(dòng)續(xù)期機(jī)制(看門(mén)狗),持有鎖的線程會(huì)每隔 10 秒自動(dòng)將鎖過(guò)期時(shí)間延長(zhǎng)至 30 秒,直到線程釋放鎖。
  • Redis 單節(jié)點(diǎn)宕機(jī):若 Redis 主節(jié)點(diǎn)宕機(jī),鎖數(shù)據(jù)丟失,多個(gè)線程同時(shí)獲取鎖,導(dǎo)致超賣(mài)。
    • 解決:使用Redlock 算法(部署多個(gè) Redis 節(jié)點(diǎn),線程需獲取半數(shù)以上節(jié)點(diǎn)的鎖才算成功)或Redis 主從 + 哨兵(主節(jié)點(diǎn)宕機(jī)后從節(jié)點(diǎn)自動(dòng)切換,減少鎖丟失概率)。

方案 2:使用 DB 鎖(SELECT FOR UPDATE)實(shí)現(xiàn)

1. 核心實(shí)現(xiàn)

@Service
public class SeckillService {
    @Autowired
    private SeckillMapper seckillMapper;
 
    // 秒殺核心方法(事務(wù)內(nèi)執(zhí)行)
    @Transactional(rollbackFor = Exception.class)
    public String seckill(String productId, String userId) {
        // 1. 獲取DB行鎖(根據(jù)商品ID鎖定庫(kù)存記錄,悲觀鎖)
        SeckillStock stock = seckillMapper.selectStockByProductIdForUpdate(productId);
        if (stock == null || stock.getStock() <= 0) {
            return "秒殺已結(jié)束!";
        }
 
        // 2. 扣減庫(kù)存
        seckillMapper.decrementStock(productId);
        // 3. 生成訂單
        seckillMapper.createOrder(productId, userId);
        return "秒殺成功!";
    }
}
 
// Mapper接口
public interface SeckillMapper {
    @Select("SELECT * FROM seckill_stock WHERE product_id = #{productId} FOR UPDATE")
    SeckillStock selectStockByProductIdForUpdate(String productId);
 
    @Update("UPDATE seckill_stock SET stock = stock - 1 WHERE product_id = #{productId}")
    int decrementStock(String productId);
 
    @Insert("INSERT INTO seckill_order (product_id, user_id) VALUES (#{productId}, #{userId})")
    void createOrder(String productId, String userId);
}

2. DB 鎖在秒殺場(chǎng)景的劣勢(shì)

  • 性能極低:每秒僅能支撐幾千 QPS,10 萬(wàn) + 并發(fā)下會(huì)出現(xiàn)大量請(qǐng)求阻塞,數(shù)據(jù)庫(kù)連接池被占滿,甚至導(dǎo)致數(shù)據(jù)庫(kù)宕機(jī)。
    • 原因:SELECT FOR UPDATE是磁盤(pán) IO 操作,且事務(wù)需要等待鎖釋放,高并發(fā)下產(chǎn)生大量鎖競(jìng)爭(zhēng)。
  • 死鎖風(fēng)險(xiǎn):若多個(gè)線程同時(shí)鎖定多個(gè)商品的庫(kù)存記錄(如用戶同時(shí)秒殺 iPhone 和華為),可能出現(xiàn)死鎖。
    • 示例:線程 A 鎖定商品 1,等待商品 2 的鎖;線程 B 鎖定商品 2,等待商品 1 的鎖,導(dǎo)致死鎖。
    • 解決:數(shù)據(jù)庫(kù)會(huì)自動(dòng)檢測(cè)死鎖并回滾其中一個(gè)事務(wù),但會(huì)增加業(yè)務(wù)失敗率。
  • 鎖粒度問(wèn)題:若product_id沒(méi)有索引,SELECT FOR UPDATE會(huì)升級(jí)為表鎖,導(dǎo)致所有商品的秒殺都被阻塞,性能進(jìn)一步下降。

3. 結(jié)論:秒殺場(chǎng)景優(yōu)先用 Redis 鎖

Redis 鎖的性能和并發(fā)支撐能力遠(yuǎn)優(yōu)于 DB 鎖,適合高并發(fā)、短事務(wù)的場(chǎng)景,即使存在少量鎖過(guò)期風(fēng)險(xiǎn),也可通過(guò)業(yè)務(wù)兜底(如庫(kù)存最終一致性校驗(yàn))解決。

案例 2:金融資金扣減場(chǎng)景(低并發(fā)、核心數(shù)據(jù)、長(zhǎng)事務(wù))

業(yè)務(wù)背景

某銀行 APP 的用戶轉(zhuǎn)賬功能,用戶 A 向用戶 B 轉(zhuǎn)賬 1 萬(wàn)元,需要扣減 A 的余額,增加 B 的余額,要求資金絕對(duì)不能出錯(cuò)(不能多扣、少扣、重復(fù)扣)。

方案 1:使用 DB 鎖(SELECT FOR UPDATE)實(shí)現(xiàn)

1. 核心實(shí)現(xiàn)

@Service
public class TransferService {
    @Autowired
    private AccountMapper accountMapper;
 
    // 轉(zhuǎn)賬核心方法(事務(wù)內(nèi)執(zhí)行,保證原子性)
    @Transactional(rollbackFor = Exception.class)
    public String transfer(String fromUserId, String toUserId, BigDecimal amount) {
        // 1. 校驗(yàn)金額
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            return "轉(zhuǎn)賬金額必須大于0!";
        }
 
        // 2. 獲取用戶A的行鎖(扣減余額前加鎖,防止并發(fā)扣減)
        Account fromAccount = accountMapper.selectByUserIdForUpdate(fromUserId);
        if (fromAccount == null) {
            return "轉(zhuǎn)出賬戶不存在!";
        }
        // 校驗(yàn)余額
        if (fromAccount.getBalance().compareTo(amount) < 0) {
            return "余額不足!";
        }
 
        // 3. 獲取用戶B的行鎖(防止并發(fā)更新)
        Account toAccount = accountMapper.selectByUserIdForUpdate(toUserId);
        if (toAccount == null) {
            return "轉(zhuǎn)入賬戶不存在!";
        }
 
        // 4. 扣減用戶A的余額
        accountMapper.decrementBalance(fromUserId, amount);
        // 5. 增加用戶B的余額
        accountMapper.incrementBalance(toUserId, amount);
 
        return "轉(zhuǎn)賬成功!";
    }
}
 
// Mapper接口
public interface AccountMapper {
    @Select("SELECT * FROM account WHERE user_id = #{userId} FOR UPDATE")
    Account selectByUserIdForUpdate(String userId);
 
    @Update("UPDATE account SET balance = balance - #{amount} WHERE user_id = #{userId}")
    int decrementBalance(String userId, BigDecimal amount);
 
    @Update("UPDATE account SET balance = balance + #{amount} WHERE user_id = #{userId}")
    int incrementBalance(String userId, BigDecimal amount);
}

2. DB 鎖在資金扣減場(chǎng)景的優(yōu)勢(shì)

  • 數(shù)據(jù)絕對(duì)安全:依賴數(shù)據(jù)庫(kù)事務(wù)的 ACID 特性,扣減和增加余額的操作要么全成,要么全敗,不會(huì)出現(xiàn)中間狀態(tài)(如 A 的余額扣減了,但 B 的余額沒(méi)增加)。
  • 鎖的持久性:即使服務(wù)宕機(jī),數(shù)據(jù)庫(kù)的鎖和事務(wù)狀態(tài)會(huì)被持久化,恢復(fù)后數(shù)據(jù)一致;而 Redis 鎖若宕機(jī),鎖數(shù)據(jù)丟失,可能導(dǎo)致并發(fā)扣減。
  • 無(wú)鎖過(guò)期風(fēng)險(xiǎn):資金扣減的業(yè)務(wù)處理時(shí)間可能較長(zhǎng)(如需要校驗(yàn)用戶身份、風(fēng)控規(guī)則),Redis 鎖的過(guò)期時(shí)間難以設(shè)置(設(shè)置太短會(huì)提前釋放,設(shè)置太長(zhǎng)會(huì)導(dǎo)致死鎖),而 DB 鎖只要事務(wù)不提交,就會(huì)一直持有鎖(可通過(guò)數(shù)據(jù)庫(kù)超時(shí)機(jī)制兜底)。
  • 易于審計(jì):所有資金操作都在數(shù)據(jù)庫(kù)事務(wù)中,可通過(guò)日志追溯,滿足金融合規(guī)要求;而 Redis 的操作日志難以審計(jì)。

3. DB 鎖的優(yōu)化點(diǎn)

  • 鎖粒度:必須為user_id創(chuàng)建主鍵索引,確保SELECT FOR UPDATE是行鎖,而非表鎖。
  • 死鎖處理:按用戶 ID 的字典序加鎖(如先鎖定 user_id 小的賬戶,再鎖定大的),避免死鎖。
    • 示例:用戶 A(ID:1001)向用戶 B(ID:1002)轉(zhuǎn)賬,先鎖定 1001,再鎖定 1002;用戶 B 向用戶 A 轉(zhuǎn)賬,同樣先鎖定 1001,再鎖定 1002,避免死鎖。

方案 2:使用 Redis 鎖實(shí)現(xiàn)

1. 核心實(shí)現(xiàn)

@Service
public class TransferService {
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private AccountMapper accountMapper;
 
    public String transfer(String fromUserId, String toUserId, BigDecimal amount) {
        // 1. 定義Redis鎖key(粒度:用戶ID,按字典序加鎖)
        String lockKey1 = "transfer_lock:" + (fromUserId.compareTo(toUserId) < 0 ? fromUserId : toUserId);
        String lockKey2 = "transfer_lock:" + (fromUserId.compareTo(toUserId) > 0 ? fromUserId : toUserId);
        RLock lock1 = redissonClient.getLock(lockKey1);
        RLock lock2 = redissonClient.getLock(lockKey2);
 
        try {
            // 2. 批量獲取鎖(等待時(shí)間10秒,鎖過(guò)期30秒)
            boolean lockSuccess = RedissonMultiLock(lock1, lock2).tryLock(10, 30, TimeUnit.SECONDS);
            if (!lockSuccess) {
                return "轉(zhuǎn)賬請(qǐng)求處理中,請(qǐng)稍后重試!";
            }
 
            // 3. 業(yè)務(wù)邏輯(扣減+增加余額,無(wú)事務(wù)保障)
            Account fromAccount = accountMapper.selectByUserId(fromUserId);
            if (fromAccount == null || fromAccount.getBalance().compareTo(amount) < 0) {
                return "余額不足或賬戶不存在!";
            }
            Account toAccount = accountMapper.selectByUserId(toUserId);
            if (toAccount == null) {
                return "轉(zhuǎn)入賬戶不存在!";
            }
 
            // 4. 扣減余額(無(wú)事務(wù),可能出現(xiàn)扣減成功但增加失?。?
            accountMapper.decrementBalance(fromUserId, amount);
            // 模擬網(wǎng)絡(luò)異常:此處若服務(wù)宕機(jī),A的余額被扣減,B的余額未增加,資金丟失
            // int a = 1 / 0;
            accountMapper.incrementBalance(toUserId, amount);
 
            return "轉(zhuǎn)賬成功!";
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return "轉(zhuǎn)賬失敗,請(qǐng)重試!";
        } finally {
            // 5. 釋放鎖
            if (lock1.isHeldByCurrentThread()) {
                lock1.unlock();
            }
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
            }
        }
    }
}

2. Redis 鎖在資金扣減場(chǎng)景的致命問(wèn)題

  • 數(shù)據(jù)一致性無(wú)法保障:Redis 鎖只能控制并發(fā),但無(wú)法保證扣減和增加余額的原子性。若扣減 A 的余額后,服務(wù)宕機(jī),B 的余額未增加,會(huì)導(dǎo)致資金丟失,這在金融場(chǎng)景中是絕對(duì)不允許的。
    • 解決:需引入分布式事務(wù)(如 TCC、本地消息表),但會(huì)增加系統(tǒng)復(fù)雜度,且性能進(jìn)一步下降。
  • 鎖過(guò)期風(fēng)險(xiǎn):若資金扣減的業(yè)務(wù)處理時(shí)間超過(guò) 30 秒(如風(fēng)控校驗(yàn)耗時(shí)較長(zhǎng)),鎖會(huì)提前過(guò)期,其他線程獲取鎖后,會(huì)再次扣減 A 的余額,導(dǎo)致重復(fù)扣減。
  • 審計(jì)困難:Redis 的鎖操作日志無(wú)法與資金操作日志關(guān)聯(lián),難以滿足金融合規(guī)的審計(jì)要求。

3. 結(jié)論:資金扣減場(chǎng)景優(yōu)先用 DB 鎖

DB 鎖結(jié)合事務(wù)的 ACID 特性,能保證核心數(shù)據(jù)的一致性和安全性,即使性能較低,也符合金融場(chǎng)景的核心需求(數(shù)據(jù)安全 > 性能)。

總結(jié):Redis 鎖與 DB 鎖的選擇指南

場(chǎng)景特征推薦使用原因
高并發(fā)、短事務(wù)、非核心數(shù)據(jù)Redis 鎖性能高,能支撐高并發(fā),少量異??赏ㄟ^(guò)業(yè)務(wù)兜底解決
低并發(fā)、長(zhǎng)事務(wù)、核心數(shù)據(jù)DB 鎖(SELECT FOR UPDATE)事務(wù) ACID 保障數(shù)據(jù)安全,無(wú)鎖過(guò)期風(fēng)險(xiǎn),滿足合規(guī)審計(jì)要求
冪等性控制(如重復(fù)支付)DB 鎖(唯一索引)無(wú)需鎖等待,性能高于悲觀鎖,能有效防止重復(fù)寫(xiě)入
分布式系統(tǒng)、跨服務(wù)并發(fā)Redis 鎖天然支持分布式,跨服務(wù) / 跨機(jī)器的鎖控制更簡(jiǎn)單
單服務(wù)、本地并發(fā)本地鎖(synchronized)比 Redis 鎖和 DB 鎖更輕量,性能更高

補(bǔ)充:Redis 鎖與 DB 鎖的混合使用場(chǎng)景

在實(shí)際項(xiàng)目中,可結(jié)合兩者的優(yōu)勢(shì):

  • 秒殺場(chǎng)景:Redis 鎖控制并發(fā)下單 + DB 唯一索引防止超賣(mài)(兜底)。
    • 流程:Redis 鎖扣減 Redis 庫(kù)存 → 異步寫(xiě)入 DB → DB 唯一索引(訂單號(hào))防止重復(fù)下單,若 Redis 庫(kù)存扣減成功但 DB 寫(xiě)入失敗,通過(guò)定時(shí)任務(wù)回滾 Redis 庫(kù)存。
  • 訂單支付場(chǎng)景:Redis 鎖控制并發(fā)支付 + DB 事務(wù)保證資金變動(dòng)原子性。
    • 流程:Redis 鎖防止重復(fù)支付 → DB 事務(wù)扣減余額 + 生成支付記錄 → 支付完成后釋放 Redis 鎖。

擴(kuò)展補(bǔ)充:

一、為什么會(huì)有 Redis 鎖的存在?為什么還會(huì)有 DB 鎖的存在?

Redis 鎖和 DB 鎖的誕生,本質(zhì)是不同業(yè)務(wù)場(chǎng)景對(duì) “并發(fā)控制” 的需求存在本質(zhì)差異,二者分別解決了對(duì)方無(wú)法高效解決的問(wèn)題,是分布式系統(tǒng)中針對(duì) “性能” 和 “數(shù)據(jù)安全” 的不同選擇。

1. Redis 鎖的誕生:為解決高并發(fā)分布式場(chǎng)景下的性能型并發(fā)控制問(wèn)題

在 Redis 鎖出現(xiàn)之前,處理分布式并發(fā)的方案有:

  • 本地鎖(synchronized/Lock):僅能控制單服務(wù)內(nèi)的并發(fā),分布式集群下多個(gè)服務(wù)節(jié)點(diǎn)無(wú)法共享鎖,會(huì)導(dǎo)致并發(fā)安全問(wèn)題(如秒殺場(chǎng)景中多個(gè)節(jié)點(diǎn)同時(shí)扣減庫(kù)存,引發(fā)超賣(mài));
  • DB 鎖:能解決分布式并發(fā),但性能極低(磁盤(pán) IO + 事務(wù)開(kāi)銷(xiāo)),無(wú)法支撐高并發(fā)場(chǎng)景(如秒殺、電商促銷(xiāo)的 10 萬(wàn) + QPS)。

而 Redis 作為內(nèi)存數(shù)據(jù)庫(kù),具備高性能、分布式部署、輕量易擴(kuò)展的特性,基于它實(shí)現(xiàn)的分布式鎖,恰好彌補(bǔ)了上述方案的缺陷:

  • 面對(duì)高并發(fā)請(qǐng)求,Redis 鎖的加鎖 / 解鎖操作是內(nèi)存級(jí)別的,耗時(shí)微秒級(jí),能支撐超高 QPS;
  • Redis 天然支持分布式部署,跨服務(wù)、跨機(jī)器的節(jié)點(diǎn)能共享鎖狀態(tài),完美解決分布式集群的并發(fā)控制問(wèn)題;
  • 支持靈活的過(guò)期機(jī)制,能有效防止死鎖,且鎖粒度可自定義(如按商品 ID、用戶 ID 鎖),適配不同業(yè)務(wù)場(chǎng)景。

總結(jié):Redis 鎖的存在,是為了滿足分布式系統(tǒng)中高并發(fā)、短事務(wù)、非核心數(shù)據(jù)場(chǎng)景對(duì) “高性能并發(fā)控制” 的需求,是性能優(yōu)先的選擇。

2. DB 鎖的誕生:為解決核心數(shù)據(jù)場(chǎng)景下的安全型并發(fā)控制問(wèn)題

在 DB 鎖出現(xiàn)之前,僅靠應(yīng)用層的邏輯控制并發(fā),會(huì)面臨以下問(wèn)題:

  • 應(yīng)用層邏輯無(wú)法保證數(shù)據(jù)操作的原子性(如扣減余額時(shí),查余額和更新余額的兩步操作之間,可能被其他請(qǐng)求插入,導(dǎo)致余額計(jì)算錯(cuò)誤);
  • 分布式場(chǎng)景下,應(yīng)用層的臨時(shí)狀態(tài)(如內(nèi)存中的標(biāo)記)無(wú)法持久化,服務(wù)宕機(jī)后會(huì)丟失,導(dǎo)致數(shù)據(jù)不一致;
  • 核心數(shù)據(jù)(如資金、訂單狀態(tài))需要事務(wù)級(jí)別的安全保障,應(yīng)用層方案無(wú)法滿足 ACID 特性。

而數(shù)據(jù)庫(kù)作為持久化存儲(chǔ),具備事務(wù) ACID 特性、數(shù)據(jù)持久化、鎖與事務(wù)強(qiáng)綁定的特性,基于它實(shí)現(xiàn)的鎖,恰好解決了上述問(wèn)題:

  • DB 鎖與事務(wù)深度融合,能保證鎖范圍內(nèi)的操作要么全成、要么全敗,從底層杜絕數(shù)據(jù)中間態(tài);
  • 數(shù)據(jù)和鎖狀態(tài)都持久化到磁盤(pán),即使服務(wù)或數(shù)據(jù)庫(kù)宕機(jī),恢復(fù)后數(shù)據(jù)和鎖狀態(tài)依然一致,無(wú)數(shù)據(jù)丟失風(fēng)險(xiǎn);
  • 支持細(xì)粒度的行鎖、表鎖,能精準(zhǔn)控制核心數(shù)據(jù)的并發(fā)訪問(wèn),滿足金融、電商等場(chǎng)景的合規(guī)和數(shù)據(jù)安全要求。

總結(jié):DB 鎖的存在,是為了滿足核心數(shù)據(jù)場(chǎng)景(資金、訂單)、低并發(fā)、長(zhǎng)事務(wù)對(duì) “數(shù)據(jù)安全與一致性” 的需求,是安全優(yōu)先的選擇。

二、Redis 鎖與 DB 鎖最根本、最本質(zhì)的區(qū)別

二者的本質(zhì)區(qū)別,源于底層存儲(chǔ)介質(zhì)和設(shè)計(jì)目標(biāo)的不同,最終體現(xiàn)為 **“性能與臨時(shí)態(tài)” vs “安全與持久態(tài)”** 的核心差異:

維度本質(zhì)特征(Redis 鎖)本質(zhì)特征(DB 鎖)
存儲(chǔ)介質(zhì)內(nèi)存(臨時(shí)存儲(chǔ),非持久化優(yōu)先)磁盤(pán)(持久化存儲(chǔ),數(shù)據(jù)落地優(yōu)先)
設(shè)計(jì)目標(biāo)高性能處理并發(fā),犧牲部分?jǐn)?shù)據(jù)安全的容錯(cuò)性高安全保障數(shù)據(jù)一致性,犧牲部分性能
鎖的本質(zhì)分布式協(xié)調(diào)的 “臨時(shí)標(biāo)記”:鎖是內(nèi)存中的一個(gè) key-value,僅用于標(biāo)記資源是否被占用,與數(shù)據(jù)操作無(wú)強(qiáng)綁定數(shù)據(jù)操作的 “原子性保障”:鎖是事務(wù)的一部分,與數(shù)據(jù)操作強(qiáng)綁定,確保操作的 ACID 特性
一致性保障最終一致性(依賴應(yīng)用層補(bǔ)償機(jī)制,如重試、對(duì)賬)強(qiáng)一致性(依賴數(shù)據(jù)庫(kù)事務(wù)的 ACID 特性)
故障恢復(fù)鎖狀態(tài)可能丟失(如 Redis 宕機(jī)),需依賴過(guò)期時(shí)間、集群(Redlock)兜底鎖狀態(tài)與數(shù)據(jù)一起持久化,宕機(jī)恢復(fù)后狀態(tài)不變

一句話總結(jié)本質(zhì)區(qū)別:Redis 鎖是基于內(nèi)存的、松耦合的、性能導(dǎo)向的分布式并發(fā)協(xié)調(diào)工具;DB 鎖是基于磁盤(pán)的、強(qiáng)耦合的、安全導(dǎo)向的事務(wù)內(nèi)數(shù)據(jù)操作保障工具。

三、Redis 鎖解決的最核心問(wèn)題?DB 鎖解決的最核心問(wèn)題?

1. Redis 鎖解決的最核心問(wèn)題

在分布式集群環(huán)境下,以極致的性能解決 “高并發(fā)場(chǎng)景中資源的并發(fā)訪問(wèn)控制” 問(wèn)題,具體拆解為:

  • 分布式并發(fā)控制:突破單服務(wù)本地鎖的限制,讓跨服務(wù)、跨機(jī)器的節(jié)點(diǎn)共享鎖狀態(tài),避免分布式場(chǎng)景下的并發(fā)安全問(wèn)題(如秒殺場(chǎng)景中多個(gè)節(jié)點(diǎn)同時(shí)扣減庫(kù)存);
  • 高性能并發(fā)處理:內(nèi)存級(jí)別的加鎖 / 解鎖操作,支撐 10 萬(wàn) + QPS 的高并發(fā)請(qǐng)求,解決 DB 鎖在高并發(fā)下的性能瓶頸;
  • 靈活的資源隔離:通過(guò)自定義鎖 key 的粒度(如商品 ID、用戶 ID),實(shí)現(xiàn)細(xì)粒度的資源隔離,避免全局鎖導(dǎo)致的性能浪費(fèi)。

典型場(chǎng)景驗(yàn)證:秒殺活動(dòng)中,Redis 鎖能控制 10 萬(wàn) + 并發(fā)請(qǐng)求對(duì) 100 臺(tái)庫(kù)存的訪問(wèn),確保不超賣(mài),且系統(tǒng)不會(huì)因并發(fā)壓力崩潰 —— 這是 Redis 鎖核心價(jià)值的體現(xiàn)。

2. DB 鎖解決的最核心問(wèn)題

在數(shù)據(jù)操作過(guò)程中,以事務(wù)的 ACID 特性解決 “核心數(shù)據(jù)的一致性與安全性保障” 問(wèn)題,具體拆解為:

  • 數(shù)據(jù)操作的原子性:確保一組數(shù)據(jù)操作(如扣減用戶余額 + 增加商戶余額)要么全部完成,要么全部回滾,杜絕數(shù)據(jù)中間態(tài)(如用戶余額扣減了但商戶余額沒(méi)增加,導(dǎo)致資金丟失);
  • 核心數(shù)據(jù)的強(qiáng)一致性:鎖與數(shù)據(jù)存儲(chǔ)強(qiáng)綁定,并發(fā)操作下數(shù)據(jù)的讀取和寫(xiě)入都是準(zhǔn)確的,無(wú)需依賴應(yīng)用層補(bǔ)償;
  • 數(shù)據(jù)的持久化安全:鎖狀態(tài)和數(shù)據(jù)一起持久化到磁盤(pán),即使系統(tǒng)宕機(jī),恢復(fù)后數(shù)據(jù)依然一致,滿足金融、電商等核心場(chǎng)景的合規(guī)要求。

典型場(chǎng)景驗(yàn)證:銀行轉(zhuǎn)賬場(chǎng)景中,DB 鎖(SELECT FOR UPDATE)能保證用戶 A 扣減 1 萬(wàn)元與用戶 B 增加 1 萬(wàn)元的操作原子性,無(wú)論發(fā)生何種故障,都不會(huì)出現(xiàn)資金丟失或賬實(shí)不符 —— 這是 DB 鎖核心價(jià)值的體現(xiàn)。

補(bǔ)充:為何二者無(wú)法相互替代?

  • Redis 鎖無(wú)法替代 DB 鎖:Redis 鎖缺乏事務(wù)的原子性保障,核心數(shù)據(jù)操作(如資金變動(dòng))若僅用 Redis 鎖,會(huì)出現(xiàn)數(shù)據(jù)不一致且無(wú)法兜底,這在金融場(chǎng)景中是致命的;
  • DB 鎖無(wú)法替代 Redis 鎖:DB 鎖的性能瓶頸無(wú)法突破,高并發(fā)場(chǎng)景(如秒殺)下,DB 鎖會(huì)導(dǎo)致大量請(qǐng)求阻塞,甚至數(shù)據(jù)庫(kù)宕機(jī),無(wú)法支撐業(yè)務(wù)需求。

二者的共存,是分布式系統(tǒng)中 **“性能” 與 “安全” 平衡 ** 的必然結(jié)果。

到此這篇關(guān)于Redis鎖與DB鎖的使用與區(qū)別小結(jié)的文章就介紹到這了,更多相關(guān)Redis鎖與DB鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Redis實(shí)現(xiàn)高效內(nèi)存管理的示例代碼

    Redis實(shí)現(xiàn)高效內(nèi)存管理的示例代碼

    Redis內(nèi)存管理是其核心功能之一,為了高效地利用內(nèi)存,Redis采用了多種技術(shù)和策略,如優(yōu)化的數(shù)據(jù)結(jié)構(gòu)、內(nèi)存分配策略、內(nèi)存回收、數(shù)據(jù)壓縮等,下面就來(lái)詳細(xì)的介紹一下
    2025-08-08
  • 淺談redission鎖的默認(rèn)失效時(shí)間

    淺談redission鎖的默認(rèn)失效時(shí)間

    Redisson是一個(gè)基于Redis的Java駐留庫(kù),提供了許多分布式對(duì)象和服務(wù),包括分布式鎖,本文主要介紹了淺談redission鎖的默認(rèn)失效時(shí)間, 具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • redis GEO數(shù)據(jù)結(jié)構(gòu)、實(shí)現(xiàn)附近商鋪功能實(shí)踐

    redis GEO數(shù)據(jù)結(jié)構(gòu)、實(shí)現(xiàn)附近商鋪功能實(shí)踐

    文章介紹了Redis中的GEO命令及其用途,包括地理坐標(biāo)存儲(chǔ)、距離計(jì)算、坐標(biāo)轉(zhuǎn)換和位置搜索等功能,還分享了如何使用Redis實(shí)現(xiàn)查詢附近商鋪的功能,包括導(dǎo)入商鋪信息和根據(jù)類(lèi)型及距離進(jìn)行搜索
    2025-12-12
  • redis配置文件中常用配置詳解

    redis配置文件中常用配置詳解

    這篇文章主要介紹了redis配置文件中常用配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • redis?手機(jī)驗(yàn)證碼實(shí)現(xiàn)示例

    redis?手機(jī)驗(yàn)證碼實(shí)現(xiàn)示例

    本文主要介紹了redis?手機(jī)驗(yàn)證碼實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Redis之RedisTemplate配置方式(序列和反序列化)

    Redis之RedisTemplate配置方式(序列和反序列化)

    這篇文章主要介紹了Redis之RedisTemplate配置方式(序列和反序列化),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Redis獲取某個(gè)前綴的key腳本實(shí)例

    Redis獲取某個(gè)前綴的key腳本實(shí)例

    這篇文章主要給大家介紹了關(guān)于Redis獲取某個(gè)前綴的key腳本的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-04-04
  • 基于setnx,lua腳本和Redisson詳解Redis分布式鎖實(shí)現(xiàn)的三種方式

    基于setnx,lua腳本和Redisson詳解Redis分布式鎖實(shí)現(xiàn)的三種方式

    分布式鎖是解決分布式系統(tǒng)中多節(jié)點(diǎn)并發(fā)訪問(wèn)共享資源的核心方案,本文從原理層面拆解Redis分布式鎖的核心邏輯,并詳細(xì)分析三種常見(jiàn)實(shí)現(xiàn)方式的代碼邏輯、優(yōu)缺點(diǎn)及生產(chǎn)環(huán)境注意事項(xiàng),感興趣的朋友跟隨小編一起看看吧
    2026-03-03
  • Redis?常見(jiàn)緩存問(wèn)題總結(jié)

    Redis?常見(jiàn)緩存問(wèn)題總結(jié)

    這篇文章主要給大家總結(jié)了一些Redis?常見(jiàn)緩存問(wèn)題,并介紹了解決辦法,文中的圖文示例介紹的非常仔細(xì),感興趣的同學(xué)可以參考閱讀下
    2023-06-06
  • redis如何設(shè)置key的有效期

    redis如何設(shè)置key的有效期

    這篇文章主要介紹了redis如何設(shè)置key的有效期方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01

最新評(píng)論

西乌珠穆沁旗| 丽江市| 峨眉山市| 永城市| 五峰| 星座| 临城县| 瓦房店市| 马公市| 马公市| 鄂伦春自治旗| 浦东新区| 积石山| 蓬莱市| 承德市| 海南省| 新巴尔虎右旗| 宜君县| 齐河县| 成安县| 英山县| 霞浦县| 天等县| 永胜县| 台前县| 儋州市| 永修县| 平南县| 云阳县| 雷波县| 石渠县| 荥经县| 安岳县| 濉溪县| 南靖县| 新民市| 丰都县| 壶关县| 东山县| 铜梁县| 紫云|