第 4 篇:Redis 缓存与分布式锁实现
一、Redis 在系统中的核心作用
票证信息缓存:将高频访问的票证数据(如状态、有效期)缓存至 Redis,减少数据库查询,提升验证响应速度。
分布式锁:在高并发场景下,防止同一张票被同时验证,确保 “一票一次” 机制的可靠性。
闸机状态缓存:实时存储闸机开关状态,便于前端监控与管理。
二、Redis 集成步骤
- 安装 StackExchange.Redis:
dotnet add package StackExchange.Redis
- 注册 Redis 服务:在 Program.cs 中配置连接:
builder.Services.AddSingleton\<IConnectionMultiplexer>(
ConnectionMultiplexer.Connect(builder.Configuration.GetConnectionString("Redis")));
- 封装 Redis 操作工具类:
public class RedisHelper
{
private readonly IDatabase _db;
public RedisHelper(IConnectionMultiplexer redis)
{
_db = redis.GetDatabase();
}
// 设置缓存(带过期时间)
public async Task SetAsync(string key, string value, TimeSpan? expiry = null)
{
await _db.StringSetAsync(key, value, expiry);
}
// 获取缓存
public async Task<string> GetAsync(string key)
{
return await _db.StringGetAsync(key);
}
}
三、分布式锁实现
分布式锁核心特性
- 互斥性:同一时间只有一个节点能获取锁。
- 安全性:避免死锁(锁必须有过期时间),不能误删他人的锁。
- 可用性:Redis 故障时仍能基本可用(如主从切换)。
- 重入性:同一线程可重复获取锁(可选,视场景而定)。
public class RedisDistributedLock
{
private readonly IDatabase _db;
private readonly string _lockPrefix = "lock:";
public RedisDistributedLock(IConnectionMultiplexer redis)
{
_db = redis.GetDatabase();
}
// 获取锁(返回锁标识,用于释放)
public async Task<string> AcquireLockAsync(string resource, TimeSpan expiry)
{
var lockKey = _lockPrefix + resource;
var lockValue = Guid.NewGuid().ToString(); // 唯一标识,防止误释放
// SET NX(不存在则设置)+ PX(过期时间)
var acquired = await _db.StringSetAsync(
lockKey, lockValue, expiry, When.NotExists);
return acquired ? lockValue : null;
}
// 释放锁(Lua脚本保证原子性)
public async Task ReleaseLockAsync(string resource, string lockValue)
{
var lockKey = _lockPrefix + resource;
var script = @"
if redis.call('get', KEYS\[1]) == ARGV\[1] then
return redis.call('del', KEYS\[1])
else
return 0
end";
await _db.ScriptEvaluateAsync(script, new RedisKey\[] { lockKey },
new RedisValue[] { lockValue });
}
}
c
缓存更新:票证状态变更(如使用后)时,同步更新 Redis 缓存。
过期时间:票证缓存设置与票证有效期关联,避免缓存冗余。
缓存穿透防护:对不存在的票证,缓存空值(短期过期),减少无效数据库查询。
五、缓存常见问题
问题 | 描述 | 解决方案 |
---|---|---|
缓存穿透 | 查询不存在的数据,穿透到DB | 1. 缓存空值(短期过期);2. 布隆过滤器预过滤不存在的key |
缓存击穿 | 热点key过期瞬间大量请求DB | 1. 互斥锁(查询时加锁,只让一个线程更新缓存);2. 热点key永不过期 |
缓存雪崩 | 大量key同时过期,请求压垮DB | 1. 过期时间加随机值(分散过期);2. 服务熔断/降级;3. 缓存集群高可用 |
六、注意事项
Redis 部署方式:
- 单机部署:锁性能高,但存在单点故障风险。
- 主从+哨兵:提高可用性,但主从切换时可能出现“锁丢失”(可通过 Redisson 的 RedLock 算法缓解,但性能有损耗)。
锁超时处理:
- 锁过期时间需大于业务执行时间,避免业务未完成锁已释放。
- 可使用“锁续期”机制(如 Redisson 的 Watch Dog),自动延长锁有效期。
缓存与锁的结合:
- 缓存更新时需加锁避免并发问题(如缓存击穿场景)。
- 分布式锁的粒度应尽量小,避免影响性能。
总结
- Redis 缓存:通过合理的缓存策略(如 Cache-Aside)和问题解决方案,可显著提升系统性能。
- 分布式锁:基于 Redis 的
SET NX PX
命令可实现基础锁,生产环境推荐使用 Redisson 简化开发并避免潜在问题。
两者结合可有效解决分布式系统中的性能与并发安全问题。