Redis緩存和Redis分布式鎖使用及說明
Redis 緩存 (Caching)
在Redis中,數(shù)據(jù)是以鍵值對的形式存儲的,其中鍵總是字符串類型,而值可以是多種數(shù)據(jù)類型。
目的
加速數(shù)據(jù)訪問,減少對慢速數(shù)據(jù)源(如數(shù)據(jù)庫)的頻繁查詢,提升系統(tǒng)性能和吞吐量。
核心邏輯
讀操作:
應(yīng)用優(yōu)先從 Redis 讀取數(shù)據(jù)。
若 Redis 中無數(shù)據(jù)(緩存未命中),則查詢數(shù)據(jù)庫,并將結(jié)果寫入 Redis。
寫操作:
更新數(shù)據(jù)庫后,同步或異步更新/失效 Redis 中的緩存(如
DEL key或SET key new_value)。
存儲形式總結(jié)
| 數(shù)據(jù)類型 | 底層實現(xiàn) | 最大元素數(shù) | 特點 |
|---|---|---|---|
| String | SDS 動態(tài)字符串 | 512 MB | 支持文本/二進制數(shù)據(jù) |
| Hash |
| 2³²-1 個字段 | 高效存儲對象屬性 |
| List | 雙向鏈表/ziplist | 2³²-1 個元素 | 保持插入順序 |
| Set | 哈希表或 intset | 2³²-1 個元素 | 自動去重 |
| Sorted Set | 跳表 + 哈希表 | 2³²-1 個元素 | 按分數(shù)排序 |
| Geospatial | Sorted Set | 同 Sorted Set | 支持地理坐標(biāo)計算 |
| Stream | rax 樹 | 理論無上限 | 支持消費者組 |
| Bitmap | String | 2³² 位 | 超高效布爾存儲 |
| HyperLogLog | 專用結(jié)構(gòu) | 理論無上限 | 固定12KB內(nèi)存存儲巨大基數(shù) |
// 設(shè)置過期時間
db.KeyExpire("temp_data", TimeSpan.FromMinutes(30));
// 滑動過期
db.StringSet("session:1001", data, TimeSpan.FromMinutes(20), when: When.Always);典型場景
- 高頻讀取的熱點數(shù)據(jù)(如商品信息、用戶資料)
- 減輕數(shù)據(jù)庫壓力
- 加速 API 響應(yīng)
Redis 分布式鎖 (Distributed Lock)
目的
協(xié)調(diào)分布式系統(tǒng)中多個進程/服務(wù)的并發(fā)操作,確保同一時刻只有一個客戶端能執(zhí)行關(guān)鍵邏輯(如資源修改),避免數(shù)據(jù)競爭。
核心作用
Redis 分布式鎖用于解決分布式系統(tǒng)中的并發(fā)沖突問題,主要作用包括:
資源互斥訪問
確保多個服務(wù)實例/進程同時操作共享資源(如數(shù)據(jù)庫、文件)時,同一時刻只有一個客戶端能執(zhí)行關(guān)鍵代碼。
例:避免庫存超賣、重復(fù)支付、文件覆蓋等問題。
協(xié)調(diào)分布式任務(wù)
保證定時任務(wù)、批處理操作在集群環(huán)境中只被執(zhí)行一次。
防止并發(fā)副作用
避免多個請求同時修改同一數(shù)據(jù)導(dǎo)致狀態(tài)不一致
核心邏輯
加鎖:
客戶端嘗試在 Redis 中創(chuàng)建一個唯一鍵(如
lock:order_123),通過原子操作(如SET key random_value NX PX 30000)確?;コ庑浴?/p>
執(zhí)行業(yè)務(wù)邏輯:
只有成功獲得鎖的客戶端才能執(zhí)行后續(xù)操作(如扣減庫存)。
解鎖:
完成后刪除該鍵(需通過 Lua 腳本驗證值,避免誤刪其他客戶端的鎖)。
典型場景
分布式系統(tǒng)下的資源互斥訪問(如訂單支付、庫存扣減)
防止重復(fù)任務(wù)調(diào)度(如定時任務(wù)只在一個節(jié)點執(zhí)行)
// 示例:使用 Redlock 或 StackExchange.Redis 鎖
var redisLock = _redis.AcquireLock("lock:order_123", TimeSpan.FromSeconds(30));
try
{
if (redisLock.IsAcquired)
{
// 執(zhí)行業(yè)務(wù)邏輯(如扣減庫存)
_stockService.ReduceStock(productId, 1);
}
}
finally
{
redisLock?.Release(); // 釋放鎖
}類庫中用到的包是RedLockNet:
//鎖信息集合
var trayBarcodeLockInfos = new List<IRedLock>();
try
{
//獲取鎖
var lockInfo = await _redLockLead.AcquireLockAsync(moveTrayBalance.TrayBarcode);
_ = !lockInfo.IsAcquired ? throw new BusinessException(message: _localizer["_TrayBarcodeRequestExist", moveTrayBalance.TrayBarcode]) : false;
trayBarcodeLockInfos.Add(lockInfo); //獲取鎖成功 將鎖加入集合中
//執(zhí)行業(yè)務(wù)邏輯
··········
}
catch (BusinessException ex)
{
}
finally
{
//釋放鎖
foreach (var lockInfo in trayBarcodeLockInfos)
{
await _redLockLead.ReleaseLockAsync(lockInfo);
}
}AcquireLockAsync() 獲取鎖 獲取不到返回失敗
IsAcquired() 代表是否獲取鎖成功
/// <summary>
/// 獲取鎖(獲取不到立即返回失敗)
/// </summary>
/// <param name="lockKey"></param>
/// <returns></returns>
public virtual async Task<IRedLock> AcquireLockAsync(string lockKey)
{
var redLock = await _factoryProvider.RedLockFactoryInstance.CreateLockAsync(lockKey, _defaultKeyExpiry);
return redLock;
}/// <summary>
/// 獲取鎖(阻塞直到獲取鎖成功或者1h后仍獲取不到,返回失敗)
/// </summary>
/// <param name="lockKey"></param>
/// <returns></returns>
public virtual async Task<IRedLock> AcquireLockUntilSuccessAsync(string lockKey)
{
var redLock = await _factoryProvider.RedLockFactoryInstance.CreateLockAsync(lockKey, _defaultKeyExpiry, _wait, _retry);
return redLock;
}
public async Task<IRedLock> CreateLockAsync(string resource, TimeSpan expiryTime, TimeSpan waitTime, TimeSpan retryTime, CancellationToken? cancellationToken = null)
{
return await RedLock.CreateAsync(loggerFactory.CreateLogger<RedLock>(), redisCaches, resource, expiryTime, waitTime, retryTime, configuration.RetryConfiguration, cancellationToken ?? CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false);
}默認為每10秒嘗試一次,在嘗試了一小時后還獲取不到鎖的話,就返回失敗
/// <summary>
/// 釋放鎖
/// </summary>
/// <param name="redLock"></param>
/// <returns></returns>
public virtual async Task ReleaseLockAsync(IRedLock redLock)
{
await redLock.DisposeAsync();
}核心區(qū)別總結(jié)
| 特性 | Redis 緩存 | Redis 分布式鎖 |
|---|---|---|
| 核心目標(biāo) | 提升讀取性能,降低數(shù)據(jù)庫壓力 | 解決分布式系統(tǒng)并發(fā)沖突 |
| 數(shù)據(jù)性質(zhì) | 存儲業(yè)務(wù)數(shù)據(jù)(如用戶信息) | 存儲鎖狀態(tài)(臨時性、非業(yè)務(wù)數(shù)據(jù)) |
| 讀寫模式 | 高頻讀、低頻寫 | 短期占用、立即釋放 |
| 生命周期 | 可長期存在(有過期時間) | 臨時存在(任務(wù)結(jié)束即釋放) |
| 關(guān)鍵命令 | GET/SET/DEL/EXPIRE | SET NX PX/EVAL(Lua 解鎖) |
| 數(shù)據(jù)一致性 | 需處理緩存與數(shù)據(jù)庫一致性(如雙寫策略) | 需確保鎖的互斥性和安全性(如 Redlock) |
鎖是協(xié)調(diào)機制,不存儲業(yè)務(wù)數(shù)據(jù);緩存是數(shù)據(jù)副本,兩者不可互換。
緩存僅加速數(shù)據(jù)讀取,無法控制并發(fā)寫操作(如超賣問題仍需分布式鎖或數(shù)據(jù)庫事務(wù))。
實際系統(tǒng)中二者常結(jié)合使用:
讀場景:用 Redis 緩存加速數(shù)據(jù)訪問。
寫場景:用 Redis 分布式鎖保護共享資源,確保數(shù)據(jù)一致性。
| 適用場景 | 不適用場景 |
|---|---|
| 庫存扣減/秒殺系統(tǒng) | 高頻短操作(鎖開銷 > 業(yè)務(wù)開銷) |
| 分布式任務(wù)調(diào)度 | 強一致性要求極高的金融交易 |
| 防止重復(fù)提交 | 單機應(yīng)用(用 Monitor 即可) |
| 跨服務(wù)共享資源協(xié)調(diào) | 讀多寫少場景(用樂觀鎖更優(yōu)) |
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Redis在SpringBoot工程中的綜合應(yīng)用
這篇文章主要介紹了Redis在SpringBoot工程中的綜合應(yīng)用,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10

