Redis

https://github.com/MSOpenTech/redis/releases

最新的redis下载,只有64位的,微软开发的

 

Redis 是什么?全称:REmote DIctionary Server 远程字典服务器

基于内存管理(数据存在内存),实现了5种数据结构(分别应对各种具体需求),单线程模型的应用程序(单进程单线程),对外提供插入-查询-固化-集群功能

redis-server.exe 服务端

redis-cli.exe 客户端

redis-desktop-manager 可视化的管理工具

常见命令:

https://redis.io/commands#

set zwk 123

get zwk 123

select 1 切换库

...

 

配置文件 .conf

redis服务启动时默认会加载安装根目录下的redis.conf文件来完成redis服务实例的相关配置,通常一个reids服务实例对应一个配置文件,若要在同一台服务器上启动多个redis服务实例,就必须在安装根目录下创建多个配置文件。由于每个redis服务实例都必须对应唯一的端口号,通常将对应的配置文件命名为redis_port.conf。 

save 60 10000  //60s 内10000次更改

  •  
  • timeout:请求超时时间
  • loglevel:log信息级别
  • logfile:log文件位置
  • databases:开启数据库的数量
  • save * *:保存快照的频率,第一个*表示多长时间,第三个*表示执行多少次写操作。在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。
  • rdbcompression:是否使用压缩
  • dbfilename:数据快照文件名(只是文件名,不包括目录)
  • dir:数据快照的保存目录(这个是目录)
  • appendonly:是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率。
  • appendfsync:appendonlylog如何同步到磁盘(三个选项,分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步)
  • port:配置服务的端口号,默认6379,同一服务器上启动多个服务必须在对应配置文件内指定不同端口号。 
  • timeout:配置客户端连接超时时间,单位秒,若客户端在规定时间内没发请求会关闭该连接,默认0无限制。 
  • bind:配置服务只接受来自指定ip的请求,默认127.0.0.1只接收本机请求。
  • daemonize:配置服务是否为后台运行,默认no非后台运行,如需后台运行改为yes。
  •  pidfile:配置pid文件地址,默认/var/run/redis_6379.pid,其存储了当前服务的进程号,同一服务器上多个服务必须创建对应的多个pid文件,文件名以端口号区分。 

  loglevel:配置数据库日志级别,共四个级别,默认notice正常记录,debug记录全部信息,varbose仅记录有用信息,waring记录非常重要的信息。 
  logfile:配置数据库日志文件地址,stdout标准输出到终端显示,后台模式输出到/dev/null,null是一串口设备的文件,/export/Logs/redis/redis_6379.log输出到文件,前提必须先建立此目录及文件。 
  databases:配置可用数据库个数,默认16。redis没有表概念,其数据库用一个从0开始的整数索引来标识,使用select dbid来指定数据库,默认0数据库,不同数据库内key可重复。flushdb清除当前数据库内容,flushall清除当前服务所有数据库内容,dbsize返回当前数据库内k个数。 
  save:配置内存数据与硬盘数据库同步频率,如save 900 1,表示900秒内有一个及以上的key发生变化,就将内存数据同步到硬盘。redis有rdb和aof两种实现内存数据持久化的方式,其中rdb速度快,但会缺失数据;aof速度慢,但会完全恢复。 
  rdbcompression:配置内存数据同步到rdb时是否压缩数据,默认yes。 
  dbfilename:配置rdb数据库文件名,默认为dump.rdb,仅指出文件名。 
  dir:配置rdb数据库文件存放目录,默认redis安装根目录。只能指定目录,不能指定rdb文件名。 
  slaveof:配置当前服务的主redis服务,redis的多个服务可实现主从同步,若当前服务为从库,要在此指定主库的ip和端口,如slaveof ip port,当从库开启时,会自动从主库同步数据。 
  masterauth:配置从库访问主库的密码,若当前服务为从库,且要同步的主库设置了密码,要在此指定连接到主库的密码。 
  appendonly:配置是否开启aof持久化功能,默认no,若开启会创建xxx.aof文件,其会记录服务的每一次写操作。 
  appendfilename:配置aof文件的名称,默认为appendonly.aof。 
  appendfsync:配置服务对aof文件的同步频率,always表示一有写操作就同步,everysec表示每秒进行一次同步。 
  requirepass:配置客户端连接到服务的密码,默认注释掉不设置密码,此时redis进入保护模式,除了本地客户端外,远程客户端均无法连接服务。在此设置密码后可实现远程连接,但远程登录后必须执行auth password命令输入密码才可操作。 
  maxclients:配置服务的最大客户端连接数,默认注释掉,不加限制。 
  maxmemory:配置服务的最大内存,默认注释掉,不加限制。

 

安装

 

 

数据存储

redis的存储分为内存存储、磁盘存储和log文件三部分,配置文件中有三个参数对其进行配置。

save seconds updatessave配置,指出在多长时间内,有多少次更新操作,就将数据同步到数据文件。这个可以多个条件配合,比如默认配置文件中的设置,就设置了三个条件。

appendonly yes/no ,appendonly配置,指出是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面的save条件来同步的,所以有的数据会在一段时间内只存在于内存中。

appendfsync no/always/everysec ,appendfsync配置,no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次。

 

两种保存方式dump.rdb (默认snapshot)和  appendonly.aof(每次操作都保存 效率低)

Redis有两种存储方式,默认是snapshot方式,实现方法是定时将内存的快照(snapshot)持久化到硬盘,这种方法缺点是持久化之后如果出现crash则会丢失一段数据。因此在完美主义者的推动下作者增加了aof方式。aof即append only mode,在写入内存数据的同时将操作命令保存到日志文件,在一个并发更改上万的系统中,命令日志是一个非常庞大的数据,管理维护成本非常高,恢复重建时间会非常长,这样导致失去aof高可用性本意。另外更重要的是Redis是一个内存数据结构模型,所有的优势都是建立在对内存复杂数据结构高效的原子操作上,这样就看出aof是一个非常不协调的部分。

 

...

 

单线程多进程

代码如何访问?

主流组件:

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

 

redis的五大数据结构

String:key-value,支持过期 大小不超过512M

Hash:一个key对应多个 key-value 类似于一个表

Set:无序集合,不能重复 一个key对应多个value

ZSet:有序集合,不能重复,一个key对应多个value ,score,根据Score排序

List:

 

组合命令 是单条命令

SetAll&AppendToValue&GetValues&GetAndSetValue&IncrementValue&IncrementValueBy,这些看上去是组合命令,但实际上是一个具体的命令,是一个原子性的命令,不可能出现中间状态,可以应对一些并发情况

Decr 不是组合命令 是单条命令,所以redis可以实现超卖

  /// <summary>
    /// 超卖:订单数超过商品
    /// 
    ///数据库:秒杀的时候,10件商品,100个人想买,假定大家一瞬间都来了,
    ///A 查询还有没有--有---1更新
    ///B 查询还有没有--有---1更新
    ///C 查询还有没有--有---1更新
    ///可能会卖出12  12甚至20件商品
    ///
    /// 微服务也有超卖的问题,异步队列
    /// 
    /// 
    /// Redis原子性操作--保证一个数值只出现一次--防止一个商品卖给多个人
    /// 
    /// 
    /// Redis是单线程,程序怎么又多线程操作Redis呢? 这个是可以的,
    /// 打开多个链接,去提交任务,对程序而言,Redis是并发
    /// 
    /// 假如redis命令是拼装的,Decr---1 获取值  2 程序减1  3 再save结果回去
    ///     程序多线程并发一下,  A线程  1   2   3     初始值是10
    ///                           B线程    1 2 3       结果是9,减了2次但是结果是9
    ///                           
    ///     组合命令,Decr  Redis线程直接完成当前值-1并返回结果,原子性操作                     
    ///       程序多线程并发一下,A线程     123        初始值10
    ///                           B线程  123       
    ///                           C线程        123      得到3个结果,9/8/7
    ///  
    /// 假如库存只有1个(数据库),三个人同时来下单,一检测>0,都会成功--超卖
    ///                          三个人同时来,lock/开启事务
    ///                          只有一个人能去检测>0    -1  save 
    ///                          然后第二个人来,==0 返回失败 
    ///                          然后第三个人来,==0 返回失败
    ///     因为这个等于是数据库单线程了,其他都要阻塞,各种超时
    ///     -1的时候除了操作库存,还得增加订单,等支付。。
    ///     10个商品秒杀,一次只能进一个? 违背了业务
    ///     
    /// 所以用上了Redis,一方面保证绝对不会超卖,
    ///                 另一方面没有效率影响,数据库是可以为成功的人并发的
    ///                 还有撤单的时候增加库存,可以继续秒杀,
    ///                 限制秒杀的库存是放在redis,不是数据库,不会造成数据的不一致性
    ///            
    /// Redis能够拦截无效的请求,如果没有这一层,所有的请求压力都到数据库
    /// 
    /// </summary>
    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++)
            {
                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();
        }
    }

 

redis文献资料

https://redis.io/documentation

 

redis命令

https://redis.io/commands#string

 

redis优化

https://www.infoq.cn/article/tq-redis-memory-usage-optimization-storage/

 

posted @ 2020-08-19 23:32  爱如少年。  阅读(192)  评论(0编辑  收藏  举报