一、Nuget引入 StackExchange.RedisDistributedLock.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分布式锁防止缓存击穿的更多相关文章

  1. Redis 分布式锁及缓存注释的使用方法

    使用工具:Apache an 测压命令: ab -n 100 -c 100 http://www.baidu.com -n代表模拟100个请求,-c代表模拟100个并发,相当于100个人同时访问 ab ...

  2. SpringBoot集成Redis分布式锁以及Redis缓存

    https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...

  3. SpringBoot集成Redis 一 分布式锁 与 缓存

    1.添加依赖及配置(application.yml) <!-- 引入redis依赖 --> <dependency> <groupId>org.springfram ...

  4. 【分布式缓存系列】集群环境下Redis分布式锁的正确姿势

    一.前言 在上一篇文章中,已经介绍了基于Redis实现分布式锁的正确姿势,但是上篇文章存在一定的缺陷——它加锁只作用在一个Redis节点上,如果通过sentinel保证高可用,如果master节点由于 ...

  5. Redis分布式锁前世今生

    1.redis锁前世即基于单Redis节点的分布式锁,诸如setkey value px milliseconds nx 前世者,必将经历种种磨砺,才能稍微符合一些主流.推荐自测非常好用的redis工 ...

  6. Redis 分布式锁|从青铜到钻石的五种演进方案

    缓存系列文章: 缓存实战(一):20 图 |6 千字|缓存实战(上篇) 缓存实战(二):Redis 分布式锁|从青铜到钻石的五种演进方案 缓存实战(三):分布式锁中的王者方案 - Redisson 上 ...

  7. Redis分布式锁

    Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...

  8. redis分布式锁和消息队列

    最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...

  9. Redis分布式锁---完美实现

    这几天在做项目缓存时候,因为是分布式的所以需要加锁,就用到了Redis锁,正好从网上发现两篇非常棒的文章,来和大家分享一下. 第一篇是简单完美的实现,第二篇是用到的Redisson. Redis分布式 ...

  10. Redis分布式锁的实现

    前段时间,我在的项目组准备做一个类似美团外卖的拼手气红包[第X个领取的人红包最大],基本功能实现后,就要考虑这一操作在短时间内多个用户争抢同一资源的并发问题了,类似于很多应用如淘宝.京东的秒杀活动场景 ...

随机推荐

  1. KEIL5 编译生成 hex、bin文件

    --- title: file_name date: 2020-05-31 10:27:10 categories: tags: - stm32 - keil - config --- KEIL 工程 ...

  2. uboot 修改代码 增加 环境变量

    --- title: uboot修改代码增加环境变量 date: 2019-12-27 21:26:39 categories: tags: - uboot --- 以"tftp下载kern ...

  3. 使用explain优化慢查询的业务场景分析

    问:你最害怕的事情是什么? 答:搓澡 问:为什么? 答:因为有些人一旦错过,就不在了 Explain 这个词在不同的上下文中有不同的含义.在数据库查询优化的上下文中,"EXPLAIN&quo ...

  4. 3568F-Linux-RT系统测试手册

  5. mobaXterm 查看密码

    参考:MobaXterm中密码的查看方式 检查是否把密码保存到了注册表中 然后从https://github.com/HyperSine/how-does-MobaXterm-encrypt-pass ...

  6. Java接口如何动态返回指定的对象字段属性

    经常遇到的问题 在实际得开发过程中,我们经常会遇到以下场景,我们后端请求某个接口后获取到得数据,不希望将所有字段都返回给前端,那么我们需要封装,或者过滤一些不必要得字段后返回给前端. 不完美的解决方案 ...

  7. Spark大数据集群日常开发过程遇到的异常及解决思路汇总

    总结/朱季谦 一.出现java.lang.IllegalAccessError: tried to access method com.google.common.base.Stopwatch.()V ...

  8. Luogu P2036 [COCI2008-2009 #2] PERKET

    今天我们来看一道题:Luogu P2036 [COCI2008-2009 #2] PERKET 这道题不难,典型的暴力枚举 由于食材数量随机,无法直接用循环解,但是可以使用递归 \(MY_{CODE: ...

  9. LabVIEW图标编辑器中的文本变得模糊

    问题详述 在LabVIEW图标编辑器中将文本添加到VI图标时,如果我将字体大小设置为小于10,文本会变得模糊.当字体大小设置为大于11时,文本会正常地显示,但是字体则变得太大而无法放入图标中. 真难看 ...

  10. [Unity] Dreamteck Splines实现沿路径移动功能

    Dreamteck Splines实现沿路径移动功能 最近有一个"让物体沿固定路径移动"的需求,因此接触到了Dreamteck Splines插件. Dreamteck Splin ...