Redis

《Redis》

一、概述

  • NoSql,非关系型数据库
  • 这样的话客户进来的话先经过linux,然后是Niginx(不可能直接进入Tomcat的),再然后是应用服务器的集群(也可以理解为Tomcat),接下来是mysql集群,最后是一些流媒体、电子邮件、文件图片服务器等等

  • Nosql无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。

  • 大数据时代的3V和3高:

    • 3V:
      1. 海量Volume
      2. 多样Variery
      3. 实时Velocity
    • 3高:
      1. 高并发
      2. 高可扩
      3. 高性能
  • 和传统的关系型数据库不同,非关系型数据库是三大特征:

    • C:consistency(强一致性)
    • A:Availability(可用性)
    • P:Partition tolerance(分区容错性)
    • 一个分布式系统不可能同时满足这三个特征,最多只能同时满足两个。因此,根据CAP原理将NoSql数据库分成满足CA原则,满足CP原则,满足AP原则这三大类
      • CA:单点集群,满足一致性,可用性的系统,但是扩展性不大
      • CP:满足一致性,分区容错性的系统,性能不太好(Redis选择的是CP)
      • AP:满足可用性,分区容错性的系统,通常对一致性要求低一些
      • 在分布式系统中,P(分区容错性是一定要实现的)

二、入门

  • Redis:Remote Dictionary Server(远程字典服务器)是完全开源,使用C语言编写,遵守BSD协议,是一个高性能的(key/value)分布式内存数据库,基于内存运行,并支持持久化的NoSQL数据库‘

  • 推荐在Linux下使用Redis。一般使用的时候都要先将Redis的配置文件复制一份,这样的话一旦出现问题也不会影响到原厂的配置文件,可以重新配置。查看进程:ps -ef|grep redis,启动Redis:redis-server /myredis/redis.conf,redis-cli -p 6379

  • 杂项:

    • Redis是单进程的
    • 默认16个数据库,类似数组从0开始,使用Select命令切换数据库
    • Dbsize查看当前数据库的key数量
    • Flushdb:清空当前数据库
    • Flushall:清空全部库
    • 统一的密码管理,16个库使用同一个密码,要么都🆗,要么都不🆗
    • Redis索引从0开始
    • 默认端口号是6379
  • Redis常用的1+5大数据类型

    1. key:这个是Redis关于key的操作:

      • Redis的key设定:表名:id:id值:字段名的组合来设置key,例如:t_user:uid:12:username

        set t_user:id:00789:fans 1000		//设置key为t_user:id:00789:fans,value为1000
        set t_user:id:00789:blogs 10		//设置key为t_user:id:00789:blogs,value为10
        //这种设置的方式,可以直接的操作key 例如fans数量加1
        incr t_user:id:00789:fans
        
      • 还可以以json格式设置key和value:

        set t_user:id:00789 {id:00789,fans:1000,blogs:10}		//这种设置key的方式,无法直接操作key只能在程序中先获取到key中fans的数量,再加1。这是和上面设置key的不同之处
        
      • key *:查看所有key(查看出来的结果是无序的)

      • exists key的名字:判断是否存在某个key

      • dbsize:看当前中有多少个key

      • move key db:将某个key从当前库转移到另一个库,当前库就没有这个key了

      • expire key 秒钟:为给定的key设置过期时间

      • persist key:切换key从时效性转换为永久性

      • ttl key:查看还有多少秒钟过期,-1表示永远不过期,-2表示已经过期(key过期就会被删除)

      • rename key newkey:为key改名

      • sort :对所有的key排序

      • del key:删除某个key

      • type key:查看某个key是什么类型

    2. String:单值单value(单key单value)

      • set/get/del/append(在原先的Value值后面追加,返回新字符串的长度)/strlen
      • Incre/decr/Increby/decreby,一定是数字才能进行加减操作,否则报错
      • getrange/setrange,返回内的取值和赋值
      • setex(set with expire)键 秒值 / setnx(set if not exist)如果不存在的话创建
      • mset/mget/msetnx,多赋值,多取值,最后一个有点特殊,msetnx k1 v1 k2 v2 但是k1已经存在,k2不存在使用msetnx也是不可以的,msetnx后面的k必须是之前k不存在的情况下才可以被设置。
      • getset(先get再set)
    3. Hash(哈希,类似java中的Map):

      • KV模式不变,key是String,但是value是一个键值对

      • hset/hget/hmset/hmget/hgetall/hdel

        hset user id 11		设置key为user,value为id 11
        hget user id      获取value为id的值
        结果是11
        
        hmset customer id 11 name z3 age 19
        hmget customer id name age
        结果为 11 z3 19
        
        hgetall customer
        结果为:
        id
        11
        name
        z3
        age
        19
        
      • hlen:hlen key

      • hexist key field:判断key中是否有某个值的key

      • hkeys/hvals:查看指定key里面的所有field和value

      • hincrby / hincrbyfloat:hincrby customer age 2,在原来age上面加2

      • hsetnx

    4. List(列表):Redis列表是一个简单的字符串列表,按照插入顺序排序,可以在添加一个元素在列表的左部(头部)或尾部(右部)。它底层实际是一个双向链表,其中保存的数据都是String类型的。

      • 单值多value(单key多value,并且value可以重复,有序)

      • lpush/rpush/lrange,LPUSH list01 1 2 3 4 5

      • LPOP/RPOP

      • lindex:按照索引下标获得元素(从上到下)

      • llen:获取长度

      • lrem key 步数 元素:删除N个value。举例:RPUSH list03 1 1 1 2 2 2 3 3 3 4 4 4 5 6 7,LREM list03 2 3,查看list03,结果是:1 1 1 2 2 2 3 4 4 4 5 6 7 。就是从左删除2个3

      • ltrim key start end:截取指定范围的值,然后再赋值给key。举例:

        LPUSH list01 1 2 3 4 5 6 7 8 9 (先给list01赋值)
        LRANGE list01 0 -1 (查看list01)
        9
        8
        7
        6
        5
        4
        3
        2
        1
        ltrim list01 3 5(执行ltrim)
        LRANGE list01 0 -1(再查看list01)
        6
        5
        4      结果说明:从上(3)到下(5)截取list01的值,再将截取后的值赋值给list01。结果显示为6 5 4
        
      • RPOPLPUSH 列源表 目的表。举例:

        lpush list01 4 5 6 7 8 
        rpush listo2 2 3 4 
        rpoplpush list01 list02   结果显示为:4 2 3 4 。原理就是从list01的右边弹出一个值(这个值在list01中就是4)然后再将这个值从左边加到list02中。所以结构就是 4 2 3 4
        
      • lset key index value:给key中下标为index的值,重新赋值

      • linsert key before/after 值1 值2 :在key的值1的前面或者后面插入值2

      • blpop key1[key2...] timeout / brpop key1[key2...] timeout:规定时间内获取并移除数据,如果没有数据的话就一直等,等待过程中,如果有值了就获取出来,并且显示从哪个队列获取的。如果一直到timeout也没有值,就返回空null(两个客户端能玩)

      • 性能总结:

        1. 它是一个字符串链表,left和right都可以插入添加
        2. 如果键不存在,创建新的链表
        3. 如果键已存在,新增内容
        4. 如果值被全部移除,对应的键也就消失了
        5. 链表的操作无论是头还是尾效率都非常高,但是如果是中间操作,效率就很低了
    5. Set(集合):Redis的set是String类型的无序、无重复的集合,它是通过HashTabel来实现的,它的查询效率非常快。其实set和hash的结构完全相同,只是Set仅仅存储field,不存储value

      • 单值多filed(单key多value,并且filed不可以重复,而且value无序)

      • sadd/smenbers/sismenber:

        sadd set01 1 1 2 2 3 3		向key为set01中加入1 1 2 2 3 3 
        smembers set01		查看set01中的值
        1
        2
        3
        结果为 1 2 3 
        sismember set01 值 : 结果返回的是一个boolean值
        
      • scard:获取集合里面的元素个数

      • srem key field:删除集合中的元素

      • srandmember key 整数:在key中随机获取整数个field,但是原来数据集不变

      • spop key num(出栈数量):从key中随机出栈某个数量的field,返回的就是被出栈的这个值,原数据集相应改变

      • smove key1 key2 在key1中的某个field:作用是将key1里面的某个field赋值给key2

      • 数学集合类:

        1. 差集(sdiff):sdiff set01 set02 :在第一个set里面,不在后面任何一个set里面的值
        2. 交集(sinter):
        3. 并集(sunion):
        4. sinterstore :求两个集合的交,并,差集并且再将结构存储到指定的集合中去
        5. sdiffstore
        6. sunionstore
    6. Zset(Sorted set):有序集合。它和set一样也是String类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个Double类型的分数。Redis正是通过分数来为集合中的成员进行从小到大的排序。Zset的成员是唯一的,但是分数(score)却可以重复。

      • Zset在set的基础之上,加一个score值。之前set是 set01 v1 v2,现在zset是 set01 score1 v1 score2 v2

      • zadd / zrange[withscore]

        zadd zset01 60 v1 70 v2 80 v3 90 v4 100 v5		key就是zset01,value就是(60 v1) (70 v2)
        
      • zrangebyscore key 开始score 结束score

        1. withscore

        2. “ ( ” 不包含

          zrangebyscore zset01 60 90   	结果为:v1 v2 v3 v4
          zrangebyscore zset01 60 (90  	结果为:v1 v2 v3
          zrangebyscore zset01 (60 (90  结果为:v2 v3
          
        3. limit作用是返回限制

          zrangebyscore zset01 60 90    					结果为:v1 v2 v3 v4
          zrangebyscore zset01 60 90 limit 2 2   	结果为:v3 v4  从第二个开始,截取两个
          
        4. zrem key 某score下对应的value值,作用是删除元素

          zrem zset01 v5			删除v5
          
        5. zcard / zcount key score区间 / zrank key values值:作用是获得下标值。 / zscore key field:获得分数

          zcount zset01 60 80       结果为3
          zrank zset01 v4  					结果为3,意思是下标为3
          zscore zset01 v4          结果为90
          
        6. zincrby key increment member

      • 条件删除数据

        • zremrangebyrank key start stop
        • zremrangebyscore key min max
  • 高级数据类型:所谓高级数据类型就是为了解决特定的数据场景

    • Bitmaps(多用于信息状态的统计,例如员工打卡,签到)

      • getbit key offset:获取指定key对应偏移量上的bit值
      • setbit key offset value:设置指定key对应偏移量上的bit值,value只能是1或0
      • bitop and / or / not / xor(亦或) destkey key1 [key2...]:对指定的key按位进行交,并,非,亦或操作,并将结果保存到destkey(新的key)中
      • bitcount key [start end]:统计指定key中1的数量
    • Hyperloglog:只用于统计不重复的数据的数量。但是它只进行基数统计(去重以后的数量),不是集合,所有不保存数据,只记录数量而不是具体数据。还有一点它会有0.81%的误差。

      • pfadd key element[element]:添加数据
      • pfcount key [key...]:统计数据
      • pfmerge destkey sourcekey[sourcekey...]:合并数据
    • GEO:只用于地理位置的计算

      • geoadd key longitude latitude member [ongitude latitude member...]:添加坐标点

      • geopos key member [member] :获取坐标点

      • geodist key member1 member2 [unit]:计算坐标点距离

      • georadius key longtitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]:根据坐标获取范围内的数据(点)

      • georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count]:根据点获取范围内的数据

三、解析配置文件redis.conf

  1. redis.conf在linux系统环境下的位置
  2. Units单位:1k=1000bit 1kb=1024bit 如果单位中没有b的话就是1000整数倍,如果有b的话就是1024倍
  3. INCLUDES:
  4. GENERAL通用:诸如日志级别,日志文件的前缀,默认数据库的大小等等
  5. SNAPSHOTTING快照
  6. REPLICATION复制
  7. SECURITY安全:访问密码的查看,设置和取消
  8. LIMITS限制:这里可以设置最大缓存的过期策略,共五种。默认的是noeviction永不过期,但是实际生产中,是不能使用这个配置的,一定会做修改的。LRU:就是最近使用频率少就移除
    • volatile-lru:使用LRU算法移除key,只对设置了过期时间的键
    • allkeys-lru:使用LRU算法移除key
    • volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
    • allkeys-random:移除随机的key
    • volatile-ttl:移除那些TTL最小的key,即那些最近要过期的key
    • noeviction:不进行移除,针对写操作,只是返回错误信息
  9. APPEND ONLY MODE追加:后面会着重介绍
  10. 常见配置redis.conf介绍:
    1. Redis默认不是一守护进程的方式运行,可以通过daemonize no / yes来配置该项,使用yes启动守护进程
    2. 当redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定(主从复制的时候这个地方最好改一下,可以根据主,从的端口号来命名,例如redis6379.pid,redis6380.pid)
    3. 默认端口6379(主从复制的时候也要改)
    4. 绑定的主机地址:bind 127.0.0.1
    5. 当客户端闲置多长时间后关闭连接,如果指定是0,表示关闭该功能 timeout
    6. 指定Redis的日志级别,Redis的日志总共支持四个级别:debug,verbose,notice,warning,默认是verbose
    7. 日志的记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null/logfile (主从复制的时候最好也改一下 做好区分)
    8. 设置数据库的数量,默认数据库是0,可以使用select命令在连接上指定数据库的id
    9. 指定在多长的时间内,又多少次更新操作,就将数据同步到数据文件,可以多个条件配合
      • save
      • Redis默认配置文件中提供了三个条件
        1. save 900 1
        2. save 300 10
        3. save 60 10000
        4. 分别表示900秒内有一个更新,300秒内有10个更新,60秒内有10000个更新
    10. 指定存储至本地数据库时是否压缩数据,默认是yes,Redis采用LZF压缩,如果为了节省CPU的时间,可以关闭该选项,但会导致数据库文件变得巨大 rdbcompression yes
    11. 指定本地数据库RDB文件名,默认为dump.rdb dbfilename dump.rdb
    12. 指定本地数据库存放目录 dir ./
    13. 设置当本机为slav服务时,设置master服务的IP地址及端口。在Redis启动时,它会自动从master进行数据同步。slavof
    14. 当master服务设置了密码保护时,slav服务连接master的密码 masterauth
    15. requirepass foobared设置Redis连接密码,如果配置了密码,客户端在连接Redis时需要通过auth命令提供密码,默认这个功能时关闭的
    16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置maxclients 0,表示不做限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息 maxclients 128
    17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内后,Redis会先尝试清楚已经到期或者即将到期的key,当此方法处理后,仍然倒大最大内存设置,将无法再进行写操作,但是仍然可以进行度操作。Redis新的vm机制,会把key存放在内存中,value村房子swap区 maxmemory
    18. 指定是否在每次更新操作后进行日志记录。Redis在默认的情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因此redis本身同步数据文件是按上面save的条件来同步的,所以有的数据会在一段时间内只存在于内存中,默认为no。 appendonly
    19. 指定更新日志文件名,默认为appendonly.aof appendfilename appendonly.aof
    20. 指定更新日志条件,共有3个可选值:appendfsync everysec
      1. no:表示等操作系统进行数据缓存同步到磁盘(快)
      2. always:表示每次更新操作后手动调用fsync()方法将数据写入磁盘(慢,安全)
      3. everysec:表示每秒同步一次(折中,默认)
    21. 指定是否启用虚拟内存机制,默认no。介绍一下,vm机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中 vm-enabled no
    22. 虚拟内存文件路径,默认为/tmp/redis.swap,不可多个Redis实例共享
    23. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认为0 vm-max-memory
    24. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大的对象,则可以使用更大的page,如果不确定,就是要默认值 vm-page-size 32
    25. 设置swap文件中的page数量,由于页表是在放在内存中的,在磁盘上每8个pages将消耗1byte的内存 vm-pages134217728
    26. 设置swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么 所有对swap的操作将会是串行化的,可能会造成比较长时间的延迟,默认为4 vm-max-thread 4
    27. 设置在向客户端应答时,是否把较小的包合并成一个包发送,默认为开启 glueoutputbuf yes
    28. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
      • hash-max-zipmap-entries 64
      • hash-max-zipmap-value 512
    29. 指定是否激活重置哈希,默认为开启
    30. 指定包含其他的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己特定的配置文件 include /path/to/local.conf

四、持久化(RDB,AOF)

  • RDB(Redis DataBase)
  • AOF(Append Only File)

4.1、RDB简介

  • RDB就是在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的snapshot快照,它恢复时是将快照文件直接读到内存中

  • Redis会单独创建(fork可以理解为备份)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对数据恢复的完整性不是非常敏感,那么RDB方式会比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

  • fork:fork的作用是复制一个与当前进程一样的进程。新进程的所有数据,变量,环境变量等数值都和原来的进程一致,但是是一个全新的进程,并作为原进程的子进程

  • rdb保存的是dump.rdb文件。真正工作中还要将整个dump.rdb备份一份放到另外一个电脑或者服务器的磁盘上,防止出现丢失或者损毁

  • rdb的触发条件:

    • save 900 1:15分钟一次更新操作,出发一次rbd的快照
    • save 300 10:5分钟十次更新操作,触发一次rdb的快照
    • save 60 10000
    • 如果想立即执行,执行save或者bgsave即可
      • save:只管保存,全部阻塞
      • bgsave:Redis会在后台异步进行快照操作,快照的同时还可以响应客户端的请求。可以通过lastsave命令获取最后一次成功执行快照的时间
  • 当flushall命令的时候会触发RDB的快照,会产生dump.rdb文件,但是里面是空的,然后再次启动Redis服务的时候,快照会自动恢复数据,但是,因为之前你shutdown的时候,快照记录的空数据会覆盖掉原有的数据,所以这个时候你再key *时,就是空了。所以我们之前复制备份的那个dump.rdb文件(再备份的时候可以取名为dump_bk.rdb)就会派上用处了,由配置文件,我们可以查看这个配置dbfilename dump.rdb,也就是说Redis恢复文件的名字叫dump.rdb。为了恢复文件数据,我们可以将备份好了的dump_bk.rdb再改名为dump.rdb,然后再次启动Redis服务,再key * 就可以查看到想要的数据了。

  • RDB的优势:

    • 适合大规模的数据恢复
    • 对数据的完整性和一致性要求不高
  • RDB的劣势:

    • 在一定时间间隔做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改

    • fork的时候,内存中的数据被克隆了一份,大概2倍的膨胀性需要被考虑,数据太大

4.2、AOF介绍

  • AOF是以日志的形式来记录每个写操作。将Redis执行过的所有写指令记录下来(读操作不记录),只追加文件但不允许改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
  • AOF保存的是appendonly.aof文件
  • 对应配置文件的位置:APPEND ONLY MODE
  • 默认AOF功能是关闭的:appendonly:no
  • .aof和.rdb可以存在在同一个文件夹下,并且同时存在的情况下,优先加载的是aof文件
  • 如果aof文件损毁,或者Redis通过aof的配置文件启动报错,可以执行命令:redis-check-aof --fix appendonly.aof进行修复。当然还有修复dump的命令:redis-check-dump --fix dump.rdb
  • AOF(appendonly file)的相关配置:
    • appendonly:yes or no
    • appendfilename:appendonly.aof尽量就别改名字了
    • Appendfsync:
      • Always:同步持久化,每次发生数据变化就会被记录到磁盘,性能较差但数据完整性好
      • EverySec:出厂默认推荐,异步操作,每秒记录。如果这一秒内down机,有数据损失
      • No
    • No-appendfsync-on-rewrite:重写时是否可以运用Appendfsync,用默认是no即可,保证数据安全性
    • Auto-aof-rewrite-min-size:设置重写的基准值
    • Auto-aof-rewrite-percentage:设置重写的基准值
  • rewrite:AOF越写越多,在实际的工作环境中,如果是那种互联网公司,这个AOF文件真的就是越写越多,这个时候AOF会有一个rewrite机制
    • AOF采用的是文件追加的方式,文件会越来越大,为了避免出现此种情况,新增了这个rewrite重写机制,当AOF文件的大小超过了设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
    • 原理:AOF文件持续增长而过大时,会fork出一条新进程来将文件重写。遍历新进程的内存中数据,每条记录有一条的set语句。重写aof文件的操作,并没有读取旧文件的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点相识
    • 触发机制:Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64MB时触发。配置文件中记录的:auto-aof-rewrite-percentage 100(1倍)和 auto-aof-rewrite-min-size 64mb
  • 优势:配置灵活,可以每秒同步,每次修改同步,永不同步
  • 劣势:
    • 相同的数据集的数据而言AOF文件要远大于rdb文件,恢复速度也慢于rdb
    • AOF运行效率也要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

4.3、RDB和AOF总结

  • RDB持久化方式能够在指定的时间间隔对数据进行快照存储
  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,是的AOF文件的体积不至于过大。
  • 只做缓存:如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。
  • 同时开启两中持久化方式:
    • 在这种情况下,当Redis重启的时候会优先载入AOF文件来恢复原始数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集完整
    • RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。但是尽量两者一起使用,因为RDB更适合用于备份数据库(AOF在不断变化,不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段
  • 性能建议:
    • 如果RDB文件只用作后背用途,建议只在slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则
    • 如果Enabled AOF,好处是在最恶劣情况下也只会丢失不超过两秒的数据,启动脚本只load自己的AOF文件即可。代价一是带来持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,还有AOF的重写基础之默认给的太小了64mb,可以设置在5G以上。
    • 如果不Enabled AOF,仅靠Master-Slave Replication实现高可用性也可以,能节省掉一大笔io也减少了rewrite时带来的系统波动。代价是如果Master或者Slave同时down掉,会丢失十几分钟的数据。启动脚本也要比较两个Master/Slave中的RDB文件,载入交新的那个,新浪微博就选了这种架构。

五、Redis的事务

  • 定义:可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其他命令插入,不许加塞

  • 能做什么:一个队列中,一次性,顺序性,排他性的执行一系列的命令

  • 使用MULTI命令开启Redis事务,会回复“🆗”。开启事务以后Redis会把这些一个个操作,排入队列中然后使用的操作会被EXEC命令调用执行。如果执行DISCARD就表示放弃事务。

  • 开启事务以后,执行各种操作,只要被加入到队列中(即,控制台返回queued),如果有违反Redis操作的命令,再执行EXEC的时候就会被显示出来,并且其他没有问题的操作也会被执行。但是如果执行的操作中有没有被加入到队列,而是返回erroe的话,那么就类似于Mysql的原子性那样,当执行EXEC后,所有操作都不会被执行。

  • 所以当被问道Redis是否支持事务的时候,应该回答部分支持,因为它没有那么强的一致性

  • 事务执行的三阶段:

    • 开启:一MULTI开启一个事务块
    • 入列:将多个命令入列到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列中
    • 执行:由EXEC命令触发事务
  • Redis事务的三个特性:

    • 单独的隔离操作:事务中所有的命令都会序列化,按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求打断
    • 没有隔离级别:队列中的命令没有提交之前都不会被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务里面的更新,在事务外查询不能看到”这个让人头疼的问题
    • 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
  • 常用命令:

    • DISCARD:取消事务,放弃执行事务块内的所有命令

    • EXEC:执行所有事务块内的命令。一旦执行了这个命令,那么之前加的监控锁都会被取消掉

    • MULT keyI:标记一个事务块的开始

    • UNWATCH:取消WATCH命令对所有key的监视

    • WATCH key[key...]:监视一个或多个key,如果在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断。

  • watch监控(可以理解为锁,并且必须在开启事务之前执行):

    • 乐观锁/悲观锁/CAS(Check And Set)
      • 悲观锁(类似Mysql表锁):悲观锁顾名思义,就是很悲观,每次去拿数据的时候都会认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁,读锁,写锁等,都是在操作之前先上锁。一般在执行备份到时候不希望被打扰就可以加上悲观锁
      • 乐观锁:(Optimistic lock)每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新整个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。乐观锁策略:提交版本必须大于记录当前版本才能执行更新
      • 如果想要监控某个key,那么一定要在开启事务之前执行,一旦某个key被监控,再想要修改这个key,那么就会返回异常,这个时候就可以适应unwatch key(实际上是取消掉对所有key的监控) 先取消掉对这个key的监控,再重新对这个key开始监控,就会拿到被修改后的真实的值
  • 小结:

    • WATCH命令,类似乐观锁,事务提交时,如果key的值已被别的客户端修改,比如,某个list已被别的客户端push或pop过了,整个事务队列都不会被执行
    • 提高WATCH命令在事务执行之前监控了多个keys,倘若在WATCH之后有任何key的值发生了变化,EXEC命令执行的事务都会被放弃,同时返回NullMulti-bulk异常,以通知调用者事务执行失败

六、删除策略

6.1、过期数据

  • 过期数据是指曾经设置过有效期的数据,到达了有效期,但是没被删除,最终留下来(存在于内存中的)的数据

  • Redis中的数据特征:

    • Redis是一种内存级数据库,所有的数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态
      • XX(数字):具有时效性的数据(还有多少秒过期)
      • -1:永久有效的数据
      • -2:已经过期的数据或已被删除的数据或未定义的数据
    • 那么有一个问题:已经过期的数据真的被删除了吗?

6.2、数据删除策略

  • 数据删除策略的目标:在内存占用和CPU占用之间寻找一种平衡,顾此失彼都会造成整体Redis性能的下降,甚至引发服务器宕机或内存泄漏(等晚上不忙的时候再清理过期数据)
  • Redis默认采用了惰性和定期两种策略
  1. 定时删除
    • 创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
    • 优缺点:节约内存,CPU压力大
  2. 惰性删除
    • 数据到达过期时间,不做处理。等到下次访问该数据时
      • 如果未过期,返回数据
      • 发现已过期,删除,返回不存在
  3. 定期删除
    • 当Redis启动服务器初始化的时候,会读取配置server.hz的值,默认是10(键入info server查看)
    • 周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度

七、Redis主从复制

7.1、概念:

  • 所谓主从复制,就是主机数据更新后根据配置和策略,自动同步到备机的机制。Master以写为主,Slave以读为主
  • 主要的用处:读写分离,容灾恢复

7.2、应用:

  1. 配从,不配主

  2. 复制的原理:

    • slave启动成功连接到master后会发送一个sync(同步)命令
    • master接到命令启动后台的存盘进程,同时收集所有接受到的用于修改数据集的命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
    • 全量复制:在slave服务在接受到数据库文件数据后,将其存盘并加载到内存中。首次复制时全量复制
    • 增量复制:master继续将新的所有收集到的修改命令依次传给slave,完成同步
    • 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
  3. 从库配置: 命令:slaveof + 主库IP + 主库端口。每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件

  4. 修改配置文件,如果三台客户端,一台主机,两天从机,这样就要建立3个配置文件,其中,简单的配置的话,需要修改:

    • redis.pid文件(可以根据主,从的端口号来命名,例如redis6379.pid,redis6380.pid)
    • 默认的端口号要修改,每个客户端要区分
    • 日志文件的名字也要修改,每个客户端要区分
    • 如果设置缓存的话,dump.rdb文件名也要修改,每个客户端要区分
  5. 常用的三招:

    1. 一仆二主:
      1. 分别根据三个配置文件开启,三个客户端的Redis服务
      2. 然后可以执行命令:info replication。可以查看当前客户端的信息:角色role,从机连接数connected-slave等等。这个时候你可以看见三个都是Master,因为还没有分配角色role
      3. 如果要想分配角色的话,可以执行命令:slaveof + 主库IP + 主库端口。会返回🆗,说明角色配置成功。
      4. 如果主机down,两个从机原地待命
      5. 如果从机down后,再次启动,执行info replication查看的话,角色role显示的是master,就是没有分配角色,应该重新执行命令:slaveof + 主库IP + 主库端口
    2. 薪火相传:
      1. 上一个Slave可以是下一个slave的master,slave同样可以接受其他slave的连接和同步请求,那么该slave作为了链条中下一个的master,可以有效的减轻master的写压力
      2. 中途变更转向,会清除之前的数据,重新建立拷贝最新的数据 命令:slaveof 新主库IP 新主库端口
    3. 反客为主:
      • 如果主机down,两个从机的状态是原地待命,但是可以在任意从机中执行命令:slaveof no one 使当前转成主机(主数据库),另外一个从机再去连接这个新的主机。原来down掉的主机,重新启动的时候角色role没有分配,也可以连接这个新的主机,成为从机。这样就完成了从机的反客为主。

7.3、哨兵模式(sentinel):

  1. 概念介绍
    • 上面提到的三招在工作中已经不怎么使用了,现在使用更多的是“哨兵模式”。
    • “哨兵模式“说白了就是反客为主的自动版,能够在后台监控主机是否故障,如果故障会根据投票数自动将从库转换为主库
  2. 应用:
    1. 首先调整结构,6379带80,81。还是一仆二主
    2. 自定义的/myredis目录下,新建sentinel.conf文件,名字绝对不能错!
    3. 配置哨兵模式的配置文件,就是刚才新建的sentinel.conf,在里面填写内容:
      • sentinel monitor 被监控数据库的名字(自己起名字) 被监控数据库的IP 被监控数据库的端口号 1。1代表的是基准票数:主机down后slave投票看让谁接替成为主机,得票数多的就成为主机。
    4. 启动哨兵:举例:Redis-sentinel/myredis/sentinel.conf这里只是举例,目录可以变的,但是sentinel.conf这个名字不能改变!
    5. 如果将作为主数据库的6379down,哨兵会自动的根据投票选出一个新的主数据库,但是这个时候原来down掉的6379又重新启动之后,哨兵会自动的监控到它,然后会让它成为从数据库
    6. 复制的缺点:就是会存在延时,因为会有同步操作,slave数量的增加也会增加延时

八、Redis集群

8.1、Redis集群结果设计

8.1.1、数据存储的设计

8.1.2、相互间通讯的设计

8.2、Redis的集群搭建

  • 集群的搭建的解决方案有很多:我现在只知道ruby 和 ruby-gem脚本写的一套解决方案

  • 使用cluster搭建

    • 在配置文件中至少要配置三点
      1. cluster-enabled yes
      2. cluster-config-file nodes-端口号.conf
      3. cluster-node-timeout 100000(ms,在工作中这个值不会像这个这么小):节点服务响应超时时间,用于判定该节点是否下线或者切换为从节点。在集群环境中,主节点断线再之后,本来与之相连的从节点会变为主节点,而断线的主节点再连接,就变为了从节点。
  • 准备完集群所用的所有redis(就是客户端)。

  • 执行命令:

    ./redis-trib.rb  create  --replicas  1  ip:端口号[ip:端口号...]//里面的1,代表一个master连接一个slave,如果写2就是一个master连接两个slave。//假如一共有6个客户端做集群,并且要求一个master连接一个slave,那么ip就要写6个,这样的话,执行这个命令,系统会自动的将前3个作为master,后面3个作为slave。
    
  • 集群创建好之后,set数据的时候,加命令:redis-cli -c。然后再set数据,就不会报错了。否则会提示你slot错误

九、解决方案

  • 缓存预热

    • 现象:启动时候迅速宕机
      • 问题排查:
        1. 请求数量太大
        2. 主从之间数据吞吐量较大,数据同步操作频度较高
      • 解决方案:
        1. 对数据进行分类,根据级别,Redis优先加载级别较高的数据
        2. 统计访问频度较高的热点数据
        3. 利用LRU数据删除策略,构建数据留存队列,例如:storm和Kafka配合
        4. 利用分布式多服务器同时进行数据读取,加速数据加载过程
        5. 可以使用脚本程序固定触发数据预热过程
        6. 如果条件允许,可以使用CDN(内容分发网络)效果会更好
      • 总的来说,就是避免以后让redis缓存中的数据为0,以后直接访问数据库,然后再将数据缓存。
  • 缓存雪崩

    • 问题排查
      • 在一个较短的时间内,缓存中较多的key集中过期
      • redis未命中,redis向数据库获取数据
      • Redis大量请求被积压,开始出现超时现象
    • 解决方案:
      • 删除策略从LRU(定期删除)切换为LFU(按命中次数删除)
      • 超热数据使用永久key
    • 总结:缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如果能有效的避免key的过期时间集中,可以有效地解决缓存雪崩的出现。
  • 缓存击穿

    • 问题排查
      • Redis中某个key过期,且该key的访问量巨大
      • 多个数据请求从服务器直接压到Redis后,都未命中
      • Redis在短时间之内发起大量对数据库中同一数据的访问
      • 简单说:单个key高热数据,key过期
    • 解决方案和缓存雪崩类似
    • 总结:缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,并且未能命中redis,发起了大量对同一数据的数据库访问,导致数据库服务器的压力剧增。
  • 缓存穿透

    • 问题排查
      • Redis出现大面积的未命中
      • 出现非正常的URL访问
    • 总结:缓存击穿访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次访问数据库,导致数据库服务器的压力剧增。
  • Redis性能指标的监控

    • 性能指标:Performance

      name Description
      latency Redis响应一个请求的时间
      instantanceous_ops_per_sec 平均每秒处理请求的总数
      hit rate(calculated) 缓存命中率(计算出来的)
    • 内存指标:Memory

      name Description
      used_memory 已使用的内存
      mem_fragmentation_ratio 内存碎片率
      evicted_keys 由于最大内存限制被移除的key的数量
      blocked_clients 由于BLPOP,BRPOP,BRPOPLPUSH而被阻塞的客户端
    • 基本活动指标:Basic activity

      name Description
      connected_clients 客户端连接数
      connected_slave slave数量
      master_last_io_seconds_ago 最近一次主从交互之后的秒数
      keyspace 数据库中key的总数
    • 持久性指标:Persistence

      name Description
      rdb_last_save_time 最后一次持久化保存到磁盘的时间戳
      rdb_changes_since_last_save 自最后一次持久化以来数据库的更改数
    • 错误指标:Error

      name Description
      rejected_connections 由于达到maxclient限制而被拒绝的连接数
      keyspace_misses key值查找失败(没有命中)的次数
      master_link_down_since_seconds 主从断开的持续时间(以秒为单位)
  • 监控方式:

    • 工具类:了解一下
      • Cloud Insight Redis
      • Redis-stat
    • 命令类:这个不再多记录了,以后用到了的话,再查
      • benchmark
      • redis cli
        • monitor
        • showlog
posted @ 2021-05-15 16:52  BS_Everything  阅读(75)  评论(0)    收藏  举报