1.   什么是redis?

       redis是基于内存,支持多种数据结构的存储系统,它可以作为数据库,缓存及中间件使用,支持的数据结构有字符串(strings),哈希(hashes),lists列表,sets集合,有序集合(sorted           sets)。

       还支持bitmaps,hyperloglogs和地理空间索引半径查询等功能。

      它内置了复制(replication),Lua脚本,lru驱动事件,事务及不同级别的的磁盘持久化功能,并通过哨兵模式及集群保证了缓存的高可用性。

2.   为什么redis单线程模型效率很高?

     C语言实现,纯内存操作,基于非阻塞的io复用模型机制,单线程避免了多线程的上下文操作,丰富的数据结构,全程采用hash结构。

3.   redis的线程模型

      redis内部使用文件事件处理器file envent handler,它是单线程的,采用非阻塞io多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器来处理。

     文件事件处理器的结构分为4个部分:多个socket,io多路复用程序,文件事件分派器,事件处理器(连接应答处理器,命令请求处理器,命令回复处理器)

    多个socket可能会并发产生不同的操作,每个操作对应不同的文件事件,但io多复用程序会监听多个socket,会将socket产生的事件放到队列中排队,事件分派器每次会从队列中取出一个事件交给对应的事件处理器处理

 

客户端与redis一次通信的过程如图:

redis XsTREAM 查看最后100条数据 redis查看内容_Redis

 

大致说下上图流程:

   ①客户端socket1向redis的server socket请求建立连接,此时server socket会产生一个AE_READABLE事件,io多路复用程序监听到

server scoket产生的事件后,将事件加入到队列中。文件事件分派器从队列中获取到该事件后,交给连接应答处理器后,连接应答处理器会创建一个与客户端通信的socket01,并将socket01产生的AE_READABLE事件与命令请求处理器关联。

  ②假设此时客户端发送了一个set key-value的请求,此时redis中的socket01会产生一个AE_READABLE事件,io多路复用程序将该事件加入队列,文件事件分派器获取到该事件时,由于前面socket01已经和命令请求处理器关联,因此文件事件分派器将该事件交命令请求处理器处理,命令请求处理器读取socket01中的set key-value后,在自己的内存中完成set key-value设置,操作完成后它会将socket01的AE_WRITABLE事件跟命令回复处理器关联。

 ③如果客户端已做好接受结果准备后,redis中的socket01会产生一个AE_WRITABLE事件,并加入到对列中,文件事件分派器会站到相关联的命令回复处理器,由它对socket01输入本次操作的结果,比如ok,之后解除socket01的AE_WRITABLE事件与命令回复处理器的关联。

这就是一次完整的通信。

4.   redis与memcache的区别

  ①Redis 和 Memcache 都是将数据存放在内存中,都是内存数据库。不过 Memcache 还可用于缓存

       其他东西,例如图片、视频等等。

  ②Memcache 仅支持key-value结构的数据类型,Redis不仅仅支持简单的key-value类型的数据,

      同时还提供list,set,hash等数据结构的存储。

  ③虚拟内存– Redis 当物理内存用完时,可以将一些很久没用到的value 交换到磁盘

  ④分布式–设定 Memcache 集群,利用 magent 做一主多从; Redis 可以做一主多从。都可以一主一从

  ⑤存储数据安全– Memcache 挂掉后,数据没了; Redis 可以定期保存到磁盘(持久化)

  ⑥Memcache 的单个value最大 1m , Redis 的单个value最大 512m 。

  ⑦灾难恢复– Memcache 挂掉后,数据不可恢复; Redis 数据丢失后可以通过 aof 恢复

  ⑧Redis 原生就支持集群模式, Redis3.0 版本中,官方便能支持Cluster模式了, Memcached 没有原生的集群模式,需要依赖客户端来实现,然后往集群中分片写入数据。

  ⑨Memcached 网络IO模型是多线程,非阻塞IO复用的网络模型,原型上接近于 nignx 。而 Redis使用单线程的IO复用模型,自己封装了一个简单的 AeEvent 事件处理框架,主要实现类

      epoll,kqueue 和 select ,更接近于Apache早期的模式。 

 

5,为什么 Redis 需要把所有数据放到内存中?

     Redis 将数据放在内存中有一个好处,那就是可以实现最快的对数据读取,如果数据存储在硬盘中,磁盘 I/O 会严重影响 Redis 的性能。而且 Redis 还提供了数据持久化功能,不用担心服务        器  重启对内存中数据的影响。其次现在硬件越来越便宜的情况下,Redis 的使用也被应用得越来越多, 使得它拥有很大的优势。

6,Redis 的同步机制了解是什么?

     Redis 支持主从同步、从从同步。如果是第一次进行主从同步,主节点需要使用 bgsave 命令,再将后续修改操作记录到内存的缓冲区,等 RDB 文件全部同步到复制节点,复制节点接受完成         后将RDB 镜像记载到内存中。等加载完成后,复制节点通知主节点将复制期间修改的操作记录同步到复制节点,即可完成同步过程。 

7、说说Redis持久化机制

    Redis是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。 实现: 单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时文件中,持久化的过程结束了,再用这个临时文件替换上次的快照文件,然后子进程退出,内存释放。

   ①RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的      save参数来定义快照的周期。(快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)

  ②AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。

    当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。

 

9.  说一下 Redis 有什么优点和缺点

优点:

①速度快:因为数据存在内存中,类似于 HashMap , HashMap 的优势就是查找和操作的时间复杂度都是O (1) 。

②支持丰富的数据结构:支持 String ,List,Set,Sorted Set,Hash 五种基础的数据结构。

③持久化存储:Redis 提供 RDB 和 AOF 两种数据的持久化存储方案,解决内存数据库最担心的万一 Redis 挂掉,数据会消失掉

④高可用:内置 Redis Sentinel ,提供高可用方案,实现主从故障自动转移。 内置 RedisCluster ,提供集群方案,实现基于槽的分片方案,从而支持更大的 Redis 规模。

⑤丰富的特性:Key过期、计数、分布式锁、消息队列等。

缺点

①由于 Redis 是内存数据库,所以,单台机器,存储的数据量,跟机器本身的内存大小。虽然Redis 本身有 Key 过期策略,但是还是需要提前预估和节约内存。如果内存增长过快,需要定

期删除数据。

②如果进行完整重同步,由于需要生成 RDB 文件,并进行传输,会占用主机的 CPU ,并会消耗现网的带宽。不过 Redis 2.8 版本,已经有部分重同步的功能,但是还是有可能有完整重同步

的。比如,新上线的备机。

③修改配置文件,进行重启,将硬盘中的数据加载进内存,时间比较久。在这个过程中, Redis不能提供服务。

 

10.  Redis 缓存刷新策略有哪些? 

redis XsTREAM 查看最后100条数据 redis查看内容_数据_02

 

 

11, Redis 持久化方式有哪些?以及有什么区别?

     Redis 提供两种持久化机制 RDB 和 AOF 机制:

(1)RDB 持久化方式是指用数据集快照的方式半持久化模式)记录 redis 数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。

优点:

①只有一个文件 dump.rdb ,方便持久化。

②容灾性好,一个文件可以保存到安全的磁盘。

③性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 Redis 的高性能)

④相对于数据集大时,比 AOF 的启动效率更高。

缺点:数据安全性低。 RDB 是间隔一段时间进行持久化,如果持久化之间 Redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

(2)AOF=Append-only file 持久化方式是指所有的命令行记录以 Redis 命令请求协议的格式完全持久化存储,保存为 AOF 文件。

优点:

①数据安全, AOF 持久化可以配置 appendfsync 属性,有 always,每进行一次命令操作就记录到 AOF 文件中一次。

②通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。

③ AOF 机制的 rewrite 模式。 AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的 flushall )

缺点:

① AOF 文件比 RDB 文件大,且恢复速度慢。

②数据集大的时候,比 RDB 启动效率低。 

 

 

12.  持久化有两种,那应该怎么选择呢?

①. 不要仅仅使用 RDB ,因为那样会导致你丢失很多数据。

②. 也不要仅仅使用 AOF ,因为那样有两个问题,第一,你通过 AOF 做冷备没有 RDB 做冷备的恢

复速度更快; 第二, RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备

份和恢复机制的 bug 。

③. Redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,

用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF

文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。

④. 如果同时使用 RDB 和 AOF 两种持久化机制,那么在 Redis 重启的时候,会使用 AOF 来重新

构建数据,因为 AOF 中的数据更加完整。

 

13,怎么使用 Redis 实现消息队列?

   一般使用 list 结构作为队列, rpush 生产消息, lpop 消费消息。当 lpop 没有消息的时候,要适当sleep 一会再重试。

    能不能生产一次消费多次呢?使用 pub / sub 主题订阅者模式,可以实现 1:N的消息队列。

    pub / sub 有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如 rabbitmq 等。

14,Redis 如何实现延时队列?

   使用sortedset ,拿时间戳作为 score ,消息内容作为 key 调用 zadd 来生产消息,

   消费者用zrangebyscore 指令获取 N 秒之前的数据轮询进行处理。

 

15,说说你对Redis事务的理解

 什么是 Redis 事务?原理是什么?

    Redis 中的事务是一组命令的集合,是 Redis 的最小执行单位。它可以保证一次执行多个命令,每个事务是一个单独的隔离操作,事务中的所有命令都会序列化、按顺序地执行。服务端在执行      事务的过程中,不会被其他客户端发送来的命令请求打断。

     它的原理是先将属于一个事务的命令发送给 Redis,然后依次执行这些命令。

Redis 事务的注意点有哪些?

    需要注意的点有:

     ①Redis 事务是不支持回滚的,不像 MySQL 的事务一样,要么都执行要么都不执行;

     ②Redis 服务端在执行事务的过程中,不会被其他客户端发送来的命令请求打断。直到事务命令全部执行完毕才会执行其他客户端的命令。

Redis 事务为什么不支持回滚?

     Redis 的事务不支持回滚,但是执行的命令有语法错误,Redis 会执行失败,这些问题可以从程序层面捕获并解决。但是如果出现其他问题,则依然会继续执行余下的命令。这样做的原因是因       为回滚需要增加很多工作,而不支持回滚则可以保持简单、快速的特性。

16,Redis 为什么设计成单线程的?

    多线程处理会涉及到锁,并且多线程处理会涉及到线程切···换而消耗 CPU。采用单线程,避免了不必要的上下文切换和竞争条件。其次 CPU 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器      内存或者网络带宽。 

 

17,熟悉哪些 Redis 集群模式?

①. Redis Sentinel:体量较小时,选择 Redis Sentinel ,单主 Redis 足以支撑业务。

②. Redis Cluster:Redis 官方提供的集群化方案,体量较大时,选择 Redis Cluster ,通过分片,使用更多内存。

③. Twemprox:Twemprox 是 Twtter 开源的一个 Redis 和 Memcached 代理服务器,主要用于管理 Redis 和Memcached 集群,减少与Cache 服务器直接连接的数量。

④. Codis:Codis 是一个代理中间件,当客户端向 Codis 发送指令时, Codis 负责将指令转发到后面的Redis 来执行,并将结果返回给客户端。一个 Codis 实例可以连接多个 Redis 实例,也可以        启动多个 Codis 实例来支撑,每个 Codis 节点都是对等的,这样可以增加整体的 QPS 需求,还能起到容灾功能。

⑤. 客户端分片:在 Redis Cluster 还没出现之前使用较多,现在基本很少热你使用了,在业务代码层实现,起几个毫无关联的 Redis 实例,在代码层,对 Key 进行 hash 计算,然后去对应Redis        实例操作数据。这种方式对 hash 层代码要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。

 

18,是否使用过 Redis Cluster 集群,集群的原理是什么?

    使用过 Redis 集群,它的原理是:

    ①所有的节点相互连接

    ②集群消息通信通过集群总线通信,集群总线端口大小为客户端服务端口 + 10000(固定值)

    ③节点与节点之间通过二进制协议进行通信

    ④客户端和集群节点之间通信和通常一样,通过文本协议进行

    ⑤集群节点不会代理查询数据按照 Slot 存储分布在多个 Redis 实例上

    ⑥集群节点挂掉会自动故障转移

    ⑦可以相对平滑扩/缩容节点

    Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0~16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。

 

19, Redis Cluster 集群方案什么情况下会导致整个集群不可用?Redis 没有使用哈希一致性算法,而是使用哈希槽。Redis 中的哈希槽一共有 16384 (2^14)个,计算给定密钥的哈希槽,我们只需要对密钥的 CRC16 取摸 16384。假设集群中有 A、B、C 三个集群节点,不存在复制模式下,每个集群的节点包含的哈希槽如下:

①节点 A 包含从 0 到 5500 的哈希槽;

②节点 B 包含从 5501 到 11000 的哈希槽;

③节点 C 包含从 11001 到 16383 的哈希槽;

这时,如果节点 B 出现故障,整个集群就会出现缺少 5501 到 11000 的哈希槽范围而不可用。

 

20,Redis 常见性能问题和解决方案有哪些?

Redis 常见性能问题和解决方案如下:

①Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件;

②如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次;

③为了主从复制的速度和连接的稳定性,Master 和 Slave 最好在同一个局域网内;

④尽量避免在压力很大的主库上增加从库;

⑤主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <-Slave3….;这样的结构方便解决单点故障问题,实现 Slave 对 Master 的替换。如果 Master 挂了,可      以立刻启用 Slave1 做 Master,其他不变。

 

21,什么情况下可能会导致 Redis 阻塞?

Redis 产生阻塞的原因主要有内部和外部两个原因导致:

(1)内部原因:

       ①如果 Redis 主机的 CPU 负载过高,也会导致系统崩溃;

       ②数据持久化占用资源过多;

       ③对 Redis 的 API 或指令使用不合理,导致 Redis 出现问题。

(2)外部原因

       外部原因主要是服务器的原因,例如服务器的 CPU 线程在切换过程中竞争过大,内存出现问题、网络问题等。 

 

22,怎么提高缓存命中率?

     主要常用的手段有:

       ①提前加载数据到缓存中;

       ②增加缓存的存储空间,提高缓存的数据;

       ③调整缓存的存储数据类型;

       ④提升缓存的更新频率。

23,Redis 如何解决 key 冲突?

       Redis 如果 key 相同,后一个 key 会覆盖前一个 key。如果要解决 key 冲突,最好给 key 取好名区分开,可以按业务名和参数区分开取名,避免重复 key 导致的冲突。

24,Redis 报内存不足怎么处理?

      Redis 内存不足可以这样处理:

      ①修改配置文件 redis.conf 的 maxmemory 参数,增加 Redis 可用内存;

      ②设置缓存淘汰策略,提高内存的使用效率;

      ③使用 Redis 集群模式,提高存储量。

 

25.  redis的数据类型,以及每种数据类型的使用场景

    回答:一共五种

    (一)String :这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。

    (二)hash: 这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以  cookieId作为key,设置30分钟为缓存过 期时间,能很好的模拟出类似session的效果。

    (三)list :使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。本人还用一个场景,很合适—取行           情信息。就也是个生产者和消费者的场景。LIST可以很好的完成排队,先进先出的原则。

    (四)set: 因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道             为了一个做一个全局去重,再起一个公共服务,太麻烦了。 另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

    (五)sorted set :sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。

26、redis的过期策略以及内存淘汰机制

      redis采用的是定期删除+惰性删除策略。

       为什么不用定时删除策略? 定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删   除key,因此没有采用这一策略. 定期删除+惰性删除是如何工作的呢? 定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的 key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。 于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。 采用定期删除+惰性删除就没其他问题了么?不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。 在redis.conf中有一行配置

      maxmemory-policy volatile-lru

该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)

①volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

②volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

③volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

④allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

⑤allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

⑥no-enviction(驱逐):禁止驱逐数据,新写入操作会报错

ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile

random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

 

27.  如何解决redis的并发竞争key问题

    同时有多个子系统去set一个key。这个时候要注意什么呢?

     不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。

(1)如果对这个key操作,不要求顺序:准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可

(2)如果对这个key操作,要求顺序:分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做

     set操作了。以此类推。

(3) 利用队列,将set方法变成串行访问也可以redis遇到高并发,如果保证读写key的一致性 对redis的操作都是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发        的问题了。 

 


28、了解Redis的事务吗?

  Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的 Redis会将一个事务中的所有命令序列化,然后按顺序执行。

1.redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”,所以 Redis 的内部可以保持简单且快速。

2.如果在一个事务中的命令出现错误,那么所有的命令都不会执行;

3.如果在一个事务中出现运行错误,那么正确的命令会被执行。

1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。

2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。

3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务,并且客户端会从事务状态中退出。

4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。