redis全面总结

2021-02-01 10:45:28 浏览数 (1)

  1. redis是什么 redis是c语言开发的一个开源的高性能键值对的内存数据库,一个内存中的数据结构存储系统,他可以用做数据库,缓存,消息中间件.支持多种类型的数据结构,如字符串,列表,哈希,集合,有序集合,bitmaps,Hyperloglogs,geo(地理空间) 字符串,是redis的基本类型,一个key对应一个value,最大可以存储512M,常用的命令有set,get,mget等,应用场景微博数,粉丝数 列表,是一个字符串列表,按照插入的顺序排序,是一个双向链表,支持反向查找和遍历,常用命令lpush,rpush,lpop,rpop,lrange等命令,使用场景关注列表,好友列表等 哈希,和我们java中的hashmap类似,hash特别适合存储对象常用命令,hget,hset,hgetall等,应用场景可以存储用户的信息商品的信息等, set,是一种无序集合,支持去重操作,但没有顺序,常用命令,sadd,spop,smemebers,sunion操作,方便集合之间的操作,引用场景,判断某个元素是否存在set集合中 zset,和set类似,且有序,使用一个额外的参数score为成员排序,内部使用hashmap和跳跃表实现存储和有序,HaspMap存放成员到score的映射,而跳跃表存放所有的成员,使用跳表实现比较高的查询效率,常用命令zadd,zrange,zrem,zcard等待,使用场景用分数进行成员的从小到大的排序 bitmaps(位图),实际上就是字符串,但是他可以对字符串的位进行操作,可以把位图当做一个以位为单位的数组,数组中只有0或1,如[00111010010010...],数组的下表就是偏移量,最大长度是512M,即2^32个比特币,常用命令setbit ,getbit,bitcount,使用场景,如用户的打卡,网站的浏览次数等等,(如果我们使用String记录用户的打卡,当用户量非常大的时候,就会占用内存,但是使用位图,一年365天相当于365bit,1字节=8bit,也就46字节而已,比如setbit user1 0 1表示user1第一天打卡成功,setbit user1 1 0表示user1第二天打卡失败) Hyperloglogs,提供一种不太准确的基数统计算法(统计一批数据中的不重复的个数),常用命令pfadd,pfcount,pfmerge等等,使用场景网页的UV(用户访问网站的次数) geospatial(地理空间),把指定的地理空间位置添加到指定的key中,如geoadd city 116.40 39.9 北京(geoadd key longitude latitude member),常用命令geoadd,geopos,geodist,georadius使用场景,计算两点之间的距离,查看指定位置附近范围的人.
  2. redis雪崩,缓存穿透,击穿是什么意思,如何解决 redis雪崩,我们认知的缓存都是存储热点数据,我们查询数据,先去缓存查找,如果缓存不存在,就直接到数据库查找,但是有一种情况我们要注意,比如,我们把缓存的key的有效时间是晚上0点,然而再0点正好有一次大促销活动,假设0点的请求量为8000请求/秒,同时所有的key失效了,因此8000个请求直接落到了数据库,此时数据库还没有反映过来,就直接过了,这就是雪崩。 如果遇到这种情况,我们可以给redis存储的数据设置一个随机事件,而不是设置同一时刻,这样就可以保证不会大面积失效,当然如果redis是集群,可以把热点数据分布在不同的redis库中也能避免全部失效,最暴力的就是不设置过期时间,更新操作在修改缓存就可以了 缓存穿透,其实就是缓存和数据库都没有数据,此时有大量的请求,获取不存在的数据,如我们数据的id都是大于1的值,但是黑客使用id=-1的请求不断的攻击,导致数据库压力过大,严重的时候会导致数据挂掉 如果遇到这种情况,我可以给请求入参添加校验,只有合法的参数才能请求服务,也可以利用布隆过滤器,当不存在的时候就必要查询请求了。 缓存击穿,是指一个大热点key,百万级用户不断请求,但是在某一刻失效了,也会导致大量请求到数据库,导致数据库挂掉, 如果遇到这种情况,我也可以设置缓存不过期,或者使用分布式锁。
  3. redis是单线程吗,为什么这么快 resi是单线程,但是官方声称redis的QPS可达100000 ,为什么是单线程还这么快,如下原因
    1. redis基于内存,所有数据都在内存中,类似hashMap,时间复杂度为O(1)
    2. 数据结构简单,对数据操作也简单
    3. 使用单线程,避免了不必要的上下文切换和竞争条件不存在多线程的CPU切换,也不存在加锁释放锁操作。
    4. 使用多路复用IO模型,非阻塞IO.
  4. redis和memcached有什么区别
    1. redis可以持久化,而memcached数据全部在内存中
    2. redis支持多种数据结构,memcached支持key-value
    3. 使用的协议不一样,memcached的客户端通过TCP连接服务器通信,redis使用RESP协议通信,是专门为Redis设计的通讯协议,redis直接构建自己的VM机制,因为一般的系统调用系统函数,会浪费一定的时间去移动和请求.
    4. value的大小,redis可以达到1GB,而memcached只有1MB
  5. Redis的淘汰策略有哪些 策略 描述 volatile-lru 从已设置过期时间的KV集中,优先对最近最少使用的淘汰 volatile-ttl 从已设置过期时间的KV集中,优先对剩余时间短的数据进行淘汰 volatile-random 从已设置过期时间的KV集中,随机选择数据淘汰 volatile-lfu 从已设置过期时间KV集中,优先选择最不经常的数据进行淘汰 allkeys-lru 从所有KV集中,优先对最近最少的数据进行淘汰 allkeys-random 从所有KV集中,随机选择数据淘汰 allkeys-lfu 从所有kV集中,最不长用的数据进行淘汰 noeviction 不淘汰,直到超过最大内存,返回错误信息
  6. redis的持久化有几种 redis的持久化操作有两种,一种是RDB,RDB是以快照形式直接把内存的数据保存在一个dump文件中,而另外一种AOF,是把每一次修改命令存储在文件里,redis默认是RDB持久化方式,但是在重启的的时候,他会优先使用AOF,因为AOF保存的数据比RDB更加完成, RDB触发的方式
    1. 手动触发,使用save,bgsave命令
      1. save会阻塞redis服务器进行直到RDB文件创建完毕之后,否则服务器在这期间一直阻塞,而bgsave是会fork一个子进程,有子进程负责创建RDB文件,而父进程会继续处理请求。
    2. 自动触发,通过配置文件的命令save m n,满足下面任何一个条件如
      1. save 900 1,到900秒内的时候,如果redis至少发生一次变化
      2. save 300 10,到300秒内,至少发生10变化
      3. save 69 10000,到10000秒内,知道发生10000变化
    3. 其他原因如主从复制场景,从节点执行全量复制操作,或执行shutdown命令时,都会执行RDB持久化
  7. redis中bgsave执行流程
  1. redis父进程判断是否有执行save,bgsave,bgrewriteaof的子进程,如果在执行则bgsave命令直接返回,
  2. 父进程执行fork操作创建子进程,这个过程父进程阻塞,redis不能执行客户端的任何命令
  3. 父进程fork后,bgsave命令返回"backgroud saving started"信息并不再阻塞父进程,并可以相应其他命令
  4. 子进程创建RDB文件,根据父进程内存快照生成临时文件,完成对原来文件的替换
  5. 子进程发送信号给父进程表示完成,父进程更新统计信息
  1. AOF运行原理

开启AOF,需要在配置文件中配置appendonly yes,而AOF执行流程包括下面三种

  1. 命令追加,将redis的写命令追加到缓冲区aof_buf
  2. 文件写入和文件同步,根据不同的同步策略将aof_buf的内容同步到磁盘
  3. 文件重写,定期重写AOF文件,达到压缩的目的

命令追加

redis是将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次写命令直接写入磁盘,导致硬盘IO成为redis负载的瓶颈

文件写入和文件同步

为了提高文件写入的效率,在现在操作系统中,当用户调用write函数将数据写入文件的时候,操作系统通常将数据暂存到一个内存缓冲区里,当缓冲区慢了或者超过了指定大小后,才会将内存缓存区中的数据写入到内存,这样虽然提高了效率,但是存在安全问题,比如计算机停机,内存缓存区的数据就会丢失,因此操作系统提供了文件同步的同步函数fsysnc,fdatasync函数,可以强制把操作系统立刻将缓存区中的数据写入到硬盘里,从而保证数据的安全性,文件同步策略的策略由参数appendfsync控制

代码语言:javascript复制
appendfsync always,
//命令写入aof_buf后立刻调用系统fsync操作同步到AOF
appendfsync no
//命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,
//此时的同步有操作系统负责,通常同步周期30s
appendfsync everysec
//命令写入aof_buf后调用系统write操作,write完成后线程返回,fsync同步文件操作由专门
//的线程每秒同步一次

文件重写

文件重写是指定期重写AOF文件,减少AOF文件的体积,重写就是把redis中的数据转成写命令,同步到AOF文件,不会对旧的AOF文件进行任何读取写入操作。具体的操作流程

  1. redis父进程判断是否存在bgsave/bgrewriteaof的子进程,如果存在就直接返回,
  2. 父进程fork出子进程,这个过程父进程阻塞
  3. 父进程fork后,bgrewriteaof命令就返回"Background append only file rewrite started",此时父进程不在阻塞,同时redis客户端写命令写入AOF缓存区,并根据同步策略同步到旧的AOF文件
  4. 在上图中3.2步骤,在子进程生成新的AOF文件的同时,新的命令会同时写入aof_rewrite_buf中,这样就可以防止数据丢失,
  5. 子进程根据内存的快照,按照命令合并规则写入新的AOF文件
  6. 子进程完成新AOF文件后,就会通知父进程,父进程更新统计信息,
  7. 在5.2步骤,父进程会把aof_rewrite_buf的数据写入到新AOF文件,这样就保证了新的AOF保证了数据的完成性
  8. 最后使用新AOF文件替换旧的AOF文件
  1. RDB和AOF的优缺点 命令 RDB AOF 启动优先级 低 高 体积小大 恢复速度 快慢 数据安全性 丢数据 根据策略决定 轻重 重 轻
  2. 如何选择RDB和AOF持久化策略

两种持久化策略持久化都是有性能代价的,RDB持久化,在使用bgsave的时候会阻塞主进程,子进程向硬盘写数据会带来Io压力,AOF持久化,向硬盘写数据的频率越大,IO压力更大,甚至导致AOF阻塞问题,

而在实际场景,根据数据量,应用数据安全的要求,预算限制等不同情况,会有各种各样的持久化策略,我们可以单独用一种策略,也可以两种混合使用.

如果数据完全对丢失数据对业务没有任何影响,就可以不进行任何持久化如果在单机情况,如我们自己练习,如何可以接受丢失数据,可以使用RDB,如果只能接受秒级别的数据丢失,可以选择AOF

如果是我们的生产环境,主从环境,一般我们可以master关闭持久化,可以在slave关闭RDB,开启AOF,然后关闭自动重写,添加定时任务,定时调用bgrewriteaof

上面只是简单的举例,所以如果你可以接受几分钟你的数据丢失,那么可以使用RDB,否则可以使用AOF持久化,但是AOF每一条命令追加磁盘,会降低redis性能,对于数据备份和灾难恢复,定时生成RDB快照非常便于进行数据库备份,但是RDB的回复数据集的速度比AOF恢复速度快,如果混合持久化,AOF重写的时候就直接把RDB的内容写到AOF文件开头,这样做的好处是可以结合RDB和AOF的优点,快速加载同时避免丢失过多的数据,当然AOF里面的RDB部分是压缩格式不再是AOF格式,可读性差.

  1. redis主从复制过程 主从模式结合哨兵模式能够解决掉单点模式,提高redis可用性,从节点仅提供读服务,而主节点提供些操作,对于读多写少的情况,可以从节点的数量,从而提高响应效率,而主节点和从节点直接的复制过程如下
    1. 从节点执行slave masteip masterPort.保存主节点信息
    2. 从节点的定时任务发现主节点信息,建立和主节点的socker链接
    3. 从节点发送ping信号,主节点返回pong,两边就能互相通讯
    4. 连接建立以后,主节点将所有数据发送给从节点进行数据同步
    5. 主节点把当前的数据同步给从节点后,便完成了复制的建立过程,接下来,主节点就会持续把写命令发送给从节点,保证主从数据一致性。
  2. 主从复制的原理

redis2.8之前使用的sync runid offset同步命令,redis2.8之后使用的psync runid offset命令,连个都是同步的命令,但是sync仅支持全量同步,而psync支持全量同步和增量同步,

runid:每个redis节点都有一个唯一的id,且每次重启都是重新生成

offset:主节点和从节点各自维护自己的主从复制的偏移量offset,当主节点写入命令时候,会记录offset=offset 命令字节长度,从节点在收到主节点发送的命令后,也会记录自己的offset,并把自己的offset同步给主节点,这样主节点保存自己的和从节点的offset,通过对比就可以知道数据是否一致

repl_backlog_size:保存在主节点的一个固定长度的先进先出的队列,默认大小是1MB

  • 主节点发送数据给从节点过程中,主节点还会进行一些写操作,这个时候数据就会写入复制缓存区中,从节点完成主节点数据同步后,主节点将缓存继续发送给从节点,用于部分复制
  • 主节点不但会把写命令发送给从节点,而且会写入复制缓冲区,用户复制命令丢失的数据补救

全量复制的过程和部分复制过程如下图

全量复制的主要步

  1. 从节点发送psync ? -1命令,第一次发送,不知道主节点的runid因此使用?,且默认offset=-1
  2. 主节点发现是第一次复制,返回FULLRESYNC runid offset,runid是主节点的runid,offset是主节点的offset
  3. 从节点接收到主节点的信息,保存到info中
  4. 主节点发送FULLRESYNC后,启动bgsave命令,生成RDB文件进行持久化
  5. 主节点发送RDB文件给从节点,到从节点加载数据完成期间,主节点会把写命令放到缓冲区中
  6. 从节点清理自己的数据库数据
  7. 从节点加载RDB文件,将数据保存在自己的数据库中
  8. 如果从节点开启了AOF,从节点异步重写AOF文件

增量复制过程如下

  1. 当出现网路抖动,超过了连接时间,主从就会断开连接
  2. 但是在这期间主节点还会接受到命令,会把命令写入到复制缓冲区中(repl-bacjlog-buffer)
  3. 当网络恢复以后,从节点会主动再次连接
  4. 从节点发送psync runid offset,runid是当前自己的runid,offset是主节点给他偏移量
  5. 主节点接受到从节点的runid,和offset,会进行核对,其中master如果发现偏移量在复制缓冲区中,根据offset查找复制缓存的数据,且对从节点发送continue表示可以继续部分复制
  6. 主节点把缓存中的数据发送给从节点,保证主从的数据保持一致
  1. 主从复制会有什么问题
    1. 一旦出节点宕机,从节点晋升为主节点,同时需要修改应用方的主节点地址,还要使用命令使所有的从节点去复制新的主节点,整个过程需要人工干预
    2. 主节点的写能力受到单机的限制
    3. 原生复制在复制中断后,从界定会发起psync,此时如果同步不成功,则进行全量复制,期间会导致短暂的停顿
  2. 如何解决主从复制的问题 当然我可以使用哨兵模式,下图就是就是哨兵模式

如图,是哨兵的架构图,哨兵的主要功能就是主从节点存活检测,主从运行情况检测,自动故障转移,主从切换,可以执行以下四个任务

  • 监控,不断检查主服务器和从服务器是否正常运行
  • 通知,当redis服务器出现问题,哨兵通过api通知管理员发出通知
  • 自动故障转移,当主节点不能正常工作的时候,sentinal就会开启一次自动故障转移操作,会把其中一个从节点晋升为新的主机节,且会把其他从节点重新指向新的主节点,不需要任何人为干预
  • 配置提供者,在哨兵模式下,客户端在初始化的时候链接的是sentine节点,从中获取主节点的信息

  1. 哨兵原理

上图就是一张哨兵的架构图,我接着说他是如何工作的

  1. 每个sentinel节点都需要定期执行向所有主从服务器以及其他sentinel实例发送ping命令
  2. 但是当主节点最后一次回复ping命令超过了设置的时间(down-after-milliseconds参数指定的值),那么就会认为这个节点主观下线
  3. 如果一个主服务器被标记为主观下线,此时全部的sentinel节点都会每秒一次的频率确认主服务器的确是否进入主观下线状态
  4. 此时大多数sentinel确认主服务器为下线,那个这个主服务器标记为客观下线
  5. 正常情况下,每个sentinel会以每10秒一次的频率向已知的所有主从服务器发送info命令,但当一个主服务被标记为客观下线时,sentinel就会把info命令的频率改为每秒一次
  6. sentinel和其他sentinel协商客观下线的主节点状态,如果处于sdown状态则重新投票选出新的主节点,将剩余的节点指向新的节点进行数据复制
  7. 但是如果没有足够sentinel认为主服务下线时候,主服务器的客观下线状态就会移除,同时当主服务器的ping命令返回有效的恢复时候,就会移除主观下线。

0 人点赞