一、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. 15分钟面试被5连CALL,你扛得住么?

    最近一个朋友跳槽找工作,跟V 哥说被15分钟内一个问题5连 CALL,还好是自己比较熟悉的技术点,面试官最后跟他说,面了几十个人,你是第一个回答比较满意的,我好奇都是什么问题,原来是关于锁的问题连环问 ...

  2. Mysql 聚合函数嵌套使用

    Mysql 聚合函数嵌套使用 目的:Mysql 聚合函数嵌套使用 聚合函数不可以直接嵌套使用,比如: max(count(*)) 思路:但是可以嵌套子查询使用(先分组取出count值, 再将count ...

  3. C++中UNIX时间戳与日期互转

    C++中UNIX时间戳与日期互转 使用time.h头文件 localtime 可以把时间戳转为 tm 结构体, tm结构体中可以格式化输出时间 mktime可以把tm结构体转为时间戳 tm 结构体中: ...

  4. Linux 内核:设备驱动模型(2)driver-bus-device与probe

    Linux 内核:设备驱动模型(2)driver-bus-device与probe 系列:Linux 内核:设备驱动模型 学习总结 参考: https://blog.csdn.net/lizuobin ...

  5. ZYNQ:PetaLinux工程更新HDF文件的脚本

    PetaLinux工程更新HDF文件的脚本 参考:PetaLinux工程更新HDF文件的脚本 工程师可能同时使用多个Vivado工程,以便测试不同的硬件配置.如果能够让一个PetaLinux工程支持多 ...

  6. setsocket、getsocket 函数详解

    背景 以前用到socket的时候会调用setsocket进行设置,现在整理有关的笔记的时候,重新查阅资料发现有点奇怪,发现大家比较少使用到这个. setsocket/getsocket #includ ...

  7. “国产双系统”出炉,RK3568J非对称AMP:Linux+RTOS/裸机

    "非对称AMP"双系统是什么 AMP(Asymmetric Multi-Processing),即非对称多处理架构."非对称AMP"双系统是指多个核心相对独立运 ...

  8. Java 方法中循环调用具有事务的方法

    在Java中,循环调用一个具有事务的方法时,需要特别注意事务的边界和管理.通常,事务的边界是由框架(如Spring)来控制的,确保方法执行时数据的完整性和一致性.然而,在循环中调用事务方法时,每个调用 ...

  9. [oeasy]python0004_游乐场_和python一起玩耍_python解释器_数学运算

    和python玩耍 Python 回忆 上次 了解shell环境中的命令 命令 作用 whoami 显示当前用户名 pwd 显示当前文件夹 ls 列出当前文件夹下的内容 python3 仿佛进入大于号 ...

  10. [oeasy]python0007-调试程序_debug

    ​ 调试程序 回忆上次内容 ​py​​ 的程序是按照顺序执行的 是一行行挨排解释执行的 程序并不是数量越多越好 kpi也在不断演化 ​ 编辑 写的代码越多 出现的bug就越多 什么是bug 如何找bu ...