【面试快问块答】一:Redis七问七答

 

1、redis为什么是key,value的,为什么不是支持SQL的?

答:redis其实是一个key/value数据库,是一个nosql数据库,不是关系型数据库。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销, 所以redis是单进程单线程的, 它支持存储的value类型相对更多最为常用的数据类型主要有五种:String、Hash、List、Set和Sorted Set。

       查找和操作的时间复杂度都是O(1),避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗

2、redis是多线程还是单线程?

答:Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。其中执行命令阶段,由于Redis是单线程来处理命令的,所有到达服务端的命令都不会立刻执行,所有的命令都会进入一个队列中,然后逐个执行,并且多个客户端发送的命令的执行顺序是不确定的,但是可以确定的是不会有两条命令被同时执行,不会产生并发问题,这就是Redis的单线程基本模型。Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。

       Redis基于Reactor模式开发了自己的网络事件处理器——文件事件处理器,文件事件处理器使用I/O多路复用程序来同时监听多个socket;操作系统的这个功能是通过select/poll/epoll/kqueue之类的系统调用函数来实现,这些函数都可以同时监视多个描述符的读写就绪状况,这样,多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这就叫I/O多路复用,这里的“多路”指的是多个网络连接,“复用”指的是复用同一个Redis处理线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 I/O 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,所有 Redis 具有很高的吞吐量。

      Redis 6.0首次加入了多线程,默认不开启,在conf文件进行配置

      io-threads-do-reads yes

      io-threads 线程数

      官方建议:4 核的机器建议设置为 2 或 3 个线程,8 核的建议设置为 6 个线程,线程数一定要小于机器核数,尽量不超过8个。Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。

     

流程简述如下

  • 主线程负责接收建立连接请求,获取 Socket 放入全局等待读处理队列。
  • 主线程处理完读事件之后,通过 RR(Round Robin)将这些连接分配给这些 IO 线程。
  • 主线程阻塞等待 IO 线程读取 Socket 完毕。
  • 主线程通过单线程的方式执行请求命令,请求数据读取并解析完成,但并不执行。
  • 主线程阻塞等待 IO 线程将数据回写 Socket 完毕。
  • 解除绑定,清空等待队列。

 

该设计有如下特点:

  • IO 线程要么同时在读 Socket,要么同时在写,不会同时读或写。
  • IO 线程只负责读写 Socket 解析命令,不负责命令处理。

      详细请参考文章:https://www.cnblogs.com/gz666666/p/12901507.html

 

3、redis的持久化开启了RDB和AOF下重启服务是如何加载的?

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Apend Only File)。

RDB方式

RDB方式是一种快照式的持久化方法,将某一时刻的数据持久化到磁盘中。

•redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。
•对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何IO操作的,这样就确保了redis极高的性能。
•如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。

AOF方式

AOF方式是将执行过的写指令记录下来,在数据恢复时按照丛前到后的顺序再将指令执行一遍。

•AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),因为在这种情况下,redis仍然可以保持很好的处理性能,即使redis故障,也只会丢失最近1秒钟的数据。
•如果在追加日志时,恰好遇到磁盘空间满、inode满或断电等情况导致日志写入不完整,也没有关系,redis提供了redis-check-aof工具,可以用来进行日志修复。
•因为采用了追加方式,如果不做任何处理的话,AOF文件会变得越来越大,为此,redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时,redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。举个例子或许更形象,假如我们调用了100次INCR指令,在AOF文件中就要存储100条指令,但这明显是很低效的,完全可以把这100条指令合并成一条SET指令,这就是重写机制的原理。
•在进行AOF重写时,仍然是采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性。

二. 两种方式优缺点

1. RDB方式

•优点:

1.RDB是一个单一的紧凑文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集.
2.RDB是一个紧凑的单一文件,方便传送,适用于灾难恢复.
3.RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
4.与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些.

•缺点:

1.Redis意外宕机,可能会丢失几分钟的数据(取决于配置的save时间点)。RDB方式需要保存珍整个数据集,是一个比较繁重的工作,通常需要设置5分钟或者更久做一次完整的保存。
2.RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续更久。

2. AOF方式

•优点

1.使用AOF 会让Redis数据更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据.
2.AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题.
3.Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
4.AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析也很轻松。 导出AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

•缺点

1.对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
2.根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间。

三. 配置方式

1. RDB配置方式

默认情况下,是快照rdb的持久化方式,将内存中的数据以快照的方式写入二进制文件中,默认的文件名是dump.rdb
 redis.conf配置:

save 900 1

save 300 10

save 60 10000

以上是默认配置:900秒之内,如果超过1个key被修改,则发起快照保存;
300秒内,如果超过10个key被修改,则发起快照保存 ;
1分钟之内,如果1万个key被修改,则发起快照保存 ;

这种方式不能完全保证数据持久化,因为是定时保存,所以当redis服务down掉,就会丢失一部分数据,而且数据量大,写操作多的情况下,会引起大量的磁盘IO操作,会影响性能。

所以,如果这两种方式同时开启,如果对数据进行恢复,不应该用rdb持久化方式对数据库进行恢复。

2. AOF 配置方式

使用aof做持久化,每一个写命令都通过write函数追加到appendonly.aof中.
配置方式:启动aof持久化的方式

appendonly yes

重启服务是如何加载

 

4、redis如果做集群该如何规划?AKF/CAP如何实现和设计?

     集群方案:

一、官方cluser方案

从redis 3.0版本开始支持redis-cluster集群,redis-cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他节点连接。redis-cluster是一种服务端分片技术。客户端与redis节点直连,不需要连接集群所有的节点,连接集群中任何一个可用节点即可;整个cluster被看做是一个整体,客户端可连接任意一个节点进行操作,当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点。为了增加集群的可访问性,官方推荐的方案是将node配置成主从结构,即一个master主节点,挂n个slave从节点。如果主节点失效,redis cluster会根据选举算法从slave节点中选择一个上升为master节点,整个集群继续对外提供服务。

二、哨兵模式

Sentinel(哨兵)是Redis的高可用性解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器以及这些主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。

每个Sentinel以每秒钟一次的频率向它所知的Master、Slave以及其他Sentinel实例发送一个PING命令。如果一个实例距离最后一次有效回复PING命令的时间超过down-after-milliseconds选项所指定的值,则这个实例会被Sentinel标记为主观下线。如果一个Master被标记为主观下线,则正在监视这个Master的所有Sentinel要以每秒一次的频率确认Master的确进入了主观下线状态。当有足够数量的Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态,则Master会被标记为客观下线。在一般情况下,每个Sentinel会以每10秒一次的频率向它所知的所有Master、Slave发送INFO命令。当Master被Sentinel标记为客观下线时,Sentinel向下线的Master的所有Slave发送INFO命令的频率会从10秒一次改为每秒一次。若没有足够数量的Sentinel同意Master已经下线,Master的客观下线状态就会被移除。若Master重新向Sentinel的PING命令返回有效值,Master的主观下线状态就会被移除。

三、codis

codis是一个分布式的Redis解决方案,由豌豆荚开源,对于上层的应用来说,连接codis proxy和连接原生的redis server没什么明显的区别,上层应用可以像使用单机的redis一样使用,codis底层会处理请求的转发,不停机的数据迁移等工作,所有后边的事情,对于前面的客户端来说是透明的,可以简单的认为后边连接的是一个内存无限大的redis服务。

四、客户端分片

分区的逻辑在客户端实现,由客户端自己选择请求到哪个节点。方案可参考一致性哈希,这种方案通常适用于用户对客户端的行为有完全控制能力的场景

AKF入门:https://baijiahao.baidu.com/s?id=1632326446570197886&wfr=spider&for=pc

CAP入门:https://www.cnblogs.com/huanghuanghui/p/9592016.html

面试中经常会遇到分布式一致性问题,最常见的是 Redis 和 MySQL 的一致性问题。回答这类问题,不管你采用哪种缓存模式,都有漏洞,都不完美。因此,最好的做法就是利用业务的特点,做到最终的一致性。所以这个问题也可以换成,如何保证redis在高可用状态下的数据一致性问题。

 

5、10万用户一年365天的登录情况如何用redis存储,并快速检索任意时间窗内的活跃用户?

bitmap本质上是一个string类型,只是他操作的是string的某个位是0还是1。

setbit和getbit 两条命令是对字符串的位操作。每个位只能是0/1,然后用Bitcount命令计算这个key有多少个1。

记录用户每天的登录情况

以日期为key,userId为偏移量,执行命令setbit day useId 1
例如:小梅(userId= 1992)和小明(userId=9877)在 2020-10-01日登录;
setbit '2020-10-01' 1992 1
setbit '2020-10-01' 9877 1

小梅(userId= 1992)和小达(userId=10000)在 2020-10-02日登录;
setbit '2020-10-02' 1992 1
setbit '2020-10-02' 10000 1

统计2020-10-01和2020-10-02活跃的用户数量只需要执行命令

bitcount '2020-10-01'
bitcount '2020-10-02'

统计2020-10-01到2020-10-02这个时间端活跃的用户数量只需要执行命令

bitop or 'result' '2020-10-01' '2020-10-02'
bitcount 'result'

查询小梅在2020-10-01日有没有登录只需执行命令

getbit '2020-10-01' 1992

 

6、redis的5种Value类型你用过几种,能举例吗?

Redis本身存储就是一个hash表,实际实࣫比hash表更复一些,Key只有String类型,Value包括String ,Set,List,Hash,Zset五中类型

STRING: 可以是字符串,整数或者浮点数以及任何二进制格式的数据 对字符串或一部分进行操作,对整到和浮点数进行自增或自减操作
LIST: 一个链表,链表上的每个字节都包含一个字符串 根据偏移量查找或移除某元素
SET: 包含字符串的无序收集器,并且包含的每个字符串都不重复 添加,获取,移除单个元素,是否存在,计算交集,差集,并集,从集合中取元素
HASH :包含键值对的无序散列表 添加,获取,删除多个键值对,获取所有键值对
ZSET :字符集成员member和浮点数分值score之间的有序映射,元素的排列顺序由分值的大小决定 添加获取删除单个元素,根据分值范围或成员来获取元素

 

7、100万并发4G数据,10万并发400G数据,如何设计Redis存储方式?

首先我们要搞清楚redis的主从结构数据复制机制

Redis通过主从架构,实现读写分离,主节点负责写,并将数据同步给其他从节点,从节点负责读,从而实现高并发读取;Redis高并发的同时,还需要容纳大量的数据:一主多从,每个实例都容纳了完整的数据,比如redis主就10G的内存量,其实你就最对只能容纳10g的数据量。如果你的缓存要容纳的数据量很大,达到了几十g,甚至几百g,或者是几t,那你就需要redis集群,而且用redis集群之后,可以提供可能每秒几十万的读写并发。

replication的核心机制

  redis采用异步方式复制数据到slave节点,不过redis 2.8开始,slave node会周期性地确认自己每次复制的数据量
  一个master node是可以配置多个slave node的
  slave node也可以连接其他的slave node
  slave node做复制的时候,是不会block master node的正常工作的
  slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了
  slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量

master持久化对于主从架构的安全保障的意义

  如果采用了主从架构,那么建议必须开启master node的持久化!

  不建议用slave node作为master node的数据热备,因为那样的话,如果你关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,salve node数据也丢了

  第二个,master的各种备份方案,要不要做,万一说本地的所有文件丢失了; 从备份中挑选一份rdb去恢复master; 这样才能确保master启动的时候,是有数据的

master同步数据给slave的过程

  当启动一个slave node的时候,它会发送一个PSYNC命令给master node

  如果这是slave node重新连接master node,那么master node仅仅会复制给slave部分缺少的数据; 否则如果是slave node第一次连接master node,那么会触发一次full resynchronization

  开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。

  RDB文件生成完毕之后,master会将这个RDB发送给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。

  slave node如果跟master node有网络故障,断开了连接,会自动重连。master如果发现有多个slave node都来重新连接,仅仅会启动一个rdb save操作,用一份数据服务所有slave node。

 

主从复制的断点续传

  从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份

  master node会在内存中常见一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。

  如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制

  但是如果没有找到对应的offset,那么就会执行一次resynchronization

无磁盘化复制

  master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了

过期key处理

  slave不会过期key,只会等待master过期key,如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。

复制的完整流程

  slave node启动,仅仅保存master node的信息,包括master node的host和ip(redis.conf里面的slaveof配置的),但是复制流程没开始

  slave node内部有个定时任务,每秒检查是否有新的master node要连接和复制,如果发现,就跟master node建立socket网络连接
  slave node发送ping命令给master node
  口令认证,如果master设置了requirepass,那么salve node必须发送masterauth的口令过去进行认证
  master node第一次执行全量复制,将所有数据发给slave node
  master node后续持续将写命令,异步复制给slave node

 

数据同步相关的核心机制

指的就是第一次slave连接msater的时候,执行的全量复制,那个过程里面你的一些细节的机制

(1)master和slave都会维护一个offset

  master会在自身不断累加offset,slave也会在自身不断累加offset
  slave每秒都会上报自己的offset给master,同时master也会保存每个slave的offset

  这个倒不是说特定就用在全量复制的,主要是master和slave都要知道各自的数据的offset,才能知道互相之间的数据不一致的情况

(2)backlog

  master node有一个backlog,默认是1MB大小
  master node给slave node复制数据时,也会将数据在backlog中同步写一份
  backlog主要是用来做全量复制中断候的增量复制的

(3)master run id

  info server,可以看到master run id
  如果根据host+ip定位master node,是不靠谱的,如果master node重启或者数据出现了变化,那么slave node应该根据不同的run id区分,run id不同就做全量复制
  如果需要不更改run id重启redis,可以使用redis-cli debug reload命令

(4)psync

  从节点使用psync从master node进行复制,psync runid offset
  master node会根据自身的情况返回响应信息,可能是FULLRESYNC runid offset触发全量复制,可能是CONTINUE触发增量复制

全量复制

  master执行bgsave,在本地生成一份rdb快照文件
  master node将rdb快照文件发送给salve node,如果rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可以适当调节大这个参数
  对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s
  master node在生成rdb时,会将所有新的写命令缓存在内存中,在salve node保存了rdb之后,再将新的写命令复制给salve node
  client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
  slave node接收到rdb之后,清空自己的旧数据,然后重新加载rdb到自己的内存中,同时基于旧的数据版本对外提供服务
  如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF

增量复制

  如果全量复制过程中,master-slave网络连接断掉,那么salve重新连接master时,会触发增量复制
  master直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB
  msater就是根据slave发送的psync中的offset来从backlog中获取数据的

heartbeat

  主从节点互相都会发送heartbeat信息

  master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat

异步复制

  master每次接收到写命令之后,现在内部写入数据,然后异步发送给slave node

 

redis存储大数据

https://www.cnblogs.com/capacity-yang/p/13170319.html

按单机可以支撑10万并发来计算

100万并发4G数据,问题的核心是如何提高redis的并发,这就需要建立主从架构,实现读写分离,主节点负责写,并将数据同步给其他从节点,从节点负责读,从而实现高并发读取;

10万并发400G数据,问题的核心是如何提高redis的存储能力,按单机可以支撑10万并发,但是内存只有10g,这就需要40台机器来提供服务。先把400G的数据按照功能、项目、地域等拆分,每台机器存储1/40的数据,请求时,先分析数据存储在哪台服务器上,再去访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值