C# Redis 框架开发技术详解
引言
Redis 是一个高性能的键值存储系统,广泛用于缓存、消息队列和实时分析等场景。在 C# 中,有几个著名的库和框架可以方便地与 Redis 进行交互。以下是几个常用的 C# Redis 库:
-
StackExchange.Redis: 这是目前最流行、最推荐的 C# Redis 客户端,由 StackExchange 团队(也就是开发 Stack Overflow 的团队)开发和维护。
-
ServiceStack.Redis: 这是另一个功能齐全的 Redis 客户端,但需要注意的是,ServiceStack 是一个商业产品,对于非开源项目和商业用途可能需要付费。
-
CSRedis: 这也是一个不错的 Redis 客户端,提供了简单易用的 API。
其中最常用的是 StackExchange.Redis。本篇文章将深入解析 StackExchange.Redis 的概念、原理、作用、应用场景及应用实例。
一、概念与原理
什么是 Redis
Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。Redis 的主要特点是速度快、支持持久化、丰富的数据结构和简单易用的 API。
什么是 StackExchange.Redis
StackExchange.Redis 是一个由 StackExchange 团队开发的 C# 客户端库,用于与 Redis 进行交互。它提供了高效、稳定且功能丰富的 API,适合在 .NET 应用中使用。
StackExchange.Redis 的基本原理
StackExchange.Redis 利用 Redis 的发布/订阅模型和数据结构,通过 TCP 连接与 Redis 服务器进行通信。它内部维护了连接池,以便高效地管理和复用连接,同时提供了丰富的异步操作支持。
二、作用与应用场景
作用
- 缓存:通过缓存热点数据,减少数据库的压力,提高系统性能。
- 消息队列:利用 Redis 的发布/订阅模型和列表结构,实现消息队列功能。
- 分布式锁:利用 Redis 的原子操作和过期时间,实现分布式锁。
- 计数器:利用 Redis 的字符串操作,快速实现计数器功能。
- 会话存储:将用户会话数据存储在 Redis 中,实现分布式会话管理。
- 排行榜:利用 Redis 的有序集合实现高效的排行榜功能。
应用场景
- 电商平台:缓存商品信息、库存数据、用户购物车等。
- 社交网络:缓存用户信息、好友列表、聊天记录等。
- 游戏后台:缓存玩家数据、排行榜、游戏状态等。
- 实时分析:缓存实时统计数据、分析结果等。
- 内容管理系统:缓存文章、评论、标签等。
三、应用实例
下面将通过一个详细的实例,展示如何在 C# 项目中使用 StackExchange.Redis 进行 Redis 操作。
项目准备
安装 StackExchange.Redis
首先,需要在项目中安装 StackExchange.Redis 包。可以使用 NuGet 包管理器进行安装。
使用 NuGet 命令行安装:
Install-Package StackExchange.Redis
或者使用 .NET CLI 安装:
dotnet add package StackExchange.Redis
基本操作实例
连接 Redis
首先,我们需要连接到 Redis 服务器。以下是连接 Redis 的代码示例:
using System;
using StackExchange.Redis;class Program
{private static ConnectionMultiplexer redis;private static IDatabase db;static void Main(string[] args){// 连接到 Redis 服务器redis = ConnectionMultiplexer.Connect("localhost"); // 如果 Redis 运行在本地,使用 "localhost"db = redis.GetDatabase();Console.WriteLine("Connected to Redis");// 关闭连接redis.Close();Console.WriteLine("Redis connection closed.");}
}
在上面的代码中,我们使用 ConnectionMultiplexer.Connect 方法连接到 Redis 服务器,并使用 GetDatabase 方法获取一个 Redis 数据库实例。
字符串操作
下面展示了如何进行字符串类型键值对的操作:
// 设置一个键值对
db.StringSet("mykey", "Hello, Redis!");// 获取键对应的值
string value = db.StringGet("mykey");
Console.WriteLine("mykey: " + value);// 设置一个带过期时间的键
db.StringSet("mykey_expire", "This key will expire in 10 seconds", TimeSpan.FromSeconds(10));// 获取带过期时间的键的值
value = db.StringGet("mykey_expire");
Console.WriteLine("mykey_expire: " + value);// 等待 10 秒后再次尝试获取
System.Threading.Thread.Sleep(10000);
value = db.StringGet("mykey_expire");
Console.WriteLine("mykey_expire after 10 seconds: " + value);
哈希操作
下面展示了如何进行哈希类型数据的操作:
// 设置哈希字段值
db.HashSet("user:1", new HashEntry[]
{new HashEntry("name", "Alice"),new HashEntry("age", 30)
});// 获取哈希字段值
string name = db.HashGet("user:1", "name");
int age = (int)db.HashGet("user:1", "age");
Console.WriteLine($"User: name={name}, age={age}");// 获取所有哈希字段值
HashEntry[] hashEntries = db.HashGetAll("user:1");
foreach (var entry in hashEntries)
{Console.WriteLine($"{entry.Name}: {entry.Value}");
}
列表操作
下面展示了如何进行列表类型数据的操作:
// 添加元素到列表
db.ListRightPush("mylist", "item1");
db.ListRightPush("mylist", "item2");
db.ListRightPush("mylist", "item3");// 获取列表长度
long length = db.ListLength("mylist");
Console.WriteLine($"List length: {length}");// 获取列表中的所有元素
for (long i = 0; i < length; i++)
{string item = db.ListGetByIndex("mylist", i);Console.WriteLine($"List item {i}: {item}");
}// 移除并获取列表中的第一个元素
string firstItem = db.ListLeftPop("mylist");
Console.WriteLine($"First item: {firstItem}");
集合操作
下面展示了如何进行集合类型数据的操作:
// 添加元素到集合
db.SetAdd("myset", "item1");
db.SetAdd("myset", "item2");
db.SetAdd("myset", "item3");// 获取集合中的所有元素
RedisValue[] setItems = db.SetMembers("myset");
foreach (var item in setItems)
{Console.WriteLine($"Set item: {item}");
}// 判断元素是否在集合中
bool isMember = db.SetContains("myset", "item2");
Console.WriteLine($"Is item2 in set: {isMember}");
有序集合操作
下面展示了如何进行有序集合类型数据的操作:
// 添加元素到有序集合
db.SortedSetAdd("myzset", "item1", 1.0);
db.SortedSetAdd("myzset", "item2", 2.0);
db.SortedSetAdd("myzset", "item3", 3.0);// 获取有序集合中的所有元素
SortedSetEntry[] zsetItems = db.SortedSetRangeByRankWithScores("myzset");
foreach (var item in zsetItems)
{Console.WriteLine($"ZSet item: {item.Element}, score: {item.Score}");
}// 获取元素的分数
double score = db.SortedSetScore("myzset", "item2");
Console.WriteLine($"Score of item2: {score}");
高级操作实例
发布/订阅
Redis 的发布/订阅模型非常适合用于消息队列和事件通知。下面是发布/订阅的示例:
using System;
using StackExchange.Redis;class Program
{// 定义 Redis 连接和数据库实例private static ConnectionMultiplexer redis;private static IDatabase db;static void Main(string[] args){// 连接到 Redis 服务器redis = ConnectionMultiplexer.Connect("localhost"); // 这里假设 Redis 运行在本地db = redis.GetDatabase(); // 获取 Redis 数据库实例ISubscriber sub = redis.GetSubscriber(); // 获取 Redis 发布/订阅实例// 订阅频道 "mychannel"sub.Subscribe("mychannel", (channel, message) =>{// 当收到消息时,打印消息内容Console.WriteLine($"Received message: {message}");});// 发布 5 条消息到频道 "mychannel"for (int i = 0; i < 5; i++){sub.Publish("mychannel", $"Hello, Redis! {i}"); // 发布消息// 等待 1 秒再发布下一条消息System.Threading.Thread.Sleep(1000);}// 关闭 Redis 连接redis.Close();}
}
代码详解
定义 Redis 连接和数据库实例:
private static ConnectionMultiplexer redis; private static IDatabase db;
ConnectionMultiplexer redis: 用于管理 Redis 连接。IDatabase db: 用于执行 Redis 数据库操作。连接到 Redis 服务器:
redis = ConnectionMultiplexer.Connect("localhost"); db = redis.GetDatabase(); ISubscriber sub = redis.GetSubscriber();
ConnectionMultiplexer.Connect("localhost"): 连接到本地运行的 Redis 服务器。redis.GetDatabase(): 获取 Redis 数据库实例。redis.GetSubscriber(): 获取 Redis 发布/订阅实例。订阅频道 "mychannel":
sub.Subscribe("mychannel", (channel, message) => {Console.WriteLine($"Received message: {message}"); });
sub.Subscribe("mychannel", ...):订阅频道 "mychannel"。- 当收到消息时,匿名委托
(channel, message) => { ... }会被调用,打印消息内容。发布消息到频道 "mychannel":
for (int i = 0; i < 5; i++) {sub.Publish("mychannel", $"Hello, Redis! {i}");System.Threading.Thread.Sleep(1000); }
- 使用
for循环发布 5 条消息。sub.Publish("mychannel", ...):发布消息到频道 "mychannel"。System.Threading.Thread.Sleep(1000):每次发布后等待 1 秒。关闭 Redis 连接:
redis.Close();
redis.Close():关闭 Redis 连接。
在这个示例中,我们使用 Subscribe 方法订阅了一个频道,并通过 Publish 方法发布消息。
分布式锁
下面展示了如何使用 Redis 实现分布式锁:
using System;
using StackExchange.Redis;public class RedisLock
{// 定义 Redis 数据库实例、锁的键、锁的值及锁的超时时间private readonly IDatabase db;private readonly string lockKey;private readonly string lockValue;private readonly TimeSpan lockTimeout;/// <summary>/// 构造函数,初始化 RedisLock 实例/// </summary>/// <param name="db">Redis 数据库实例</param>/// <param name="lockKey">锁的键</param>/// <param name="lockTimeout">锁的超时时间</param>public RedisLock(IDatabase db, string lockKey, TimeSpan lockTimeout){this.db = db; // 设置 Redis 数据库实例this.lockKey = lockKey; // 设置锁的键this.lockValue = Guid.NewGuid().ToString(); // 生成一个唯一的锁值this.lockTimeout = lockTimeout; // 设置锁的超时时间}/// <summary>/// 尝试获取锁/// </summary>/// <returns>如果成功获取锁,返回 true;否则返回 false</returns>public bool Acquire(){// 尝试设置锁的键和值,并指定超时时间,只有键不存在时才能设置成功return db.StringSet(lockKey, lockValue, lockTimeout, When.NotExists);}/// <summary>/// 释放锁/// </summary>/// <returns>如果成功释放锁,返回 true;否则返回 false</returns>public bool Release(){// 只有当前实例持有的锁值与 Redis 中存储的锁值相同时,才删除锁if (db.StringGet(lockKey) == lockValue){return db.KeyDelete(lockKey); // 删除锁的键}return false;}
}// 使用示例
class Program
{static void Main(string[] args){// 连接到 Redis 服务器var redis = ConnectionMultiplexer.Connect("localhost"); // 这里假设 Redis 运行在本地var db = redis.GetDatabase(); // 获取 Redis 数据库实例// 创建 RedisLock 实例,指定锁的键和超时时间var redisLock = new RedisLock(db, "mylock", TimeSpan.FromSeconds(30));// 尝试获取锁if (redisLock.Acquire()){Console.WriteLine("Lock acquired");// 执行需要锁保护的代码System.Threading.Thread.Sleep(5000); // 模拟需要锁保护的代码执行// 释放锁redisLock.Release();Console.WriteLine("Lock released");}else{Console.WriteLine("Failed to acquire lock");}// 关闭 Redis 连接redis.Close();}
}
在这个示例中,我们使用 StringSet 方法和 When.NotExists 条件来实现分布式锁的获取,使用 KeyDelete 方法来释放锁。
事务操作
Redis 支持事务操作,可以通过 IDatabase.CreateTransaction 方法来创建一个事务,并通过 ITransaction.Execute 方法来执行事务。下面是一个事务操作的示例:
using System;
using StackExchange.Redis;class Program
{static void Main(string[] args){// 连接到 Redis 服务器var redis = ConnectionMultiplexer.Connect("localhost"); // 这里假设 Redis 运行在本地var db = redis.GetDatabase(); // 获取 Redis 数据库实例// 创建一个事务var tran = db.CreateTransaction();// 在事务中设置键值对tran.StringSetAsync("key1", "value1");tran.StringSetAsync("key2", "value2");// 提交事务bool committed = tran.Execute();// 检查事务是否成功提交if (committed){Console.WriteLine("Transaction committed");}else{Console.WriteLine("Transaction failed");}// 关闭 Redis 连接redis.Close();}
}
在这个示例中,我们在事务中设置了两个键值对,并通过 Execute 方法提交事务。
四、结论
通过上面的讲解和示例,我们深入了解了 C# 中使用 StackExchange.Redis 进行 Redis 操作的基本概念、原理、作用、应用场景及具体应用实例。Redis 作为一个高性能的内存数据库,广泛应用于各种场景中,而 StackExchange.Redis 则提供了一个强大且易用的 API,可以帮助我们在 C# 项目中方便地使用 Redis。
通过合理利用 Redis 的强大功能,可以大幅提升系统的性能和稳定性,希望本文能对你在 C# 项目中使用 Redis 提供一些帮助和参考。
相关文章:
C# Redis 框架开发技术详解
引言 Redis 是一个高性能的键值存储系统,广泛用于缓存、消息队列和实时分析等场景。在 C# 中,有几个著名的库和框架可以方便地与 Redis 进行交互。以下是几个常用的 C# Redis 库: StackExchange.Redis: 这是目前最流行、最推荐的 C# Redis 客…...
Rust:Result 和 Error
在 Rust 编程语言中,错误处理是一个核心部分,用于确保程序的健売性和可靠性。Rust 通过 Result 枚举和 Error 特质(trait)来处理错误。 Result 枚举 Result 是一个泛型枚举,用于表示一个操作可能成功或失败。它有两个…...
Python基础(八)——MySql数据库
一.数据库 【库——>表——>数据】 借助数据库对数据进行组织存储,借助SQL语言对数据库、数据进行操作管理 Mysql数据库 下载:https://www.mysql.com/ 查看是否安装配置成功: 安装DBeaver用于Mysql数据库图形化 安装:…...
统一网关--gateway(仅供自己参考)
1、网关的概念: 2、网关的功能: (1):身份认证和权限校验 (2):服务路由(具体的业务路由到具体的服务),负载均衡(多台服务的话ÿ…...
【Leetcode152】分割回文串(回溯 | 递归)
文章目录 一、题目二、思路三、代码 一、题目 二、思路 具体例子和步骤:假设 s "aab",步骤如下: 初始状态: s "aab"path []res [] 第一层递归(外层循环): path []检…...
基于BiGRU+Attention实现风力涡轮机发电量多变量时序预测(PyTorch版)
前言 系列专栏:【深度学习:算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对…...
深入探究 Flask 的应用和请求上下文
目标 读完本文后,您应该能够解释: 什么是上下文哪些数据同时存储在应用程序和请求上下文中在 Flask 中处理请求时,处理应用程序和请求上下文所需的步骤如何使用应用程序和请求上下文的代理如何在视图函数中使用current_app和代理request什么…...
C++学习笔记(30)
二十三、随机数 在实际开发中,经常用到随机数,例如:纸牌的游戏洗牌和发牌、生成测试数据等。 函数原型: void srand(unsigned int seed); // 初始化随机数生成器(播种子)。 int rand(); // 获一个取随机数。…...
Rust GUI框架 tauri V2 项目创建
文章目录 Tauri 2.0创建应用文档移动应用开发 Android 前置要求移动应用开发 iOS 前置要求参考资料 Tauri 2.0 Tauri 是一个构建适用于所有主流桌面和移动平台的轻快二进制文件的框架。开发者们可以集成任何用于创建用户界面的可以被编译成 HTML、JavaScript 和 CSS 的前端框架…...
C++继承(上)
1.继承的概念 继承是一个类继承另外一个类,称继承的类为子类/派生类,被继承的类称为父类/基类。 比如下面两个类,Student和Person,Student称为子类,Person称为父类。 #include<iostream> using namespace std…...
在 Vim 中打开文件并快速查询某个字符
在 Vim 中打开文件并快速查询某个字符,可以按照以下步骤操作: 打开 Vim 并加载文件: vim your_file.txt将 your_file.txt 替换为你要查询的文件名。 进入普通模式(如果你还在插入模式或其他模式下): Es…...
oracle 条件取反
在Oracle数据库中,条件取反主要通过逻辑运算符NOT来实现。NOT是一个单目运算符,用于对指定的条件表达式取反。当条件表达式为真(True)时,NOT运算符的结果就是假(False);反之…...
力扣最热一百题——缺失的第一个正数
目录 题目链接:41. 缺失的第一个正数 - 力扣(LeetCode) 题目描述 示例 提示: 解法一:标记数组法 1. 将非正数和超出范围的数替换 2. 使用数组下标标记存在的数字 3. 找到第一个未标记的位置 4. 为什么时间复杂…...
零基础入门AI:一键本地运行各种开源大语言模型 - Ollama
什么是 Ollama? Ollama 是一个可以在本地部署和管理开源大语言模型的框架,由于它极大的简化了开源大语言模型的安装和配置细节,一经推出就广受好评,目前已在github上获得了46k star。 不管是著名的羊驼系列,还是最新…...
3.接口测试的基础/接口关联(Jmeter工具/场景一:我一个人负责所有的接口,项目规模不大)
一、Jmeter接口测试实战 1.场景一:我一个人负责所有的接口:项目规模不大 http:80 https:443 接口文档一般是开发给的,如果没有那就需要抓包。 请求默认值: 2.请求: 请求方式:get,post 请求路径 请求参数 查询字符串参数…...
【matlab】将程序打包为exe文件(matlab r2023a为例)
文章目录 一、安装运行时环境1.1 安装1.2 简介 二、打包三、打包文件为什么很大 一、安装运行时环境 使用 Application Compiler 来将程序打包为exe,相当于你使用C编译器把C语言编译成可执行程序。 在matlab菜单栏–App下面可以看到Application Compiler。 或者在…...
从底层原理上解释clickhouse查询为什么快
ClickHouse 是一个开源的列式数据库管理系统,以其极高的查询性能著称。为了理解 ClickHouse 查询为什么快,我们需要从以下几个方面进行深入探讨,包括其架构设计、存储引擎、索引结构、并行化策略以及内存管理等底层原理。 1. 列式存储&#…...
FEAD:fNIRS-EEG情感数据库(视频刺激)
摘要 本文提出了一种可用于训练情绪识别模型的fNIRS-EEG情感数据库——FEAD。研究共记录了37名被试的脑电活动和脑血流动力学反应,以及被试对24种情绪视听刺激的分类和维度评分。探讨了神经生理信号与主观评分之间的关系,并在前额叶皮层区域发现了显著的…...
标准库标头 <bit>(C++20)学习
<bit>头文件是数值库的一部分。定义用于访问、操作和处理各个位和位序列的函数。例如,有函数可以旋转位、查找连续集或已清除位的数量、查看某个数是否为 2 的整数幂、查找表示数字的最小位数等。 类型 endian (C20) 指示标量类型的端序 (枚举) 函数 bit_ca…...
redis群集三种模式:主从复制、哨兵、集群
redis群集有三种模式 redis群集有三种模式,分别是主从同步/复制、哨兵模式、Cluster,下面会讲解一下三种模式的工作方式,以及如何搭建cluster群集 ●主从复制:主从复制是高可用Redis的基础,哨兵和集群都是在主从复制…...
智慧小区网络设计避坑指南:华为设备选型、无线覆盖与安全策略实战解析
智慧小区网络设计实战:华为设备选型与无线覆盖避坑指南 当接到智慧小区网络建设项目时,很多工程师会陷入理论完美主义陷阱——画出漂亮的拓扑图,却在实际部署中遭遇信号死角、设备过载、策略冲突等现实问题。本文将从三个真实项目复盘出发&am…...
LeetCode 300. Longest Increasing Subsequence 题解
LeetCode 300. Longest Increasing Subsequence 题解 题目描述 给你一个整数数组 nums,找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,…...
M2LOrder模型在STM32项目中的潜在应用:边缘设备情绪反馈
M2LOrder模型在STM32项目中的潜在应用:边缘设备情绪反馈 最近在捣鼓一个基于STM32的智能硬件项目,想给它加点“人情味”。比如,当用户对它说话时,它能感知到用户的情绪是开心还是沮丧,并给出更贴切的反馈。这听起来很…...
揭秘C++多态:动态行为的核心奥秘
C 多态:面向对象的动态行为核心机制多态性是面向对象编程(OOP)的核心概念之一,它允许对象在运行时根据其实际类型表现出不同的行为。在C中,多态性主要通过虚函数(virtual functions)和继承机制实…...
mitmproxy实战:从环境搭建到HTTPS抓包全攻略
1. 认识mitmproxy:你的网络调试瑞士军刀 第一次听说mitmproxy时,你可能觉得这是个复杂的安全工具。但实际用过后就会发现,它就像网络调试领域的瑞士军刀,能解决各种数据抓包难题。简单来说,mitmproxy是个开源的交互式中…...
【西瓜带你学设计模式 | 第四期 - 抽象工厂模式】抽象工厂模式 —— 定义、核心结构、实战示例、优缺点与适用场景及模式区别
文章目录前言1. 抽象工厂模式是什么?2. 解决什么问题?2.1 有多个“产品维度”,并且需要成套切换2.2 变化点分散导致代码难维护3. 核心结构4. 示例4.1 抽象产品:Slice(切片)4.2 抽象产品:Pulp&am…...
pandas API on Spark 与 pandas / PySpark 互转指南
1. 为什么会有互转需求 pandas API on Spark 的定位很特殊:它既想保留 pandas 的使用体验,又建立在 Spark 的分布式执行之上。因此开发时常见的场景有三种: 已经有 pandas 代码,想迁移到分布式环境已经在用 PySpark DataFrame&…...
OpenClaw 深度研究报告:从开源框架到企业级智能体平台的演进之路
一、核心定位:突破"对话天花板"的执行中枢 OpenClaw(外号"龙虾") 是由奥地利工程师 Peter Steinberger 于 2025 年底开发的本地优先、模型无关的 AI 智能体运行框架。其核心价值主张极为鲜明: “The AI that …...
避坑指南:lidar_align标定IMU外参时,loader.cpp源码修改与运动轨迹设计的那些关键细节
避坑指南:lidar_align标定IMU外参的核心细节与实战优化 在自动驾驶和机器人定位领域,激光雷达与IMU的联合标定是系统搭建的关键环节。许多开发者在初次使用lidar_align工具时会遇到各种问题——从源码适配的困惑到标定结果的不可靠。本文将深入剖析两个最…...
springboot+vue基于web的药店管理系统 药品商城在线购药系统
目录同行可拿货,招校园代理 ,本人源头供货商功能模块分析技术实现要点扩展功能建议项目技术支持源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作同行可拿货,招校园代理 ,本人源头供货商 功能模块分析 后台管理系统(SpringBoot&…...
