c# redis系列一

NoSql

Not Only Sql非关系型数据库

由于关系型数据库中数据的关系复杂 ,再就是数据读取和写入压力,硬盘的速度满足不了,尤其是一些大数据量 所以产生了NoSql了,比如Redis。

Redis

Redis特点

(1)是基于内存的,关系型数据库则是存到硬盘中的。

(2)没有严格的数据格式

(3)支持多种类型(5种)
(4)因为Redis是基于内存的,所以万一服务器挂了,那么数据就会丢失。但是Redis有一个固化数据的功能,可以实现持久化保存,可以将一些不经常访问的信息存储到硬盘中,这个可以通过配置实现。但是如果是这个应用程序崩溃了,是不会影响到redis的,因为redis相当于一个内存的

(5)支持集群

Redis单线程模型

单线程模型:整个进程只有一个线程,线程就是执行流,这样性能会低吗?不会。最初的版本是使用的单线程模型,4.0之后加入了多线程的支持。相比于多线程,性能还是会低一点

单线程的优点:

  • 第一,单线程可以简化数据结构和算法的实现。并发数据结构实现不但困难而且开发测试比较麻
  • 第二,单线程避免了线程切换和竞态产生的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。
  • 单线程的问题:对于每个命令的执行时间是有要求的。如果 某个命令执行过长,会造成其他命令的阻塞,所以 redis 适用于那些需要快速执行的场景。

单线程模型的相关介绍

 

使用 

redis的下载地址:https://github.com/MSOpenTech/redis/releases

 下载之后解压,双击redis-server.exe开启redis服务:

 

 redis-server.exe打开之后不要关闭,然后双击redis-cli.exe打开操作界面(6379是redis的默认端口,可以再配置界面更改):

 还有一个可视化工具:

 

 安装之后登录打开就可以看到设置的信息:

 实际开发中肯定不是像上面那样弄,我们可以直接使用对应的程序集:

 ServiceStack(1小时3600次请求--可破解) 需要收费! StackExchange 免费

 

 Redis支持数据类型

String: 主要是key-value形式的缓存,可以设置过期世间, value不byte(比特)超过512;Redis是单线程的,

Hash

Set

ZSet

List

 ServiceStack中有内置的递减的api,可以实现电商中的秒杀环节,如果是用程序的话,就要是多线程加锁来实现,但是用redis的话更快更简便。

5种类型详解

示例(这是使用的ServiceStack):下面主要用的是5大类型中的string类型

 自定义类:

namespace Richard.Redis.Interface
{
    /// <summary>
    /// RedisBase类,是redis操作的基类,继承自IDisposable接口,主要用于释放内存
    /// </summary>
    public abstract class RedisBase : IDisposable
    {
        /// <summary>
        /// Redis连接;
        /// </summary>
        public IRedisClient iClient { get; private set; }
        /// <summary>
        /// 构造时完成链接的打开
        /// </summary>
        public RedisBase()
        {
            iClient = RedisManager.GetClient();
        }

        //public static IRedisClient iClient { get; private set; }
        //static RedisBase()
        //{
        //    iClient = RedisManager.GetClient();
        //}


        private bool _disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    iClient.Dispose();
                    iClient = null;
                }
            }
            this._disposed = true;
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        public void Transcation()
        {
            using (IRedisTransaction irt = this.iClient.CreateTransaction())
            {
                try
                {
                    irt.QueueCommand(r => r.Set("key", 20));
                    irt.QueueCommand(r => r.Increment("key", 1));
                    irt.Commit(); // 提交事务
                }
                catch (Exception ex)
                {
                    irt.Rollback();
                    throw ex;
                }
            }
        }


        /// <summary>
        /// 清除全部数据 请小心
        /// </summary>
        public virtual void FlushAll()
        {
            iClient.FlushAll();
        }

        /// <summary>
        /// 保存数据DB文件到硬盘
        /// </summary>
        public void Save()
        {
            iClient.Save();//阻塞式save
        }

        /// <summary>
        /// 异步保存数据DB文件到硬盘
        /// </summary>
        public void SaveAsync()
        {
            iClient.SaveAsync();//异步save
        }
    }
}


namespace Richard.Redis.Service
{
    /// <summary>
    /// key-value 键值对:value可以是序列化的数据
    /// </summary>
    public class RedisStringService : RedisBase
    {
        #region 赋值
        /// <summary>
        /// 设置key的value
        /// </summary>
        public bool Set<T>(string key, T value)
        {
            return base.iClient.Set<T>(key, value);
        }
        /// <summary>
        /// 设置key的value并设置过期时间
        /// </summary>
        public bool Set<T>(string key, T value, DateTime dt)
        {
            return base.iClient.Set<T>(key, value, dt);
        }
        /// <summary>
        /// 设置key的value并设置过期时间
        /// </summary>
        public bool Set<T>(string key, T value, TimeSpan sp)
        {
            return base.iClient.Set<T>(key, value, sp);
        }
        /// <summary>
        /// 设置多个key/value
        /// </summary>
        public void Set(Dictionary<string, string> dic)
        {
            base.iClient.SetAll(dic);
        }

        #endregion

        #region 追加
        /// <summary>
        /// 在原有key的value值之后追加value,没有就新增一项
        /// </summary>
        public long Append(string key, string value)
        {
            return base.iClient.AppendToValue(key, value);
        }
        #endregion

        #region 获取值
        /// <summary>
        /// 获取key的value值
        /// </summary>
        public string Get(string key)
        {
            return base.iClient.GetValue(key);
        }
        /// <summary>
        /// 获取多个key的value值
        /// </summary>
        public List<string> Get(List<string> keys)
        {
            return base.iClient.GetValues(keys);
        }
        /// <summary>
        /// 获取多个key的value值
        /// </summary>
        public List<T> Get<T>(List<string> keys)
        {
            return base.iClient.GetValues<T>(keys);
        }
        #endregion

        #region 获取旧值赋上新值
        /// <summary>
        /// 获取旧值赋上新值
        /// </summary>
        public string GetAndSetValue(string key, string value)
        {
            return base.iClient.GetAndSetValue(key, value);
        }
        #endregion

        #region 辅助方法
        /// <summary>
        /// 获取值的长度
        /// </summary>
        public long GetLength(string key)
        {
            return base.iClient.GetStringCount(key);
        }
        /// <summary>
        /// 自增1,返回自增后的值
        /// 就是将key对应的值加1之后返回
        /// </summary>
        public long Incr(string key)
        {
            return base.iClient.IncrementValue(key);
        }
        /// <summary>
        /// 自增count,返回自增后的值
        /// 将原先的值加上count之后返回最终值
        /// </summary>
        public long IncrBy(string key, int count)
        {
            return base.iClient.IncrementValueBy(key, count);
        }
        /// <summary>
        /// 自减1,返回自减后的值
        ///  将原先的值减一之后返回最终值
        /// </summary>
        public long Decr(string key)
        {
            return base.iClient.DecrementValue(key);
        }
        /// <summary>
        /// 自减count ,返回自减后的值
        ///  将原先的值减去count之后返回最终值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public long DecrBy(string key, int count)
        {
            return base.iClient.DecrementValueBy(key, count);
        }
        #endregion
    }
}

 自定义的redis管理中心:

 

namespace Richard.Redis.Init
{
    /// <summary>
    /// Redis管理中心
    /// </summary>
    public class RedisManager
    {
        /// <summary>
        /// redis配置文件信息
        /// </summary>
        private static RedisConfigInfo RedisConfigInfo = new RedisConfigInfo();

        /// <summary>
        /// Redis客户端池化管理
        /// </summary>
        private static PooledRedisClientManager prcManager;

        /// <summary>
        /// 静态构造方法,初始化链接池管理对象
        /// </summary>
        static RedisManager()
        {
            CreateManager();
        }

        /// <summary>
        /// 创建链接池管理对象
        /// </summary>
        private static void CreateManager()
        {
            // 可写的Redis链接地址 就是redis的服务器IP地址
            string[] WriteServerConStr = RedisConfigInfo.WriteServerList.Split(',');
            // 可读的Redis链接地址 就是redis的服务器IP地址
            string[] ReadServerConStr = RedisConfigInfo.ReadServerList.Split(',');
            prcManager = new PooledRedisClientManager(ReadServerConStr, WriteServerConStr,
                             new RedisClientManagerConfig
                             {
                                 //最大写链接数
                                 MaxWritePoolSize = RedisConfigInfo.MaxWritePoolSize,
                                 //最大读链接数
                                 MaxReadPoolSize = RedisConfigInfo.MaxReadPoolSize,
                                 //是否自动启动
                                 AutoStart = RedisConfigInfo.AutoStart,
                             });
        }

        /// <summary>
        /// 客户端缓存操作对象
        /// </summary>
        public static IRedisClient GetClient()
        {
            return prcManager.GetClient();
        }
    }
}

redis的配置类:

 

namespace Richard.Redis.Init
{
    /// <summary>
    /// redis配置文件信息
    /// 也可以放到配置文件去
    /// </summary>
    public sealed class RedisConfigInfo
    {
        /// <summary>
        /// 可写的Redis链接地址
        /// format:ip1,ip2
        /// 
        /// 默认6379端口
        /// </summary>
        public string WriteServerList = "127.0.0.1:6379";
        /// <summary>
        /// 可读的Redis链接地址
        /// format:ip1,ip2
        /// </summary>
        public string ReadServerList = "127.0.0.1:6379";
        /// <summary>
        /// 最大写链接数
        /// </summary>
        public int MaxWritePoolSize = 60;
        /// <summary>
        /// 最大读链接数
        /// </summary>
        public int MaxReadPoolSize = 60;
        /// <summary>
        /// 本地缓存到期时间,单位:秒
        /// </summary>
        public int LocalCacheTime = 180;
        /// <summary>
        /// 自动重启
        /// </summary>
        public bool AutoStart = true;
        /// <summary>
        /// 是否记录日志,该设置仅用于排查redis运行时出现的问题,
        /// 如redis工作正常,请关闭该项
        /// </summary>
        public bool RecordeLog = false;
    }
}

使用DecrementValue递减方法实现商品秒杀:

public class OversellTest
    {
        private static bool IsGoOn = true;//是否继续秒杀,由库存和秒杀时间共同决定,任意一个结束都结束
        public static void Show()
        {
            using (RedisStringService service = new RedisStringService())
            {
                service.Set<int>("Stock", 10);//默认库存库存
            }

            for (int i = 0; i < 5000; i++) //5000人同时并发;
            {
                int k = i;
                Task.Run(() =>//每个线程就是一个用户请求
                {
                    using (RedisStringService service = new RedisStringService())
                    {
                        if (IsGoOn)
                        {
                            long index = service.Decr("Stock");//-1并且返回  
                            if (index >= 0)
                            {
                                Console.WriteLine($"{k.ToString("000")}秒杀成功,秒杀商品索引为{index}");
                                //可以分队列,去数据库操作
                            }
                            else
                            {
                                if (IsGoOn)
                                {
                                    IsGoOn = false;
                                }
                                Console.WriteLine($"{k.ToString("000")}秒杀失败,秒杀商品索引为{index}");
                            }
                        }
                        else
                        {
                            Console.WriteLine($"{k.ToString("000")}秒杀停止......");
                        }
                    }
                });
            }
            Console.Read();
        }
    }

结果:

 

5000人抢10件商品,这样比写多线程外加锁性能快多了。

 此外还有一些简单的调用,可以看一下:

 

 

Console.WriteLine("*****************************************");
            {
                using (RedisStringService service = new RedisStringService())
                {

                    service.FlushAll();

                    service.Set<string>("student1", "梦的翅膀");
                    Console.WriteLine(service.Get("student1"));

                    service.Append("student1", "20180802");
                    Console.WriteLine(service.Get("student1"));

                    Console.WriteLine(service.GetAndSetValue("student1", "程序错误"));
                    Console.WriteLine(service.Get("student1"));
                    //5秒钟过期,5秒钟之后保存在内存中的这个数据就会消失
                    service.Set<string>("student2", "", DateTime.Now.AddSeconds(5));
                    Thread.Sleep(5100);
                    Console.WriteLine(service.Get("student2"));

                    service.Set<int>("Age", 32);
                    Console.WriteLine(service.Incr("Age"));
                    Console.WriteLine(service.IncrBy("Age", 3));
                    Console.WriteLine(service.Decr("Age"));
                    Console.WriteLine(service.DecrBy("Age", 3));
                }
            }

 

转载:

https://www.cnblogs.com/anjingdian/p/15378697.html

 

posted @ 2023-03-24 13:38  冲向蓝天  阅读(65)  评论(0编辑  收藏  举报