目录
Redis概述
Redis的优缺点?
Redis的数据类型有哪些?
Redis的数据结构有那些?
Redis的应用场景?
Redis是单线程的,如何提高CPU的利用率?
过期键的删除策略
键的过期删除策略
redis的内存淘汰机制是怎样的?
Redis的持久化
什么是redis的持久化?
redis常见的持久化机制有哪些?有什么优缺点?
Redis的事务
什么是redis的事务?
Redis事务相关命令
Redis事务执行的三个阶段
Redis事务的特性
Redis事务为什么不支持回滚?
Redis的集群、主从、哨兵
redis集群的实现方案有哪些?
Redis主从模式
Redis哨兵模式
Redis自研
Redis Cluster
Redis主从架构中数据会丢失吗?
Redis的缓存问题
缓存雪崩
缓存击穿
缓存穿透
Redis概述
Redis的优缺点?
优点:
读写性能好支持数据持久化,有AOF和RDB两种持久化方式数据结构丰富,支持String,List,Set,Hash,Zset支持事务,redis的所有操作都是原子性的支持主从复制,主机可以自动将数据同步到从机,实现读写分离
缺点:
因为redis是将数据存到内存中,所以会受到内存大小的限制,不能用作海量数据的读写redis不具备自动容错和恢复功能,主机或从机宕机会导致前端部分读写请求失败,需要重启机器或者手动切换前端的IP才能切换
Redis是单线程还是多线程?Redis为什么这么快?
Redis6.0之前是单线程,为什么这么快有以下几个原因:
运行在内存当中数据结构简单使用多路IO复用技术单线程实现,避免上下文切换,锁等造成的性能开销
Redis的数据类型有哪些?
常见的数据类型,String,Hash,Set,List,Zset
Redis的数据结构有那些?
Redis的数据结构有简单的动态字符串,链表,字典,跳表,整数集合,压缩列表等
简单动态字符串:Redis是用C写的,但是没有直接使用C语言的传统的字符串表示,而是创建了一种名为简单动态字符串的抽象类型链表:链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过删除节点来灵活的调整链表的长度,链表是列表的底层实现之一字典:又称符号表,关联数组或者映射,是一种用于保存键值对的抽象数据结构,redis的数据库就是使用字典来作为底层实现的,实现对数据库的增删改查操作也是构建在对字典的操作之上的整数集合:整数集合(intset)是集合键的底层实现之一,当一个集合质保函整数值元素,并且这个集合的元素数量不多时,redis就会使用整数集合作为集合键的底层实现压缩列表:压缩列表是redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序数据结构
以上的数据结构redis并没有直接使用来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含了字符串对象,列表对象,哈希对象,集合对象以及有序集合对象,每种对象都用到了至少一种我们前面说的数据结构。
为什么不直接使用底层数据结构,而是创建对象系统,对象系统有以下优点:
通过五种不同类型的对象,redis可以在执行命令前,根据对象的类型来判断一个对象在不同场景下的使用效率可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候。内存将会被释放
对象这个部分写了比较多,但是面试问的不太多,总之有备无患吧
跳表:
跳表是一种有序的数据结构,他通过每个节点中维持多个指向其他节点的指针。从而达到快速访问节点的目的,跳表支持平均时间复杂度O(logN),最坏O(N)的节点查询,还可以通过顺序性操作来批量处理节点,跳表有序集合键的底层实现之一。redis只在两个地方用到了跳表,一个是实现有序集合键,另一个是集群节点中用作内部数据结构
跳表本质上采用的是一种空间换时间的策略,是一种可以进行二分查找的有序链表,跳表在原有的有序链表上增加了多级索引,通过索引来实现快速查询,跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能
对于理想的跳表,每向上一层索引节点数量都是下一层的1/2,跳表的时间复杂度O(logn),虽然是空间汉时间的策略,这里说的只是数字,如果是存储比较大的对象,浪费的空间就不值一提,因为索引节点只需要存储关键值和几个指针,并不需要存储对象
跳表相比红黑树的优点(redis为什么用跳表不用红黑树):
内存占用少,自定义参数决定使用多少内存查询性能至少不比红黑树差简单更容易实现和维护
redis中的跳跃表和普通的跳跃表的区别:
redis中的跳跃表分数(score)允许重复,即跳跃表的key允许重复,如果分数重复,还需要根据数据内容来进行字典排序,普通的跳跃表是不支持的第一层链表不是一个单向链表,而是一个双向链表,这是为了方便以倒叙方式获取一个范围内的元素redis的跳跃表可以方便的计算出每个元素的排名
Redis的应用场景?
缓存:redis基于内存,读写速度非常快,并且有键过期清除功能和键淘汰策略,可以用作缓存使用排行榜:redis提供的有序集合可以很方便的实现排行榜分布式锁:Redis的setnx功能来实现分布式锁社交功能:实现共同好友,共同关注等计数器:通过String进行自增自减实现计数功能消息队列:redis提供了发布,订阅,阻塞队列等功能,可以实现一个简单的消息队列
Redis是单线程的,如何提高CPU的利用率?
可以在一个服务器上部署多个redis实例,把他们当做不同的服务器使用
过期键的删除策略
键的过期删除策略
常见的过期策略是惰性策略,定期策略,定时策略
惰性删除:只有访问这个剪的时候才会检查是否过期,如果过期则清除。优点:最大化的节约CPU资源。缺点:如果大量过期键没有被访问,会一直占用大量内存定时删除:为每个设置过期的时间key都创造一个定时器,到了时间就会清除。优点:该策略可以立即清除过期的键。缺点:会占用大量的CPU资源进行数据过期处理定期删除:每隔一段时间就对一些键进行检查,删除其中过期的键,该车略是惰性删除和定时删除的一个折中,既避免了占用大量CPU资源又避免了出现大量过期键不被清除占用内存的情况
redis中同时使用了惰性删除和定期删除两种
redis的内存淘汰机制是怎样的?
redis4.0前一共有六种淘汰策略
volatile-lru:当redis内存不足时,会在设置了过期时间的键中使用LRU算法移除那些最少使用的键volatile-ttl:从设置了过期时间的键中移除将要过期的volatile-random:从设置了过期时间的键中随机淘汰一些allkeys-lru:当内存空间不足时,根据LRU算法移除一些键allkeys-random:当内存空间不足时,随机移除某些键noeviction:当内存空间不足时,新的写入操作会报错
前三个是在设置了过期时间的键的空间进行移除,后三个是在全局的空间进行移除
在redis4.0以后增加了两个:
volatile-lfu:从设置过期时间的键中移除一些最不经常使用的键(LFU算法)allkeys-lfu:当内存不足时,从所有的键中移除一些最不经常使用的键
这两个也是一个是在设置了过期时间的键的空间,一个是在全局空间
Redis的持久化
什么是redis的持久化?
因为redis是基于内存的,为了让防止一些意外情况导致数据丢失,需要将数据持久化到磁盘上
redis常见的持久化机制有哪些?有什么优缺点?
redis提供了两种不同的持久化方式,一种是RDB,一种是AOF
RDB是默认的持久化方式,按照一定的时间间隔将内存的数据以快照的形式保存到硬盘,恢复的时候将快照读到内存中,RDB持久化实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,在替换之前的文件,用二进制压缩存储,如下图
优点:
适合对大规模的数据恢复,比AOF启动效率高只有一个文件dump.rdb,方便持久化性能最大化,在最开始持久化时,它唯一需要做的就是只是fork出子进程,之后再由子进程完成这些持久化的工程,这样就可以极大地避免服务进程执行IO操作
缺点:
数据安全性低,在一定时间间隔内做一次备份,如果redis突然宕机,会丢失最后一次快照的修改由于RDB是通过fork子进程来协助完成数据持久化工作的,因此当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至一秒钟
AOF
AOF的持久化以日志的形式记录服务器处理的每一个写,删除操作,查询操作不会记录,以文本形式记录,可以打开文件看到详细的操作记录
优点:
具备更高的安全性,redis提供了三种同步策略,分别是每秒同步,每修改同步和不同步,相比RDB突然宕机丢失的数据会更少,每秒同步会丢失一秒钟的数据,每修改同步不会丢失数据由于该机制对日志文件的写入操作采用的是append模式,因此在写入的过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容AOF包含一个格式清晰,易于理解的日志文件用于记录所有的修改操作,可以通过该文件完成数据的重建
缺点:
对于相同数量的数据集而言,AOF文件通常要大于RDB文件,RDB在恢复大数据集时的速度比AOF的恢复速度要快根据AOF选择同步策略的不同,效率也不同,但AOF在运行效率上往往会慢于RDB
Redis的事务
什么是redis的事务?
redis的事务是一个单独的隔离操作,事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中不会被其他客户端发送的命令请求所打断,所以redis的事务是在一个队列中,一次性、顺序性、排他性地执行一系列命令
redis事务的主要作用就是串联多个命令防止别的命令插队
Redis事务相关命令
DISCARD:命令取消事务,放弃执行事务队列的所有命令,恢复连接为非(transaction)模式,如果正在使用WATCH命令监视某个key,那么取消所有的监视等同于执行命令UNWATCHEXEC:执行事务队列内的所有命令MULTI:用于标记一个事务块的开始UNWATCH:用于取消watch命令对所有key的监控WATCH:用于标记要监视的key,以便有条件的执行事务,WATCH命令可以监控一个或者多个键,一旦其中有一个键被修改(或者删除),之后的事务就不会执行
Redis事务执行的三个阶段
开始事务(MULTI)命令入列执行事务(EXEC)
Redis事务的特性
redis事务不保证原子性,单条的redis命令是原子性的,但事务不能保证原子性redis事务是有隔离性的,但没有隔离级别,十五中的所有命令都会序列化、按顺序的执行。事务在执行的过程中,不会被其他的客户端发送的命令请求打断(顺序性,排他性)redis事务不支持回滚,redis执行的过程中的命令执行失败,其他命令仍然可以执行(一次性)
Redis事务为什么不支持回滚?
redis命令只有两种情况失败:1.语法错误的时候才失败(输入命令的时候不检查语法)2.要执行的key数据类型不匹配:这种错误实际上是编程错误,这应该是开发阶段被测试出来的而不是生产上因为不需要回滚,所以redis内部实现简单并高效(在redis为什么为什么是单线程而不是多线程也用了这个思想,实现简单并且高效)
Redis的集群、主从、哨兵
redis集群的实现方案有哪些?
说redis集群前有必要说下为什么要使用redis集群,redis单机版主要有以下几个缺点:
不能保证数据的可靠性,服务部署在一台服务器上,一旦服务器宕机服务就不可用性能瓶颈,内存有限,处理能力有限
说redis集群前有必要说下为什么要使用redis集群,redis单机版主要有以下几个缺点:
不能保证数据的可靠性,服务部署在一台服务器上,一旦服务器宕机服务就不可用性能瓶颈,内存有限,处理能力有限
redis集群方案:
redis主从模式redis哨兵模式redis自研redis cluster
现在我详细说下这三种方案
Redis主从模式
从定义上就很好理解,一个主数据库数据更新之后,自动将更新的数据同步在从库上,这里从库可以有很多个,这是为了解决单机版数据丢失问题,因为单机版的数据通过持久化到了硬盘上,并且读写都在同一服务器上(读写不分离),一旦硬盘出现问题,就会导致数据不可用,上图看的更清楚
正所谓人无完人,主从模式也有他的优缺点:
优点:
高可靠性,在master数据库出现故障后,可以切换到salve数据库读写分离,salve库可以扩展master库节点的读能力,有效应对大并发量的读操作
缺点:
不具备自动容错和恢复能力,主节点挂点之后,需要手动把从节点升为主节点,可用性低
Redis哨兵模式
开始进化了,正因为redis的主从模式不具备自动容错和恢复能力,所以redis2.6之后就有了哨兵模式,但是哨兵模式的主要核心还是主从复制,不过相比于原来的主从复制,多了一个竞选机制(也就是多了一个哨兵集群),从所有节点中竞选出主节点,上图
从图可以看出来也就只是多了一个哨兵集群,哨兵集群的主要作用如下:
监控所有服务器是否正常运行:通过发送命令返回监控服务器的运行状态,处理监控主服务器、从服务器外,哨兵之间也是相互监控故障切换:当哨兵检测到master宕机之后,会自动将salve切换成master,然后通过发布订阅模式通知其他从服务器修改配置文件,让他们切换master,同时旧的主机也会变为从机,就算这个旧的主机恢复之后也不会再变为主机了
哨兵模式的优缺点:
优点:
解决了主从模式的mater故障后不能自动切换主机的问题
缺点:
浪费资源,集群节点保存的都是数据,数据量过大时,主从同步性能会受到影响redis主机宕机后,竞选机制会有一段时间空白,此时redis会开启保护机制,禁止写操作,直到选取出新的master只有一个master库执行写请求,写的操作会受到单机性能瓶颈影响
Redis自研
客户端分片
客户端分片就是把分片的逻辑放在redis客户端实现,通过redis客户端预先定义好的路由规则(使用哈希算法),把对key的访问转发到不同的redis实例中,查询数据时把返回结果汇集,看下图
客户端分片的优缺点:
优点:redis实例彼此独立,相互无关联,每个redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性强
缺点:
客户端sharding不支持动态删除节点,服务端redis实例群拓扑结构有变化时,每个客户端都需要更新调整运维成本较高,集群的数据除了任何意外都需要运维人员和开发人员一起合作,减缓了解决问题的速度,增加了跨部门沟通的成本在不同的客户端程序中,维护相同的路由分片逻辑成本大
代理分片
为了解决上述客户端分片的最大问题(服务端redis实例群拓扑结构有变化时,每个客户端都需要调整),代理分片将客户端分片模块单独分了出来,作为redis客户端和服务端的桥梁,看下图
代理分片的优缺点:
优点:解决了服务端redis实例群拓扑结构有变化时,每个客户端都需要更新调整的问题
缺点:是由于redis客户端的每个请求都经过代理才能达到redis服务器,这个过程中会产生性能损失,常见的代理分片有Twitter开源的redis代理Twemproxy和豌豆荚自主研发的Codis
Redis Cluster
前面介绍为了解决哨兵模式的问题,各大企业提出了数据分片存储方案,在redis3.0中,redis也提供了响应的解决方案,就是redis cluster
redis cluster是一种服务端的Sharding技术,redis cluster并没有使用一致性hash,而是采用了slot(槽)的概念,一共分成16384个槽,将请求发送到任意节点,接收请求的节点会将查询请求发送到正确的节点上执行
一致性哈希:
其实就是用来保证节点负载均衡的,那么多主节点,到底要把数据存放在主节点上呢?就可以通过一致性哈希算法来解决
一致性哈希算法:
首先就是对key计算出hash值,然后对2^32取模,也就是值的范围在[0,3^32-1],一致性哈希将其范围抽象为一个环,使用crc16算法计算出来的哈希值回落到圆环上的某一个位置。
如图,假设三个redis实例A,B,C按照如图所示的位置分布在圆环上,通过上述介绍的方法计算出key的hash值,发现其落在了位置E上,按照顺时针,这个key值应该分配到redis实例A上。如果A挂了,会继续按照顺时针的方向,之前计算出的在E位置上的key会被分配到redisB,而其他redis不受影响
但是一致性哈希也不完美,主要存在以下问题:
当redis实例节点少的时候,节点变化对整个哈希环中的数据影响大,容易出现部分节点数据过多,部分节点数据过少的问题,出现数据倾斜的情况
为了解决这种问题,可以对一致性哈希算法引入虚拟节点(A#1,B#1,C#1),如下图
计算出key的hash值后落入到了位置D,按照顺时针顺序,应该落到节点C#1这个虚拟节点上,因为虚拟节点会映射到真实的节点,所以数据最终存储到了节点C
虚拟槽的原理和哈希一致性很像,redis Cluster一共有2^14(16384)个槽,所有的master节点都会有一个槽的范围比如0~1000,槽数是可以迁移的,master节点的slave节点不分配槽,只拥有读的权限,其实虚拟槽也可以看成一致性哈希中的虚拟节点。
虚拟槽和一致性哈希算法的实现也很像,先通过CRC16算法计算出key的hash的值,然后对16384取模,得到相对应的槽位,根据槽找到对应的节点,如下图
使用虚拟槽的好处:
更加方便地添加和移除节点,增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了,当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了,不需要停掉redis任何一个节点的服务(采用一致性哈希需要增加和移除节点需要rehash)
Redis Cluster结构
上面介绍了redis cluster如何将数据分配到合适的节点,下面说一下redis cluster结构,简单来说,redis cluster可以看成多个主从架构组合在一起,如下图
图上看有点乱,其实很好理解,图片中一个redis cluster有两组节点组成(官方推荐,一般至少三主三从六个节点,图中为了好看只画了两个主节点),每组节点可以看成一个主从模式,并且每组节点负责的slot不同(假设有4个slot,A组节点负责第一个和第二个slot,B组节点负责第三个和第四个,其中master节点负责写,slave节点负责读)
上图共有三种线
主备复制的线好理解,就是和主从模式一样,在master库中的数据更新后,自动更新的数据同步到slave库上
对外服务的线就是外部在对redis进行读取操作,访问master进行写操作,访问slave进行读操作
redis bus的作用相对复杂,我就在这里简单说一下,具体可以找相对应文章看看,简单来说redis bus是作用于节点之间的信息交路,交互的信息有以下几个:
数据分片(slot)和节点的对应关系(对应上图中的slot1和slot2在masterA节点上,就是要知道哪个slot在哪个节点上)集群中每个节点可用状态(不断向其他节点发消息看看你挂了没有)集群结构发生变化时,通过一定的协议对配置信息达成了一致,数据分片的迁移、主备切换、单点master的发现和其发生主备关系变更等,都会导致集群结构变化(发现有的节点挂了或者有新的节点加进来了,赶紧和其他节点同步信息)
Redis主从架构中数据会丢失吗?
会的,redis主从架构丢失主要有两种情况
异步复制同步丢失集群产生脑裂数据丢失
异步复制同步丢失:
redis主节点和从节点之间的复制是异步的,当主节点数据未完全复制到从节点时就发生了宕机,master内存中的数据就会丢失
如果说主节点开启持久化是否可以解决呢?
答案是否定的,在master发生宕机后,sentinel集群检测到主节点发生故障后,就会选举新的节点,如果就的节点在故障恢复后重启,那么此时他需要同步新的主节点的数据,此时新的主节点的数据是空的(假设这段时间中没有数据写入),那么旧主机点中的数据就会被刷掉,此时数据还是会丢失
集群产生脑裂:
简单来说,集群脑裂就是指一个集群中有多个主节点,不知道要听谁的,比如说,由于网络原因,集群出现了分区,master与slave节点之间断开了联系,哨兵检测以为主节点故障,从新选举新的节点,但主节点可能并没有发生故障,此时客户端依然在旧的主节点上写数据,而新的主节点中没有数据,在发现这个问题之后,旧的主节点会被降为slave,并且开始同步新的主节点得出数据,那么之前写入旧的主节点的数据就会被刷掉,大量数据丢失
Redis的缓存问题
缓存雪崩
缓存雪崩就是在某一个时刻出现了大规模的缓存失效情况,大量的请求直接打在数据库上,导致数据库宕机,再次空气数据库不能解决根本问题,还会再次造成缓存雪崩
造成雪崩的情况:
redis宕机很多的key设置了相同的过期时间
解决办法:
为避免redis宕机造成雪崩可以搭建redis集群尽量不设置相同的过期时间,例如可以在原有的过期时间加上随机数服务降级
缓存击穿
雪崩是大规模的key失效,而击穿就是一个热点key,大并发集中访问,突然间这个key失效了,导致大并发打在了数据库上,数据库压力剧增,经典例子就是商品秒杀,大量用户强某个商品的时候,商品的key突然过期,所有请求都打在了数据库
解决办法:
热点key不设置过期时间加锁,如果缓存失效,只有拿到锁才能查询数据库
缓存穿透
缓存穿透是指用户的请求没有经过缓存直接请求到数据库上了,比如用户请求的key在redis中不存在,或者用户恶意伪造大量不存在的key进行请求,都可以绕过缓存,导致数据库压力太大挂了
解决办法:
参数校验,例如可以对用户id进行校验,直接拦截不合法请求布隆过滤器
好了大概的我就说到这里,如有不对,欢迎指正,编写不易,一键三连哦



