1、简述Redis中RDB和AOF持久化机制

RDB :redis DataBase,在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
优点:
1.整个redis数据库只包含一个dump.rdb,方便持久化
2.容灾性好,方便备份
3.性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化,使用单独子进程来持久化,主进程不会进行任何IO操作,也保证了redis的高性能
4.相比于数据集大时,比AOF的启动效率更高
缺点:
1.数据安全性比较低,RDB是间隔一段时间进行持久化,如果持久化期间redis发生故障,会发生数据丢失
2.由于RDB是通过fork一个子进程来协助完成数据持久化的工作,因此,当数据集较大时,会长时间占据CPU,可能会导致整个服务器停止几百毫秒,甚至是1秒钟。
AOF:Append Only File,只追加文件,以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式进行记录,可以打开文件查看详细的操作记录
优点:
1.数据安全,redis提供了3种同步策略,即每秒同步、每修改同步和不同步:每秒同步也就是异步完成,其效率也是非常高的,所差的就是一旦系统出现宕机现象,那么这一秒钟之内修改的数据也将丢失。而每修改同步是我们每次发生的数据变化都会被立即记录到磁盘中,效率比每秒同步低,但更安全。
2.通过append模式写文件,即使中途服务器宕机也不会破坏已存在的内容
3.AOF机制的rewrite模式,定期会对AOF文件进行重写,以达到压缩的目的。
缺点:
1.AOF机制文件比RDB文件大,恢复速度慢
2.数据集大的时候,比EDB启动效率低
3.运行效率没有RDB高

总结:AOF文件比RDB更新频率高,安全性更好,但性能不如RDB,如果两种机制都配置的情况下优先加载AOF。

2、什么是 Redis?简述它的优缺点?

Redis 的全称是:Remote Dictionary.Server,本质上是一个 Key-Value 类型的内存数据库,很像
memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据 flush 到硬盘上进行保存。
因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作,是已知性能最快的Key-Value DB。
Redis 的出色之处不仅仅是性能,Redis 最大的魅力是支持保存多种数据结构,此外单个 value 的最大限制是 1GB,不像 memcached 只能保存 1MB 的数据,因此 Redis 可以用来实现很多有用的功能。比方说用他的 List 来做 FIFO 双向链表,实现一个轻量级的高性 能消息队列服务,用他的 Set 可以做高性能的 tag 系统等等。
另外 Redis 也可以对存入的 Key-Value 设置 expire 时间,因此也可以被当作一 个功能加强版memcached 来用。 Redis 的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。

3、Redis支持哪几种数据类型?

String、List、Set、Sorted Set、hashes

4、Redis的过期键的删除策略?

redis是存储key-value键值对的数据库,我们可以设置redis中缓存的key的过期时间。redis的过期策略就说指当redis中缓存的key过期了,该如何处理:redis同时采用惰性过期和定期过期策略
1.惰性过期:只有当访问一个key时,我们才会判断该key是否已经过期,过期则对其清除。该策略可以最大化节省CPU资源,却对内存不太友好。(可能会出现大量过期key没有被访问从而不会被清除,占用大量的内存)
2.定期过期:每隔一段时间,系统都会扫描一定数量的数据库的expires字典中的一定数量的key键值,并清除其中已经过期的key值,该策略是前两者的一个折中方案,通过调整定时扫描的时间见和每次扫描的限定耗时,鸡儿意在不同情况下使得cpu和内存资源达到最优的平衡效果。

5、Redis线程模型,单线程为什么快?

Redis线程模型:
redis基于reactor模式开发了网络事件处理器,这个处理器叫做文件事件处理器,file event handler,因为这个事件处理器是单线程的,所以redis才叫做单线程的模型。redis采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器来处理这个事件。
如果被监听的socket准备好执行accept、read、write、close等操作时,跟操作对应的文件事件就会产生,这个时候文件事件处理器就会调用之前关联好的事件处理器来处理对应事件。
文件事件处理器是单线程模式运行的,但是通过IO多路复用机制监听多个socket,实现高性能的网络通信模型,又可以跟内部其他单线程的模块进行对接,保证了redis内部的线程模型的简单性。
文件事件处理器的结构包含4个部分

  1. - 多个socket
  2. - IO多路复用程序
  3. - 文件事件分派器
  4. - 事件处理器(命令请求处理器、命令回复处理器、连接应答处理器,等等)

多个socket可能并发的产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,但是会将socket放入一个队列中排队,每次从队列中取出一个socket给事件分派器,事件分派器把socket给对应的事件处理器。
然后一个socket的事件处理完之后,IO多路复用程序才会将队列中的下一个socket给事件分派器。文件事件分派器会根据每个socket当前产生的事件,来选择对应的事件处理器来处理。

总结:redis基于文件事件处理器采用IO多路复用程序监听多个socket,将其放入到队列中,每一个取出一个socket,事件分派器将其给对应的事件处理器进行处理。
文件事件处理器和socket的通信:
如果是客户端要连接redis,那么会为socket关联连接应答处理器 如果是客户端要写数据到redis,那么会为socket关联命令请求处理器 如果是客户端要从redis读数据,那么会为socket关联命令回复处理器。
客户端与redis的一次通信:
step1:在redis启动初始化的时候,redis会将连接应答处理器跟AE_READABLE事件关联起来,接着如果一个客户端跟redis发起连接,此时会产生一个AE_READABLE事件,然后由连接应答处理器来处理跟客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件跟命令请求处理器关联起来。
step2:当客户端向redis发起请求的时候(不管是读请求还是写请求,都一样),首先就会在socket产生一个AE_READABLE事件,然后由对应的命令请求处理器来处理。这个命令请求处理器就会从socket中读取请求相关数据,然后进行执行和处理。
step3:接着redis这边准备好了给客户端的响应数据之后,就会将socket的AE_WRITABLE事件跟命令回复处理器关联起来,当客户端这边准备好读取响应数据时,就会在socket上产生一个AE_WRITABLE事件,会由对应的命令回复处理器来处理,就是将准备好的响应数据写入socket,供客户端来读取。
step4:命令回复处理器写完之后,就会删除这个socket的AE_WRITABLE事件和命令回复处理器的关联关系。
单线程的原因,单线程为什么快:
1、redis采用纯内存操作
2、核心是基于非阻塞的IO多路复用机制
3、单线程反而避免了多线程的频繁上下文切换带来的性能问题。

6、什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?什么是缓存击穿?何如避免?

缓存雪崩:
当缓存服务器重启或者大量缓存集中在某一个时间段失效,后面的请求都会落到数据库中。
解决方案:
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1 为原始缓存,A2 为拷贝缓存,A1 失效时,可以访问 A2,A1 缓存失效时间设置为短期,A2 设置为长期
3:不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀

缓存穿透:
缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库中,造成数据库短时间承受大量请求。
解决方案:
1、接口层增加校验,如用户鉴权校验,id做基础校验,不满足要求的直接拦截
2、对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该 bitmap 过滤。一定不存在的数据会被这个bitmap拦截掉,避免对底层存储系统的查询压力。

缓存击穿:
缓存击穿是指缓存在没有但是数据库中有的数据(一般是指缓存时间到期),这时候由于并发用户特别多,同时去读取数据库的数据,造成数据库的大压力。
解决方案:
1、设置缓存中热点数据永不过期
2、加互斥锁

8、怎么理解 Redis 事务?

redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行,事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。redis事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Redis通过MULTIEXECWATCHDISCARD等命令来实现事务功能。主要有以下三个阶段:
1、事务开始
MULTI命令的执行,标识着一个事务的开始。MULTI命令会将客户端状态的flags属性中打开REDIS_MULTI标识来完成的。
2、命令入队
当一个客户端切换到事务状态之后,服务器会根据这个客户端发送来的命令来执行不同的操作。将命令放入一个事务队列里面,然后向客户端返回QUEUED回复

     - 如果客户端发送的命令为MULTI、EXEC、WATCH、DISCARD中的一个,立即执行这个命令,否则放入到事务队列
     - 首先会检查此命令的格式是否正确,如果格式不正确,服务器会在客户端状态(redisClient)的flags属性关闭REDIS_MULTI标识,取消事务的执行。
     - 事务队列是按照FIFO的方式入队

3、事务执行
当一个处于事务状态的客户端向服务器发送EXEC命令时,服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行完的结果全部返回给客户端(每个命令对应一个返回)

     - 在事务执行过程中,如果客户端flag属性不包含REDIS_MULTI标识或者包含了REDIS_DIRTY_CAS或者REDIS_DIRTY_EXEC标识,那么直接取消事务的执行
     - 服务器会遍历客户端的事务队列,然后依次执行事务队列的所有命令,最后将返回结果统一返回给客户端

4、注意点:
redis不支持事务回滚机制,但是每次事务命令入队时都会检查该命令是否正确
redis事务不支持检查那些程序员自己逻辑错误,例如对String类型的数据库键执行对Hashmap类型的操作。

9、redis集群方案

redis主从复制:
通过执行slaver of命令进行redis主从赋值,让一个服务器去复制另一个服务器的数据。主数据库可以进行读写操作,当写操作导致数据变化时会自动把数据复制同步到从数据库,而从数据库一般是只读的,并接受主数据库同步过来的数据,一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
1)全量复制:
1、主节点通过bgsave命令fork子进程进行RDB持久化,该过程十分消耗CPU、内存
2、主节点同通过网络将RDB文件发送给从节点对主从节点的带宽都会带来很大的消耗
3、从节点清空老数据、载入新RDB文件的过程是阻塞的,无法响应客户端的命令,如果从节点执行bgrewriteraof,也会带来额外的消耗。
2)部分复制:
1、复制一个偏移量: 主从节点都会维护一个复制偏移量offset
2、复制挤压缓冲区:主节点内部维护了一个固定长度、先进先出的队列复位复制积压缓冲区,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。
3、服务器运行ID:每个redis节点,都有一个运行ID,在节点启动时自动生成,主节点会将自己的运行ID发送给从节点,从节点会保存主节点的运行id,一旦从节点Redis断开重连,根据运行ID来判断同步:主从节点的同步进度:
1、如果从节点保存的ID和主节点的ID相同,主从节点将继续使用部分复制
2、如果从节点保存的ID和主节点的ID不相同,那么主从节点只能进行全量复制。
image.png

redis哨兵模式:
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
image.png
哨兵的作用主要是

  - 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  - 当哨兵监测到master宕机,会自动将slave切换成master,然后通过**发布订阅模式**通知其他的从服务器,修改配置文件,让它们切换主机。

然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。

redis cluster:
redis cluster是一种服务端sharding技术,采用slot(槽)的概念,一共分为16384个槽,将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行,架构图如下图所述:
image.png
1、通过哈希的方式,根据key的哈希将数据分片,每个节点均分存储一定的哈希槽区间的数据
2、每份数据分片会存储在多个互为主从的多节点上
3、数据写入先写主节点,在同步到从节点
4、同一分片多个节点间的数据不保持强一致性
5、读取数据时,当客户端操作的key没有分配在该节点上,redis会返回转向指令,指向正确的节点。
6、扩容时需要把旧节点的数据迁移一部分到新节点
在redis cluster架构中,每个redis要放开两个端口号,比如一个是6379,另外一个就说加1W的端口号,16379端口是用来进行节点间通信的,也就是cluster bus的通信,主要是负责故障检测、配置更新、故障转移授权。cluster bus用了另外一种二进制的协议(gossip协议),用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。
优点:
1、无中心架构,支持动态扩容,对业务透明
2、具备sentinel的监控和自动Failover(故障转移)能力
3、客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
4、高性能、客户端直连redis服务,避免了proxy代理的损耗

10、为什么Redis要把数据都放在内存中?

Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。
所以 redis 具有快速和数据持久化的特征,如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。
在内存越来越便宜的今天,redis 将会越来越受欢迎, 如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

11、redis适用于哪些场景?

1)会话缓存(Session Cache)
最常用的一种使用 Redis 的情景是会话缓存(sessioncache),用 Redis 缓存会话比其他存储(如Memcached)的优势在于:Redis 提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?
幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用 Redis 来缓存会话的文档。甚至广为人知的商业平台 Magento 也提供 Redis 的插件。
(2)全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实 例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地 FPC。
再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。
此外,对WordPress的用户来说,Pantheon有一个非常好的插件wp-redis,这个插件能帮助你以最快 速度加载你曾浏览过的页面。
(3)排行榜/计数器
Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(SortedSet)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。
所以,我们要从排序集合中获取到排名最靠前的 10 个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORESAgora Games 就是一个很好的例子,用 Ruby 实现的,它的排行榜就是使用 Redis 来存储数据的,你可以在这里看到。
(4)队列
Reids在内存存储引擎领域的一大优点是提供list和set操作,这使得Redis能作为一个很好的消息队列 平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。

12、redis如何对内存进行优化?

image.png
以电商网站为例,当用户的一些查询请求,比如要查询一个商品的信息,会通过前端页面会经过缓存层,如果这些信息在缓存层就已经存在了,则直接返回给客户端,如果没有则去数据库集群查询,并经过缓存层缓存然后返回给客户端。
这样设计的好处是可以大大减小数据库集群的压力。我们都知道类似 MySQL 这种关系型数据库最大的瓶颈就是当出现像双十一这样的高并发请求时候会给 IO 带来巨大的压力,导致 IO 出现瓶颈。哪怕你的数据库优化做的再好,也不能改变这个基本事实。那么在数据库的上面加一道缓存层就可以大大缓解后端数据库的压力,通过类似 Redis 这样的内存型数据库将热点数据存储在内存中,可以大大提高读写效率和请求时延。

redis内存优化:
1)系统层面:

     - Linux操作系统对大部分申请内存的请求都回复 yes,以便能运行更多的程序。设置内存分配策略**.vm.overcommit_memory**
     - 当物理内存不足时,可以将一部分内存页进行swap操作,但是swap 空间是由硬盘提供的,对于需要高并发、高吞吐的应用来说,磁盘IO通常会成为系统瓶颈。
     - OOM killer会在可用内存不足时选择性地杀掉用户进程,OOM killer进程会为每个用户进程设置一个权值,这个权值越高,被 Kill 的概率就越高,反之概率越低。

2)redis自身优化:

     - 设置内存上限,使用maxmemory参数限制最大可用内存
     - 配置内存回收策略
     - 键值对优化:
     - 控制键的数量:

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。
比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面。

     - 共享对象池:共享对象池是指Redis内部维护 [0-9999]的整数对象池,用于节约内存。

13、redis和memcached的区别?

1)性能方面:
 由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据时,Memcached性能要高于Redis,虽然Redis最近也在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
2)内存使用效率:
memcached使用key-value形式存储和访问数据,在内存中维护一张巨大的HashTable,使得对数据查询的时间复杂度降低到O(1),保证了对数据的高性能访问。
redis与memcached相比,比仅支持简单的key-value数据类型,同时还提供list,set,zset,hash等数据结构的存储;
对于简单的key-value数据类型,memcached缓存的内存利用率更高,而如果Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于Memcached。
3)数据存储:
 Redis和Memcached都是将数据存放在内存中,都是内存数据库。不过memcached还可用于缓存其他东西,例如图片、视频等等。memcached把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;redis有部份存在硬盘上,这样能保证数据的持久性,支持数据的持久化(RDB、AOF),而Memcached不支持持久化。
4)内存管理机制
Memcached默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。
Redis通过定义一个数组来记录所有的内存分配情况,这个数组的长度为ZMALLOC_MAX_ALLOC_STAT。数组的每一个元素代表当前程序所分配的内存块的个数,且内存块的大小为该元素的下标。Redis采用的是包装的mallc/free,相较于Memcached的内存管理方法来说,要简单很多。