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

一、简介

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桌面管理工具

Linux下Redis-cli

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

三、string(字符串)

1、简单键值对操作

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

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

代码实战:

(1)、Linux终端

(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("写入异常");
            }
        }

桌面管理工具:

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 = 0; 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 = 0; 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=23
                },
                 new UserInfo()
                {
                    Id=Guid.NewGuid(),
                    Name="大超",
                    Age=23
                },
            };

        }
    }

(2)、管理工具

(3)、Linux终端

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(2)))
            {
                Console.WriteLine("Redis中存在键为name的键值对,值为:{0}",await RedisClient.StringGetAsync("name"));
                await Task.Delay(2000);//模拟休息两秒
                Console.WriteLine("休息两秒后,Redis的键为name的键值对:{0}", string.IsNullOrEmpty(await RedisClient.StringGetAsync("name")) ? "不存在" : "存在");
            }
            else
            {
                //记录日志
                Console.WriteLine("写入异常");
            }
        }
    }

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

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("站点首页",0))
            {

                    //模拟用户访问
                    Parallel.For(0, 250000, 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("写入异常");
            }
        }
    }

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

原文地址:https://www.cnblogs.com/GreenLeaves/p/10165352.html

时间: 2024-10-06 22:25:13

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

hibernate学习系列-----(1)开发环境搭建

其实一两个月前就在了解hibernate方面的知识了,但一直以来,都没有好好的总结,而且一直使用的是myeclipse,感觉有些傻瓜式的操作就可以搭建起hibernate的开发环境,但这样一点都不好,没有理解到hibernate到底是怎么配置的,所以你今天特使用Eclipse来一步一步搭建hibernate的开发环境,下面,正式进入正题. 新建一个web项目,名字就随便吧,你喜欢什么名字就什么吧,这是我的截图. 引入hibernate的依赖jar包,我使用的是hibernate-release-

android快速上手(二)android开发环境搭建及hello world

基本了解了java语法,下一步,我们一起开启hello world的神秘之旅. (一)android开发环境搭建 之前搭建android开发环境是件非常费力的事情,下载Eclipse,安装ADT等,如今android官方给我们提供了全套配置. https://developer.android.com/sdk/index.html 搭建android开发环境之前记得先安装jdk (二)开启Hello World之旅 (1)创建Hello World项目 安装完带ADT的Eclipse,打开Ecl

嵌入式linux QT开发(二)——QT开发环境搭建

嵌入式linux QT开发(二)--QT开发环境搭建 一.Windows系统QT开发环境搭建 操作系统:Windows 7 QT Creator:qt-creator-win-opensource-2.4.1 QT SDK:qt-win-opensource-4.7.4-mingw 1.安装QT Creator 点击qt-creator-win-opensource-2.4.1源程序安装. 2.安装QT SDK 点击qt-win-opensource-4.7.4-mingw源程序安装. 选择mi

Android学习路线(一)开发环境搭建

工欲善其事,必先利其器. 回想我刚开始学习Android的时候,环境搭建真的是很头疼的一件事:找了好多博客,看了很多文章,费了九牛二虎之力才搭好环境.当时好想有个直接就能用的开发环境,赶紧开始编写Android App. 不过现在好啦,托Google的福,我们可以直接下载一个环境都配置好了的IDE,直接就能编写App了.当然,你得现有Java(jdk/jre)环境:还没有安装jdk的同学可以参考这篇文章:http://jingyan.baidu.com/article/6dad5075d1dc4

8086汇编语言学习(二) 8086汇编开发环境搭建和Debug模式介绍

1. 8086汇编开发环境搭建 在上篇博客中简单的介绍了8086汇编语言.工欲善其事,必先利其器,在8086汇编语言正式开始学习之前,先介绍一下如何搭建8086汇编的开发环境. 汇编语言设计之初是用于在没有操作系统的裸机上直接操作硬件的,但对于大部分人来说,在8086裸机上直接进行编程将会面临各种困难.好在我们可以使用软件模拟器来模拟硬件进行8086的学习实践.在<汇编语言>中作者推荐通过windows环境下的masm和debug进行学习. masm介绍: masm是一款DOS下的汇编工具包,

Hadoop那些事儿(二)---MapReduce开发环境搭建

上一篇文章介绍了在ubuntu系统中安装Hadoop的伪分布式环境,这篇文章主要为MapReduce开发环境的搭建流程. 1.HDFS伪分布式配置 使用MapReduce时,如果需要与HDFS建立连接,及使用HDFS中的文件,还需要做一些配置. 首先进入Hadoop的安装目录 cd /usr/local/hadoop/hadoop2 在HDFS中创建用户目录 ./bin/hdfs dfs -mkdir -p /user/hadoop 创建input目录,并将./etc/hadoop中的xml文件

老邓的andorid学习笔记-Android 4.0 开发环境搭建

目前android版本早已经四4.xx了,5.0的版本也快出来了.  关于基本环境搭建有好多的文章都介绍过. 我在这里简单的整理了一下,就不用自己专门写此类的文章了. Android SDK 4.0.3 开发环境配置及运行     http://bk-lin.iteye.com/blog/1477808 android 4.0 for windows 7 开发环境搭建   http://cash.iteye.com/blog/1463253 Windows搭建Eclipse+JDK+SDK的An

python Qrcode二维码开发环境搭建

开发环境环境 系      统:windows10 x64 开发工具:python2.7  +qrcode 最近发现一个挺好用的工具,思维导图,可以让我们建立一个清晰思路,我个人比较喜欢用xmind6,所以环境搭建方法是使用思维导图工具编写 . python qrcode 作用: 现在最流行事就是,在外面经常有人让我们扫二维码,而这个插件包的作用就是生成二维码. 以下就是在windows环境下搭建Qrcode环境的具体过程,详见下图.所需要的资料详见附件.

angularjs2 学习笔记(一) 开发环境搭建

开发环境,vs2013 update 5,win7 x64,目前最新angular2版本为beta 17 第一步:安装node.js 安装node.js(https://nodejs.org/en/),为的是能够使用npm获得angular2.0的开发包 验证是否安装成功 cmd下输入 node -v npm -v 第二步:在vs2013上安装typescript 安装完成后在项目中可以添加typescript项目了,并且在项目属性栏中会有typescript页 第三步:创建项目 可以将没用的都