zoukankan      html  css  js  c++  java
  • 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));
                    }
                }

    Z

  • 相关阅读:
    可以
    全链路压测方案
    PyTestReport使用
    查看mysql的版本号
    Centos7 下的SVN安装与配置
    confluence中org.apache.tomcat.util.net.NioEndpoint$Acceptor.run Socket accept failed的解决方法
    CentOS7安装部署zabbix3.4操作记录
    Aasible中cryptography兼容性报错解决办法
    windows10中git-bash闪退的解决办法
    jira发送邮件报错
  • 原文地址:https://www.cnblogs.com/anjingdian/p/15378697.html
Copyright © 2011-2022 走看看