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个领取的人红包最大],基本功能实现后,就要考虑这一操作在短时间内多个用户争抢同一资源的并发问题了,类似于很多应用如淘宝.京东的秒杀活动场景 ...
随机推荐
- OpenCV程序:OCR文档扫描
一.文档扫描 代码 import cv2 import numpy as np #==============================计算输入图像的四个顶点的坐标=============== ...
- Nuxt3 的生命周期和钩子函数(十)
title: Nuxt3 的生命周期和钩子函数(十) date: 2024/6/30 updated: 2024/6/30 author: cmdragon excerpt: 摘要:本文详细介绍了Nu ...
- Spring5.X bean自动装配Autowire属性
属性注入 set方法.构造函数.POJO.list.map.ref,属于手工注入,点我直达 Spring自动注入 使用<bean>元素的autowire属性为一个bean定义指定自动装配模 ...
- 基于FileZilla上传、下载服务器数据的方法
本文介绍FileZilla软件的下载.配置与使用方法. 在之前的博客中,我们提到了下载高分遥感影像数据需要用到FTP(文件传输协议,File Transfer Protocol)软件FileZ ...
- Linux-Cgroup V2 初体验
本文主要记录 Linux Cgroup V2 版本基本使用操作,包括 cpu.memory 子系统演示. 1. 开启 Cgroup V2 版本检查 通过下面这条命令来查看当前系统使用的 Cgroups ...
- WebGL加载本地模型
前言 大部分的webgl框架,比如threejs和babylon等,都可以加载obj和gltf模型. 我们的引擎,基于three封装,同样有加载模型的loader,因此加载obj和gltf模型也是很简 ...
- oeasy教您玩转vim - 80 - # 宏macro
宏 macro 回忆 这次我们了解了编码格式 屏幕显示的encoding 文件保存的fileencoding 不能搞乱了 一般用什么编的就用什么解 解铃还须系铃人 打开不正确的话,就要切到正确的上 ...
- .Net4.5及.Net Core2.1下的HttpClient使用详解
一.HTTP系列演进 方式 说明 HttpWebRequest .NET早期版本,同步方式 WebClient HttpWebRequest的封装简化版,同步方式 HttpClient .NET4.5 ...
- JMeter 配置元件之按条件读取CSV Data Set Config
实践环境 win10 JMeter 5.4.1 需求描述 需求是这样的,需要压测某个接口(取消分配接口),请求这个接口之前,需要先登录系统(物流WMS系统),并在登录后,选择并进入需要操作的仓库,然后 ...
- c++ 异常记录
vector的排序使用的iterator必须先+1 再-1,否则报溢出警告,不能正确排序遍历map获取到的是对象副本,需要引用,不能直接拿来做引用 fortmat只支持原始类型,wstring,str ...