Redis分布式锁防止缓存击穿
一、Nuget引入 StackExchange.Redis、DistributedLock.Redis依赖
二、使用 StackExchange.Redis 对redis操作做简单封装
public class RedisHelper
{
private static ConnectionMultiplexer _redis;
private static string _connectionString; // 静态构造函数,确保在程序启动时初始化连接
static RedisHelper()
{
_connectionString = "127.0.0.1:6379,password=ist123$%^"; // 替换为你的Redis服务器地址和端口,例如:"localhost:6379"
_redis = ConnectionMultiplexer.Connect(_connectionString);
} // 获取数据库实例
public static IDatabase GetDatabase()
{
return _redis.GetDatabase();
} // 字符串设置与获取
public static void SetString(string key, string value)
{
GetDatabase().StringSet(key, value);
} public static string GetString(string key)
{
return GetDatabase().StringGet(key);
} // 哈希设置与获取
public static void SetHashField(string key, string field, string value)
{
GetDatabase().HashSet(key, field, value);
} public static string GetHashField(string key, string field)
{
return GetDatabase().HashGet(key, field);
} // 列表操作
public static long ListRightPush(string key, string value)
{
return GetDatabase().ListRightPush(key, value);
} public static string ListLeftPop(string key)
{
return GetDatabase().ListLeftPop(key);
} // 集合操作
public static bool SetAdd(string key, string value)
{
return GetDatabase().SetAdd(key, value);
} public static bool SetRemove(string key, string value)
{
return GetDatabase().SetRemove(key, value);
} public static bool SetContains(string key, string value)
{
return GetDatabase().SetContains(key, value);
} // 键的其他操作
public static bool KeyExists(string key)
{
return GetDatabase().KeyExists(key);
} public static void Remove(string key)
{
GetDatabase().KeyDelete(key);
} // 异步方法示例
public static async Task SetStringAsync(string key, string value)
{
await GetDatabase().StringSetAsync(key, value);
} public static async Task<string> GetStringAsync(string key)
{
return await GetDatabase().StringGetAsync(key);
} // 关闭连接(通常在应用程序关闭时调用)
public static void CloseConnection()
{
if (_redis != null && _redis.IsConnected)
{
_redis.Close();
_redis.Dispose();
}
} }
三、模拟从Redis获取缓存数据的逻辑
/// <summary>
/// 模拟操作
/// </summary>
public class RedisOper
{
public static string GetDataByRedis(string RedisKey)
{
string ThreadId = Thread.GetCurrentProcessorId().ToString();
string data = string.Empty;
//从redis读数据
if (RedisHelper.KeyExists(RedisKey))
{
data = RedisHelper.GetString(RedisKey);
Console.WriteLine($"查询到缓存数据:{data} 线程ID:{ThreadId}");
}
else
{
Console.WriteLine($"未查询到缓存数据,准备从数据库查询存入缓存 线程ID:{ThreadId}");
//模拟从数据库查询存入redis,使用分布式锁,只允许一个请求完成
var redisDistributedLock = new RedisDistributedLock("lockkey", RedisHelper.GetDatabase());
Console.WriteLine($"尝试获取锁 线程ID:{ThreadId}");
using (redisDistributedLock.Acquire())
{
//再次判断是否存在缓存
if (!RedisHelper.KeyExists(RedisKey))
{
Console.WriteLine($"获取到锁,模拟将数据库数据写入缓存 线程ID:{ThreadId}");
var GetDataByDB = "This is test data";
RedisHelper.SetString(RedisKey, GetDataByDB);
Thread.Sleep(2000);
}
}
}
return data;
} public static async Task<string> GetDataByRedisAsync(string RedisKey)
{
Console.WriteLine($"当前线程ID:{Thread.GetCurrentProcessorId()}");
string data = string.Empty;
//从redis读数据
if (RedisHelper.KeyExists(RedisKey))
{
data = await RedisHelper.GetStringAsync(RedisKey);
Console.WriteLine($"查询到缓存数据:{data}");
}
else
{
Console.WriteLine($"未查询到缓存数据,准备从数据库查询存入缓存");
//模拟从数据库查询存入redis,使用分布式锁,只允许一个请求完成
var redisDistributedLock = new RedisDistributedLock("lockkey", RedisHelper.GetDatabase());
Console.WriteLine($"尝试获取锁 ");
using (redisDistributedLock.Acquire())
{
//再次判断是否存在缓存
if (!RedisHelper.KeyExists(RedisKey))
{
Console.WriteLine("获取到锁,模拟将数据库数据写入缓存");
var GetDataByDB = "This is test data";
await RedisHelper.SetStringAsync(RedisKey, GetDataByDB);
await Task.Delay(2000);
}
}
}
return data;
}
}
四、测试
string redisKey = "testData";
RedisHelper.Remove(redisKey);
//模拟10个线程同时去获取Redis缓存
List<Task> tasksList= new List<Task>();
tasksList.Add(Task.Run(()=>RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
tasksList.Add(Task.Run(() => RedisOper.GetDataByRedis(redisKey)));
Task.WaitAll(tasksList.ToArray());
await Task.Run(() => RedisOper.GetDataByRedis(redisKey));
Redis分布式锁防止缓存击穿的更多相关文章
- Redis 分布式锁及缓存注释的使用方法
使用工具:Apache an 测压命令: ab -n 100 -c 100 http://www.baidu.com -n代表模拟100个请求,-c代表模拟100个并发,相当于100个人同时访问 ab ...
- SpringBoot集成Redis分布式锁以及Redis缓存
https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...
- SpringBoot集成Redis 一 分布式锁 与 缓存
1.添加依赖及配置(application.yml) <!-- 引入redis依赖 --> <dependency> <groupId>org.springfram ...
- 【分布式缓存系列】集群环境下Redis分布式锁的正确姿势
一.前言 在上一篇文章中,已经介绍了基于Redis实现分布式锁的正确姿势,但是上篇文章存在一定的缺陷——它加锁只作用在一个Redis节点上,如果通过sentinel保证高可用,如果master节点由于 ...
- Redis分布式锁前世今生
1.redis锁前世即基于单Redis节点的分布式锁,诸如setkey value px milliseconds nx 前世者,必将经历种种磨砺,才能稍微符合一些主流.推荐自测非常好用的redis工 ...
- Redis 分布式锁|从青铜到钻石的五种演进方案
缓存系列文章: 缓存实战(一):20 图 |6 千字|缓存实战(上篇) 缓存实战(二):Redis 分布式锁|从青铜到钻石的五种演进方案 缓存实战(三):分布式锁中的王者方案 - Redisson 上 ...
- Redis分布式锁
Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...
- redis分布式锁和消息队列
最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...
- Redis分布式锁---完美实现
这几天在做项目缓存时候,因为是分布式的所以需要加锁,就用到了Redis锁,正好从网上发现两篇非常棒的文章,来和大家分享一下. 第一篇是简单完美的实现,第二篇是用到的Redisson. Redis分布式 ...
- Redis分布式锁的实现
前段时间,我在的项目组准备做一个类似美团外卖的拼手气红包[第X个领取的人红包最大],基本功能实现后,就要考虑这一操作在短时间内多个用户争抢同一资源的并发问题了,类似于很多应用如淘宝.京东的秒杀活动场景 ...
随机推荐
- 使用Redis+SpringBoot实现定时任务测试
Redis实现定时任务是基于对RedisKey值的监控 具体代码实现: 代码GitHub地址:https://github.com/Tom-shushu/Project 建一个SpringBoot项目 ...
- Debian安装Redis服务
Debian安装Redis服务 安装命令 apt-get update apt-get install redis-server 等待安装完成 配置密码 编辑Redis的配置文件/etc/redis/ ...
- Android 中的property_get/property_set
Android 中的property_get/property_set 背景 在安卓中调试Linux驱动层以及应用层之间的一些功能时,需要获取一些属性. 参考: https://blog.csdn.n ...
- 解决:编译安卓源码时 JDK 报错 error='Not enough space' (errno=12)
背景 在编译 Android 10 代码的时候,OpenJDK发现报错: OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(.., . ...
- 修改Git Commit提交记录的用户名Name和邮箱Email
修改Git 本次Commit提交记录的用户名Name和邮箱Email git commit --amend --author="new-name <xxx@new.com>&qu ...
- nicegui 第一次
from nicegui import ui from ex4nicegui.reactive import rxui from ex4nicegui import to_ref,ref_comput ...
- VUE商城项目 - 项目优化上线 - 手稿
- nodejs-mime类型
mime是一个互联网标准,通过设定它就可以设定文件在浏览器的打开方式. mime使用方法: 使用mime模块查询文件的MIME类型: mime.getType('/path/to/file.txt') ...
- yb课堂 核心数据库表字段设计和测试数据准备 《一》
设计对应的表字段(统一使用Innodb引擎,mysql5.7) video_banner video chapter episode video_order user 数据库脚本 CREATE TAB ...
- django 信号判断是新增、修改还是删除
在Django的信号处理器中,你可以使用一些方法来确定信号是关于新增(create).修改(update)还是删除(delete)的.这通常涉及到检查 created 和 instance 参数的值. ...