一、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. 灰色预测GM(1,1)模型的理论原理

    灰色预测是对时间有关的灰色过程进行预测.通过建立相应的微分方程模型,从而预测事物未来发展趋势的状况. 由于笔者的水平不足,本章只是概括性地介绍GM(1,1)模型的理论原理,便于对初学者的初步理解 目录 ...

  2. SpringBoot 对接美团闪购,检验签名,获取推送订单参数,text转json

    接口文档地址 订单推送(已确定订单):https://open-shangou.meituan.com/home/docDetail/177 签名算法:https://opendj.meituan.c ...

  3. Swagger注解说明

    常用注解: - @Api()用于类: 表示标识这个类是swagger的资源 - @ApiOperation()用于方法: 表示一个http请求的操作 - @ApiParam()用于方法,参数,字段说明 ...

  4. 【原创软件】第6期:极简SciHub论文下载器

    一.背景 因为科研需求下载英文论文,省得自己去找有效的scihub网址,特此写了一个基于c#和wpf的小软件. 二.使用方法 只需要输入doi即可,点击[打开浏览器下载论文]即可跳转浏览器进行下载.下 ...

  5. jQuery 插件autocomplete 应用

    项目中有时会用到自动补全查询,就像Google搜索框.淘宝商品搜索功能,输入汉字或字母,则以该汉字或字母开头的相关条目会显示出来供用户选择, autocomplete插件就是完成这样的功能. auto ...

  6. SQL Server 验证某栏位是否存在某字符串(CHARINDEX)

    SELECT * FROM LiuJun_PKqitchqi WHERE CHARINDEX('230527Z3258',qr_code) > 0

  7. [oeasy]python0136_接收输入_input函数_字符串_str

    输入变量 回忆上次内容 上次研究了 一行赋值多个变量 a = b = 5 a, b = 7, 8   还研究了 标识符的惯用法 python使用的是 snake_case蛇形命名法 用下划线 分隔开小 ...

  8. Python爬虫(5-10)-编解码、ajax的get请求、ajax的post请求、URLError/HTTPError、微博的cookie登录、Handler处理器

    五.编解码(Unicode编码) (1)GET请求 所提方法都在urllib.parse.路径下 get请求的quote()方法(适用于只提交一两个参数值) url='http://www.baidu ...

  9. ElementUI Dialog 结合Vue实现对话框body“二分”布局

    Dialog 结合Vue实现对话框body"二分"布局 需求描述 如下图, 把对话框body内容部分,分成上下两部分,其中上部分高度根据窗口大小动态调整,如果内容过多,则出现滚动条 ...

  10. c++ 快速复习第一部份

    去年有事无事学过一c++ ,,由于工作用不上,学来没有用,所以学得乱七八的,最近需要复习一下,因为最近想学习一下硬 件相关 第一   引用头文件和自定义头 #include <iostream& ...