.NET中使用Redis大全

一、存放数据

安装:Install-Package StackExchange.Redis

建立连接

cs 复制代码
ConfigurationOptions configurationOptions = new ConfigurationOptions
{
    EndPoints = { { "127.0.0.1", 6379 } }, // Redis服务器地址和端口  
};
ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(configurationOptions);
IDatabase RedisDb = connection.GetDatabase();

1、字符串

存放

cs 复制代码
RedisDb.StringSet("Mystring", "你好");
RedisDb.StringSetAsync("MystringAsync", "你好");

获取

cs 复制代码
Console.WriteLine(RedisDb.StringGet("Mystring"));
Console.WriteLine(await RedisDb.StringGetAsync("MystringAsync"));

删除

cs 复制代码
RedisDb.KeyDelete("Mystring");
RedisDb.KeyDeleteAsync("MystringAsync");

2、哈希

存放

cs 复制代码
RedisDb.HashSet("MyHash",new HashEntry[]
{
    new HashEntry("a","小明"),
    new HashEntry("b","小红"),
    new HashEntry("c","小军"),
});
RedisDb.HashSet("MyHash", "d", "小蓝");

获取

cs 复制代码
Console.WriteLine(RedisDb.HashGet("MyHash", "b")); //获取单个字段
foreach(var item in RedisDb.HashGetAll("MyHash"))//获取表中所有字段
{
    Console.WriteLine(item.Name+"===="+ item.Value);
}

删除

cs 复制代码
RedisDb.HashDelete("MyHash", "a");//删除单个字段
RedisDb.KeyDelete("MyHash");//删除整个哈希表

3、列表

存放

cs 复制代码
RedisDb.ListLeftPush("Mylist", "小绿");//列表头部添加
RedisDb.ListRightPush("Mylist", "小蓝");//列表尾部添加

获取

cs 复制代码
RedisValue[] listitem = RedisDb.ListRange("Mylist",0,-1);//范围获取列表的值,(0到-1为所有值)(0到2为获取下标为0到2的值)
foreach(var item in listitem)
{
    Console.WriteLine(item);
}

删除

cs 复制代码
Console.WriteLine(RedisDb.ListLeftPop("Mylist"));//从列表头部取出值并删除
Console.WriteLine(RedisDb.ListRightPop("Mylist"));//从列表尾部取出值并删除
RedisDb.ListRemove("Mylist","小绿");//删除指定元素
RedisDb.KeyDelete("Mylist");//删除整个列表

4、集合

是一个无序且不包含重复元素的字符串集合,相同值只会存储一个。

存放

cs 复制代码
bool isboot = RedisDb.SetAdd("Myset", "小绿");//会返回一个bool类型值,true为存放成功,false为存放失败,同时也表示该集合中存在该值

获取

cs 复制代码
RedisValue[] setMembers = RedisDb.SetMembers("Myset");//获取集合中所有值
foreach(RedisValue member in setMembers)
{
    Console.WriteLine(member);
}
Console.WriteLine(RedisDb.SetContains("Myset", "小绿"));//集合中是否有该值,true为有,反之

删除

cs 复制代码
bool isRemoved = RedisDb.SetRemove("Myset", "小绿");//true删除成功,false删除失败,集合中无该值

二、原子性

原子性是指一系列操作在执行过程中不会被其他操作打断或干扰,即这些操作要么全部成功执行,要么全部不执行,这确保了数据的一致性和正确性。

cs 复制代码
// 创建事务  
var tran = RedisDb.CreateTransaction();

// 添加一个条件:表"Mylist" 必须存在,否则事务不执行  
tran.AddCondition(Condition.KeyExists("Mylist"));

// 添加要执行的命令  
tran.ListLeftPushAsync("Mylist", "你好");
tran.ListRightPushAsync("Mylist", "不好");

// 执行事务  
bool committed = tran.Execute();//返回false条件不满足,事件不执行,上面操作不执行

三、数据持久化

Redis 提供了几种不同的持久化策略,其中最常见的是 RDB(Redis DataBase)快照和 AOF(Append Only File)日志。

两种方式的配置皆在Redis 服务器安装目录下的redis.conf文件当中配置,列如我这里的是redis.windows.conf文件,需要注意寻找以.conf结尾的文件。

1、RDB快照

RDB快照是在指定的时间间隔内,Redis 会创建一个数据快照并将其写入磁盘。

默认情况下,RDB 快照可能是启用的,可以通过查找save指令来确认和修改策略,save指令的格式为save <seconds> <changes>,表示在指定的秒数内如果有指定数量的键被修改,则创建一个快照,我们在.conf文件中查找save <seconds> <changes>,在下面进行配置。如:

bash 复制代码
save 900 1  # 如果在 900 秒(15 分钟)内至少有 1 个键被修改,则保存快照  
save 300 10 # 如果在 300 秒(5 分钟)内至少有 10 个键被修改,则保存快照  
save 60 10000 # 如果在 60 秒内至少有 10000 个键被修改,则保存快照

配置完成之后需要保存然后重启Redis服务器,注意:频繁的快照可能会对 Redis 服务器的性能产生影响,特别是在高负载的情况下。

2、AOF 日志

AOF 日志是Redis 将每一个写命令追加到一个日志文件中。当 Redis 重启时,它会重新执行这些命令来恢复数据。

我们找到appendonly配置项,将其设置为yes,这即为启用AOF 日志。

bash 复制代码
appendonly yes

配置 AOF 文件目录:

bash 复制代码
appendfilename "文件名"  # AOF 日志文件名  
dir /.../.../.../       # AOF 日志文件存储目录

配置 AOF 同步策略 :

always 表示每个写命令都立即同步到磁盘,这提供了最高的数据安全性但可能会降低性能。

everysec 表示每秒同步一次。

no 表示不同步,可能会丢失数据但提供了更高的性能。如:

bash 复制代码
appendfsync everysec

注意:AOF 日志可能会对磁盘 I/O 产生较高的负载。

四、分布式锁

分布式锁用于在分布式系统中控制对共享资源的访问,该方法的实现主要是利用Redis在对一个字符串类型的数据进行保存时,如果该字符串数据的键存在,那么会导致操作失败,利用这个特性就可以制作一把锁,这里的锁就是键值对,这个键存在就说明正在被使用,需要等待一段时间。

创建一个分布式锁的类RedisDistributedLock继承IDisposable并且实现父类的Dispose方法。

cs 复制代码
public class RedisDistributedLock : IDisposable
{
    private readonly IDatabase _db;
    private readonly string _lockKey;
    private readonly RedisValue _requestId;
    private bool _isDisposed;

    public RedisDistributedLock(IDatabase db, string lockKey, RedisValue redisValue, TimeSpan lockTimeout, TimeSpan lockWaitTimeout)
    {
        _db = db;
        _lockKey = lockKey;
        _requestId = redisValue;

        // 尝试获取锁,如果Redis数据库中存在会返回false
        var acquired = _db.StringSet(_lockKey, _requestId.ToString(), lockTimeout, When.NotExists, CommandFlags.DemandMaster);
        if (!acquired)
        {
            // 如果锁已经被其他客户端持有,则等待一段时间再尝试获取  
            var waitStart = DateTime.UtcNow;
            while ((DateTime.UtcNow - waitStart) < lockWaitTimeout)
            {
                Thread.Sleep(100); // 等待一段时间(例如100毫秒)  
                acquired = _db.StringSet(_lockKey, _requestId, lockTimeout, When.NotExists, CommandFlags.DemandMaster);
                if (acquired) break;
            }
        }

        if (!acquired)
        {
            throw new TimeoutException("Could not acquire the lock within the specified timeout.");
        }
    }

    //这个接口主要用于释放非托管资源或执行任何必要的清理操作
    public void Dispose()
    {
        if (!_isDisposed)
        {
            _isDisposed = true;

            // 释放锁:只有锁的持有者才能释放锁  
            if (_db.StringGet(_lockKey) == _requestId)
            {
                _db.KeyDelete(_lockKey);
            }
        }
    }
}

需要注意的是,我们创建利用完锁之后, 需要对该锁移除,这样才能保障下一个任务会执行,否则,一直不删除这个锁,会导致当前任务完成,后续任务一直处于等待状态,这里我们利用了Dispose方法对锁(也就是键值对)的删除。

使用分布式锁

cs 复制代码
// 当离开 using 块时,锁会自动释放
using (var lockInstance = new RedisDistributedLock(RedisDb, "MyString","你好", TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(5)))
{
    // 在这里执行需要同步的代码  
}

下面两个参数是我们所使用过的两个参数,这两个参数都是一个枚举。

1、when参数

用于确定何时应用过期时间。

When.Always:无论键是否已存在,都应用过期时间。如果键不存在,操作会失败。

When.Exists:仅当键已存在时应用过期时间。如果键不存在,操作不会执行,并且不会有任何错误。

When.NotExists:仅当键不存在时尝试设置键并应用过期时间。如果键已存在,操作不会执行,并且不会有任何错误。

2、CommandFlags参数

为Redis命令提供额外的选项和指示。

None:默认值,表示没有额外的标志被设置。

FireAndForget:此标志用于指示命令应在后台执行,并且调用者不需要等待结果。当使用此标志时,被调用的方法会立即返回,而命令的执行则会在后台异步进行。

PreferMaster:如果主服务器可用,此标志指示命令应在主服务器上执行。但请注意,这个标志也允许读操作在从服务器上执行。

DemandMaster:此标志强制命令只在主服务器上执行。

PreferSlave:如果从服务器可用,此标志指示命令应优先在从服务器上执行。

五、发布/订阅

发布/订阅模式的三个主要角色:

发送者(Publisher):负责发布消息到频道。

订阅者(Subscriber):订阅频道并接收从该频道发布的消息。

频道(Channel):发送者和订阅者之间传递消息的桥梁。

发布订阅的基本流程:订阅者通过Subscribe订阅一个或多个频道,发布者通过Publish向频道中发布消息,Redis接收到发布的消息存储在内部缓冲区中,一旦有订阅者订阅了相应的频道,Redis会将该频道缓冲区中的消息发送给订阅者,如果订阅者在消息发布后才进行订阅,那么它将不会收到之前发布的消息。

注意:Redis服务器重启或者断连等,消息会丢失。

1、发布

cs 复制代码
ConfigurationOptions configurationOptions = new ConfigurationOptions
{
    EndPoints = { { "127.0.0.1", 6379 } }, // Redis服务器地址和端口  
};
ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(configurationOptions);
IDatabase RedisDb = connection.GetDatabase();

// 发布消息到频道  
RedisDb.Publish("mychannel", "你好");

connection.Close();
connection.Dispose();

2、订阅

这是一个新的项目,两个为不同项目(可以跨进程发送消息)。

cs 复制代码
ConfigurationOptions configurationOptions = new ConfigurationOptions
{
    EndPoints = { { "127.0.0.1", 6379 } }, // Redis服务器地址和端口  
};
ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(configurationOptions);

ISubscriber subscriber = connection.GetSubscriber();
// 订阅一个频道  
subscriber.Subscribe("mychannel", (channel, message) =>
{
    Console.WriteLine(channel+"===="+message);
});

// 等待用户输入,以保持程序运行并接收消息  
Console.ReadKey();

// 取消订阅并关闭连接  
subscriber.UnsubscribeAll();

connection.Close();
connection.Dispose();
相关推荐
kyranhan18 分钟前
c#使用COM接口设置excel单元格宽高匹配图片,如何计算?
c#·excel
工业3D_大熊29 分钟前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
编程、小哥哥1 小时前
设计模式之抽象工厂模式(替换Redis双集群升级,代理类抽象场景)
redis·设计模式·抽象工厂模式
测试界的酸菜鱼1 小时前
C# NUnit 框架:高效使用指南
开发语言·c#·log4j
小码编匠2 小时前
领域驱动设计(DDD)要点及C#示例
后端·c#·领域驱动设计
工业甲酰苯胺2 小时前
C# 单例模式的多种实现
javascript·单例模式·c#
yi碗汤园2 小时前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
Humbunklung4 小时前
一种EF(EntityFramework) MySQL修改表名去掉dbo前缀的方法
数据库·mysql·c#
IT规划师10 小时前
开源 - Ideal库 - 常用时间转换扩展方法(二)
开源·.net core·时间转换·ideal库