Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

时间:2023-12-19 08:12:38

一、简介

Redis有5种基本数据结构,分别是string、list(列表)、hash(字典)、set(集合)、zset(有序集合),这是必须掌握的5种基本数据结构.注意Redis作为一个键值对缓存系统,其所有的数据结构,都以唯一的key(字符串)作为名称,然后通过key来获取对应的数据.

二、.Net开发环境搭建

这个版本,暂时不考虑并发问题,后续的文章会说!
第一步:安装StackExchange.Redis包,我用的是2.0.519版本的.

第二步:编写代码,采用扩展方法的链式编程模式+async/await的编程模型

AppConfiguration.cs  全局配置类

    /// <summary>
/// 全局配置类
/// </summary>
public class AppConfiguration
{
/// <summary>
/// 单例实现,static关键字默认加锁
/// </summary>
static AppConfiguration()
{
Current = new AppConfiguration();
} public static readonly AppConfiguration Current; /// <summary>
/// 配置完Redis之后,所有需要的Redis基础服务对象,都在这里面
/// </summary>
public RedisConfigurations RedisConfigurations { get; set; }
}

FluentConfiguration.cs 链式配置核心类

    /// <summary>
/// 链式编程模式,扩展方法实现
/// </summary>
public static class FluentConfiguration
{
/// <summary>
/// 配置Redis
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="configuration"></param>
/// <returns></returns>
public static AppConfiguration ConfigureRedis<T>(this AppConfiguration configuration) where T: IRedisConfig, new()
{
if (configuration == null)
throw new ArgumentNullException("configuration");
var config = new T();
var redisConfigurations=config.ConfigRedis();
configuration.RedisConfigurations = redisConfigurations;
return configuration;
}
}

RedisConfigurations.cs Redis全局配置共享类

    /// <summary>
/// Redis配置完毕后,返回需要使用的相关对象
/// </summary>
public class RedisConfigurations
{
public IConnectionMultiplexer ConnectionMultiplexer { get; set; }
}

RedisConfig.cs Redis配置类

    /// <summary>
/// Redis配置类
/// </summary>
public class RedisConfig : IRedisConfig
{
/// <summary>
/// 比较耗费资源,所以写入缓存,全局共享
/// 封装了Redis基础服务对象的详细信息
/// </summary>
public static ConnectionMultiplexer ConnectionMultiplexer { get; } /// <summary>
/// 可能存在线程安全的配置,或者只需要初始化一次的配置,放这里
/// </summary>
static RedisConfig()
{
//暂时读配置文件,后期可以用Core的配置文件系统读json文件
var redisServerAdress = ConfigurationManager.AppSettings["RedisServerAdress"];
if (string.IsNullOrEmpty(redisServerAdress))
throw new ApplicationException("配置文件中未找到RedisServer的有效配置");
ConnectionMultiplexer = ConnectionMultiplexer.Connect(redisServerAdress);
} /// <summary>
/// 配置Redis
/// </summary
public RedisConfigurations ConfigRedis()
{
var config = new RedisConfigurations();
config.ConnectionMultiplexer = ConnectionMultiplexer;
return config;
}
}

相关约束接口如下:

    /// <summary>
/// Redis配置约束
/// </summary>
public interface IRedisConfig
{
/// <summary>
/// 配置Redis
/// </summary>
RedisConfigurations ConfigRedis();
} /// <summary>
/// Redis客户端实例约束接口
/// </summary>
public interface IRedisInstance
{ }

RedisClient.cs Redis客户端调用类

    /// <summary>
/// 基于async和await的异步操作的Redis客户端,有效利用CPU资源
/// </summary>
public class RedisClient: IRedisInstance
{
private static RedisConfigurations RedisConfigurations { get; } static RedisClient()
{
RedisConfigurations=AppConfiguration.Current.RedisConfigurations;
} /// <summary>
/// 异步,写入键值对
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public static async Task<bool> StringSetAsync(string key,string value)
{
var db=GetDatabase();
return await db.StringSetAsync(key,value);
} /// <summary>
/// 根据传入键,异步获取对应的值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static async Task<string> StringGetAsync(string key)
{
var db = GetDatabase();
return await db.StringGetAsync(key);
} /// <summary>
/// 异步判断是否存在某个键
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static async Task<bool> KeyExistsAsync(string key)
{
var db = GetDatabase();
return await db.KeyExistsAsync(key);
} /// <summary>
/// 异步删除某个键
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static async Task<bool> KeyDeleteAsync(string key)
{
var db = GetDatabase();
return await db.KeyDeleteAsync(key);
} /// <summary>
/// Redis DataBase工厂方法
/// </summary>
/// <returns></returns>
private static IDatabase GetDatabase()
{
return RedisConfigurations.ConnectionMultiplexer.GetDatabase();
}
}

暂时只扩展了一些方法,或许会持续扩展.

Program.cs 控制台入口类

    class Program
{
static Program()
{
//链式配置Redis
AppConfiguration.Current.ConfigureRedis<RedisConfig>();
} static void Main(string[] args)
{
StringSetGetAsync();
Console.ReadKey();
} static async void StringSetGetAsync()
{
if (await RedisClient.StringSetAsync("name", "xiaochao"))
{
Console.WriteLine("Redis中键为name的值为:{0}", await RedisClient.StringGetAsync("name"));
}
else {
Console.WriteLine("写入异常");
}
}
}

ok,到这里.Net下使用StackExchange.Redis包操作Redis的环境构建完毕.

运行代码:

控制台环境:

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

Redis桌面管理工具

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

Linux下Redis-cli

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

后续的文章都会围绕上面三个操作方式展开.

三、string(字符串)

1、简单键值对操作

字符串string是Redis中最简单的数据类型,内部原理和C#的string类型一样,是一个字符数组.常见的用法是缓存一些用户数据,将用户数据序列化程Json,然后以用户Id作为键值,然后将用户数据存入Redis中.获取的时候,只需要通过用户Id去获取,然后将Json反序列化成对应的实体.

注:Redis的string类型是动态字符串,而且支持修改,这和C#中的string不一样,内部结构类似于C#中的List,有一个初始大小,如果存入string的长度大小大于string的初始大小,那么每次都会扩展1倍的大小.但是字符串最大长度只能为512MB.

代码实战:

(1)、Linux终端

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

(2)、C#控制台

修改控制台方法如下:

        static void Main(string[] args)
{
StringSetGetAsync();
Console.ReadKey();
} static async void StringSetGetAsync()
{
var key = "name";
if (await RedisClient.StringSetAsync(key, "xiaochao"))
{
Console.WriteLine("Redis中键为name的值为:{0}", await RedisClient.StringGetAsync(key));
if (await RedisClient.KeyExistsAsync(key))
{
Console.WriteLine("Redis中,存在key为name的键值对");
}
if (await RedisClient.KeyDeleteAsync(key))
{
Console.WriteLine($"删除键:{key}成功");
if (await RedisClient.KeyExistsAsync(key))
Console.WriteLine($"{key}存在,删除失败");
else
Console.WriteLine($"{key}不存在了,被删除了");
}
}
else {
Console.WriteLine("写入异常");
}
}

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

桌面管理工具:

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

2、批量键值对操作

C#控制台:首先引入Newtonsoft.Json包

修改RedisClient.cs如下,给它扩展两个方法

        /// <summary>
/// 异步批量插入键值对
/// </summary>
/// <param name="keyValuePair"></param>
/// <returns></returns>
public static async Task<bool> StringSetAsync(KeyValuePair<RedisKey, RedisValue>[] keyValuePair)
{
var db = GetDatabase();
return await db.StringSetAsync(keyValuePair);
} /// <summary>
/// 异步批量获取值
/// </summary>
/// <param name="keyValuePair"></param>
/// <returns></returns>
public static async Task<RedisValue[]> StringGetAsync(RedisKey[] keys)
{
var db = GetDatabase();
return await db.StringGetAsync(keys);
}

Program.cs如下:

    class Program
{
static Program()
{
//链式配置Redis
AppConfiguration.Current.ConfigureRedis<RedisConfig>();
} static void Main(string[] args)
{
StringSetGetAsync();
Console.ReadKey();
} static async void StringSetGetAsync()
{
var userInfos = UserInfo.UserInfos;
var keyValues = new KeyValuePair<RedisKey, RedisValue>[userInfos.Count];
var keys =new RedisKey[userInfos.Count];
for (var i = ; i < userInfos.Count; i++)
{
var currUserInfo = userInfos[i];
var key = currUserInfo.Id.ToString();
var value = JsonConvert.SerializeObject(currUserInfo);
keyValues[i] = new KeyValuePair<RedisKey, RedisValue>(key, value);
keys[i] = key;
}
if (await RedisClient.StringSetAsync(keyValues))
{
try
{
var values = await RedisClient.StringGetAsync(keys);
for (var i = ; i < values.Length; i++)
{
Console.WriteLine(values[i]);
}
}
//捕获辅助线程产生的异常
catch (AggregateException ex)
{
ex.Handle(x =>
{
//记录日志
Console.WriteLine("异常处理完毕,批量获取值失败!");
return true;
});
}
}
else
{
//记录日志
Console.WriteLine("写入异常");
}
} class UserInfo
{ public Guid Id { get; set; } public string Name { get; set; } public int Age { get; set; } internal static List<UserInfo> UserInfos = new List<UserInfo>()
{
new UserInfo()
{
Id=Guid.NewGuid(),
Name="小超",
Age=
},
new UserInfo()
{
Id=Guid.NewGuid(),
Name="大超",
Age=
},
}; }
}

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

(2)、管理工具

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

(3)、Linux终端

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

3、过期时间

Redis可以给Key设置过期时间,到达设置的时间,对应的键值对会被删除,内存会被回收,这个功能常用来控制缓存的失效时间.这里这个自动删除的机制很复杂,这里不想说太多,只介绍基本用法,后续的文章会介绍.

C#控制台,修改RedisClient.cs中的StringSetAsync方法如下:

        /// <summary>
/// 异步,写入键值对,可指定过期时间
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expireTime">过期时间</param>
/// <returns></returns>
public static async Task<bool> StringSetAsync(string key,string value, TimeSpan? expireTime=null)
{
var db=GetDatabase();
return await db.StringSetAsync(key,value, expireTime);
}

Program.cs代码如下:

    class Program
{
static Program()
{
//链式配置Redis
AppConfiguration.Current.ConfigureRedis<RedisConfig>();
} static void Main(string[] args)
{
StringSetGetAsync();
Console.ReadKey();
} static async void StringSetGetAsync()
{ if (await RedisClient.StringSetAsync("name","xiaochao",TimeSpan.FromSeconds()))
{
Console.WriteLine("Redis中存在键为name的键值对,值为:{0}",await RedisClient.StringGetAsync("name"));
await Task.Delay();//模拟休息两秒
Console.WriteLine("休息两秒后,Redis的键为name的键值对:{0}", string.IsNullOrEmpty(await RedisClient.StringGetAsync("name")) ? "不存在" : "存在");
}
else
{
//记录日志
Console.WriteLine("写入异常");
}
}
}

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

这边其它两个终端就不演示了,自行观察.

4、计数器

Redis提供了自增命令,前提操作的数据必须是整数,而且自增是有范围的.默认对应long的最大值,一般达不到这个值.

C#控制台:

修改RedisClient.cs下的StringSetAsync方法如下:

        /// <summary>
/// 异步,写入键值对,可指定过期时间
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expireTime">过期时间</param>
/// <returns></returns>
public static async Task<bool> StringSetAsync(string key,RedisValue value, TimeSpan? expireTime=null)
{
var db=GetDatabase();
return await db.StringSetAsync(key, value, expireTime);
}

Program.cs代码如下:

    class Program
{
static Program()
{
//链式配置Redis
AppConfiguration.Current.ConfigureRedis<RedisConfig>();
} static void Main(string[] args)
{
StringSetGetAsync();
Console.ReadKey();
} static async void StringSetGetAsync()
{ if (await RedisClient.StringSetAsync("站点首页",))
{ //模拟用户访问
Parallel.For(, , async (i, ParallelLoopState) =>
{
try
{
await RedisClient.StringIncrementAsync("站点首页");
}
catch (RedisServerException ex)
{
//记录日志
Console.WriteLine(ex.Message);
ParallelLoopState.Stop();
return;
}
});
//输出站点的UV
Console.WriteLine(await RedisClient.StringGetAsync("站点首页"));
}
else
{
//记录日志
Console.WriteLine("写入异常");
}
}
}

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

Redis学习系列二之.Net开发环境搭建及基础数据结构String字符串

注:这里存在两个问题,如果你把Parallel的上限值设置的过大,那么短时间内,可能Redis无法处理这么多的并发量,而报超时错误,这个时候,解决方案是使用集群或者升级虚拟机硬件配置的方式,解决这个问题,但是这里就不演示了.第二个问题是,如果把Set的初始值设为Long.MaxValue,那么Redis会报溢出错误,上面的代码已经处理.