Redis基础知识(面试必过)

          
 Redis  它的全称是  REmote DIctionary Service,直接翻译过来是 程字典服务
1、Redis 是什么?可以用来做什么?
  Redis 可以理解为高性能的非关系型数据库,(NoSQL)是键值对数据库。 
  Redis 可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合
  Redis与传统的数据库不同的是 ,它的数据时存在内存中的,因此它的读写效率才会这么快的,是已知的比较快的K-V型数据库了
  可以用来做:数据存储分布式锁,也同样支持事务持久化LUA脚本,各种方式的集群
   应用场景 如下
       缓存——提升热点数据的访问速度
       共享数据——数据的存储和共享的问题
       全局 ID —— 分布式全局 ID 的生成方案(分库分表)
       分布式锁——进程间共享数据的原子操作保证
       在线用户统计和计数
       队列、栈——跨进程的队列/栈
       消息队列——异步解耦的消息机制
       服务注册与发现 —— RPC 通信机制的服务协调中心(Dubbo 支持 Redis)
       购物车(用户差评,关注,点赞,产品标签等)
   
2、Redis有哪些基本特性?
  1. 更丰富的数据类型(string,set,zset ,hash,list)
  2. 进程内与跨进程;单机与分布式
  3. 功能丰富:持久化机制、过期策略
  4. 支持多种编程语言
  5. 高可用,集群
3、说说Redis 的几种数据类型?
         对象
对象Types属性值
Type命令输出
底层可能的存储结构
object encoding
字符串OBJ_STRING
"string"
OBJ_ENCODING_INT
OBJ_ENCODING_EMBSTR
OBJ_ENCODING_RAW
int
embstr
raw
列表
OBJ_LIST
"list"
OBJ_ENCODING_QUICKLIST
quicklist
哈希
OBJ_HASH
"hash"
OBJ_ENCODING_ZIPLIST
OBJ_ENCODING_HT
ziplist
hashtable
集合
OBJ_SET
"set"
OBJ_ENCODING_INTSET
OBJ_ENCODING_HT
intset
hashtable
有序集合
OBJ_ZSET
"zset"
OBJ_ENCODING_ZIPLIST
OBJ_ENCODING_SKIPLIST
ziplist
hashtable
skiplist(跳表)
  redis 内码转换总结
     字符串 
  •   int                                           - > (embstr -> raw)
  •    整数并且小于long (2^63-1)   - > 超过44字节 被修改
    哈希         
  •  ziplist       (升级)-->  hashtable
  • 键和值的长度小于 64byte,键值对个数不 超过 512 个,同时满足
    列表
       quicklist 
    集合
  •   intset           (升级)-->  hashtable
  • 元素都是整数类型,元素个数小于 512 个, 同时满足 
    有序集合
  •  ziplist             (升级)-->  skiplist
  • 元素数量不超过 128 个,任何一个 member 的长度小于 64 字节,同时满足。      
4、为什么要用Redis ,它起到了什么作用?/ 缓存的作用是什么?
     说到作用或者缓存,主要从2个方面入手这个问题
  •   高并发
           当请求量过大的时候(就是说缓存能够承受的请求量会远大于数据库的请求量),因此在高并发的情况下,选择将一部分请求率比较高的数据,放入缓存,缓解了数据库的压力,这部分请求就不会
  •   高性能
         一般在请求第一次时,数据从数据库中去获取数据相对来说会慢些,需要从硬盘上获取然后存入缓存中,以便下次获取同样的数据时,直接从缓存中获取,操作缓存就等于直接操作内存了,速度那就快很多了
  
  用Redis的原因
                        因为利用其它的(如:map),map是主要优势就是轻量快速,但是随着多实例的产生,其实是每个实例各自保留一份的(线程私有的),那么这样就很难保证数据的一致性了(即缓存的一致性也没办法保证)。但是Redis就不一样的,就是无论实例有多少,数据就独一份(保证了数据的一致性),的,因此Redis也叫分布式缓存,但是需要保证Redis的高可用,也就相对来说增加了运维层面的难度了。
  
5、Redis为什么这么快?
         它到底有多快?   一部分原因取决于CPU
    根据官方的数据,Redis 的 QPS 可以达到 10 万左右(每秒请求数)
  • 基于内存,k-v的数据结构内存数据库,操作内存,速度快,时间复杂度为o(1)
  • 采用单线程,减少了创建,销毁线程带来的消耗,避免上下文的切换,带来CPU的消耗,避免线程之间锁的竞争带来的锁等待的一些列问题。
  • 使用非阻塞I/O多路复用、异步非阻塞I/O的多路复用
  • 底层模型、构建了自己的VM机制,减少系统之间调用带来的消耗
    Redis 为什么是单线程的?
         因为在设计者看来单线程已经够用了,CPU 不是 redis 的瓶颈。Redis 的瓶颈最有可能是机器内存 或者网络带宽。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单 线程的方案了。
    介绍下 I/O 多路复用
         I/O :就是网络I/O
         多路:多个TCP连接
         复用:复用一个或多个线程
         基本原理: 它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件
描述符
 
     
       多路复用有很多的实现,以 select 为例,当用户进程调用了多路复用器,进程会被 阻塞。内核会监视多路复用器负责的所有 socket,当任何一个 socket 的数据准备好了, 多路复用器就会返回。这时候用户进程再调用 read 操作,把数据从内核缓冲区拷贝到用 户空间
   Redis 的事务
      Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的
     MUTIL: 开启一个事务
     EXEC :    执行事务内的命令
     DISCARD :退出事务
     WATCH: 监听一个或多个键,一旦其中键修改过了,事务执行失败,直到EXEC执行完(或执行UNWATCH)
  Redis  支持事务回滚吗?
        Redis中,单条命令是原子性执行的,但 事务不保证原子性,且没有回滚 。事务中任意命令执行失败,其余的命令仍会被执行 。Redis的其他事务的操作(LUA脚本),但其实也是不支持事务回滚的
   Redis的过期策略有哪些?(内存回收?)  
     1、过期策略 
  •   定期过期  每隔一定的时间,会扫描一定数量的数据库的 expires 字典中一定数量的 key,并清 除其中已过期的 key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和 每次扫描的限定耗时,可以在不同情况下使得 CPU 和内存资源达到最优的平衡效果
  •   惰性过期   (被动淘汰)只有当访问一个 key 时,才会判断该 key 是否已过期,过期则清除。该策略可以最 大化地节省 CPU 资源,却对内存非常不友好。极端情况可能出现大量的过期 key 没有再 次被访问,从而不会被清除,占用大量内存
  •   定时过期 (主动淘汰) 每个设置过期时间的 key 都需要创建一个定时器,到过期时间就会立即清除。该策 略可以立即清除过期的数据,对内存很友好;但是会占用大量的 CPU 资源去处理过期的 数据,从而影响缓存的响应时间和吞吐量   
  
   Redis 中同时使用了惰性过期和定期过期两种过期策略     
     2、淘汰策略  (如果都不过期,即通过过期策略,行不通了,那就需要利用淘汰机制了)
          Redis 的内存淘汰策略,是指当内存使用达到最大内存极限时,需要使用淘汰算法来 决定清理掉哪些数据,以保证新数据的存入
  如下:
volatile-lru
(最近最少使用)根据 LRU 算法删除设置了超时属性(expire)的键,直到腾出足够内存为止。如果没有 可删除的键对象,回退到 noeviction 策略 
volatile-lfu
在带有过期时间的键中选择最不常用的
volatile-random
在带有过期时间的键中随机选择
allkeys-lru
所有的键中根据 LRU 算法删除键,不管数据有没有设置超时属性,直到腾出足够内存为止
allkeys-lfu
在所有的键中选择最不常用的,不管数据有没有设置超时属性
allkeys-random
所有的键中随机删除所有键,直到腾出足够内存为止
volatile-ttl
根据键值对象的 ttl 属性,删除最近将要过期数据。如果没有,回退到 noeviction 策略
noeviction
默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息(error)OOM
command not allowed when used memory,此时 Redis 只响应读操作

     持久化  (RDB 和 AOF)
  •   RDB   是Redis DataBase缩写快照
             RDB 还有两种触发方式
                 自动触发
                       1)shutdown 触发,保证服务器正常关闭。
                       2)flushall,RDB 文件是空的,没什么意义(删掉 dump.rdb 演示一下)
                  手动触发
                     1)save    save 在生成快照的时候会阻塞当前 Redis 服务器, Redis 不能处理其他命令。如果
                          内存中的数据比较多,会造成 Redis 长时间的阻塞。生产环境不建议使用这个命令。 为了解决这个问                               题,Redis 提供了第二种方式
                     2) bgsave 执行 bgsave 时,Redis 会在后台异步进行快照操作,快照同时还可以响应客户端请
                          求。 具体操作是 Redis 进程执行 fork 操作创建子进程(copy-on-write),RDB 持久化 过程由子进程                            负责,完成后自动结束。它不会记录 fork 之后后续的命令。阻塞只发生在 fork 阶段,一般时间很短
            
              优点: 
  1. RDB只有一个dump文件,非常适合备份和灾备等   
  2. 性能好,在生成文件的时候,会fork一个子线程进行所有的保存工作,主线程不进行任何I/O的操作,从而保证了Redis的高效
  3. 在数据量比较大的时候,RDB的恢复速度比AOF要快些,                    
              缺点:
  1. 数据安全性较低,没办法做的实时或秒级持久化,因为 bgsave 每次运行都要 执行 fork 操作创建子进程,频繁执行成本过高
  2. 在一定间隔时间做一次备份,所以如果 redis 意外 down 掉的话,就会丢失最后 一次快照之后的所有修改(数据有丢失)。 如果数据相对来说比较重要,希望将损失降到最小,则可以使用 AOF 方式进行持久化
  •    AOF   Append Only File持久化
               将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据
              AOF:Redis 默认不开启。AOF 采用日志的形式来记录每个写操作,并追加到文件 中。开启后,执行更改Redis 数据的命令时,就会把命令写入到 AOF 文件中。 Redis 重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复 工作。
  
  文件越来越大,怎么办?
            由于 AOF 持久化是 Redis 不断将写命令记录到 AOF 文件中,随着 Redis 不断的进 行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间 越长。
例如 set gupao 666,执行 1000 次,结果都是 gupao=666。
               为了解决这个问题,Redis 新增了重写机制,当 AOF 文件的大小超过所设定的阈值 时,Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集。 可以使用命令 bgrewriteaof 来重写。 AOF 文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对, 然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原 来的 AOF 文件。
      重写过程中,AOF 文件被更改了怎么办?
              
     当子进程进行AOF重写的时候:有三步
  1. 处理客户端命令请求
  2. 将写命令追加到现有AOF文件中
  3. 将写命令追加到AOF重写缓存中
    AOF 2个相关参数
  • AOF 优点
  1. AOF 提供了多种同步频率(默认每秒一次,因此最多丢失一秒的数据)
  2. 通过 a ppend 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题        
  • AOF 缺点
  1. 在数据量相同的情况下,AOF的文件会比RDB大很多,数据恢复没有RDB快
  2. 高并发的情况下,AOF又高频率的备份,因此RDB也会比AOF 性能好些
  比较 RDB 和 AOF 
  • 可以忍受短时间内部分数据丢失
             如果可以忍受短时间内数据丢失,无疑RDB的效率会是更高的,RDB的快照也非常便于数据备份,并且RDB的数据恢复速度也是很快的
  •   数据完整性要求高
               数据完整性要求高,能承受丢失的数据最好越少越好,那么选择AOF肯定是好的,但是一般情况下,都不会单独的使用其中一种,一般启动的时会使用AOF,因为他的数据完整会好些

  集群
  为什么需要集群 ?
  1. 性能   Redis 本身的 QPS 已经很高了,但是如果在一些并发量非常高的情况下,性能还是 会受到影响
  2. 扩展   存储
  3. 可用性  高可用,安全性问题
  集群方案 ?
  •  主从复制
 
        主从复制 : 主从架构 -> 读写分离 -> 水平扩容支撑读高并发。
       单机的 redis,能够承载的 QPS 大概就在上万到几万不等
          对于缓存来说  , 一般都是用来支撑读高并发,因此主从复制,一主多从,主负责写,并且将数据复制到其他slave节点,从节点负责读,所有读请求都走从节点,轻松实现水平扩容, 支撑读高并发
         主从复制核心机制( redis replication 
  • 采用异步数据复制到从节点上,不过redis2.8开始,salve node 会周期性地确认自己每次复制的数据量
  • 一个主节点可以配置多个从节点
  • 从节点也可以连接其他的从节点
  • 从节点进行复制的时候,不会阻塞主节点的正常工作 
  • 从节点复制的,不会阻塞本机点的读操作,会用旧的数据提供服务,当复制完成的时候,会删除旧数据集从而加载新的数据集,这个时候会暂停对外提供服务
  • 从节点进行横向扩容,做读写分离。扩容从节点,从而提高读的吞吐量
  注意:如果采用了主从结构,那么必须开启主节点持久化,不建议用从节点作为主节点的数据热备,因为那样的话,如果关掉了 主节点的持久化,那么如果主节点宕机重启的时候数据为空,然后可能经过复制的时候,从节点数据也丢了
          主从复制的核心原理
              从节点(Slave Node)在启动的时候,会发送一个指令(PSYNC)给到主节点(Master Node)
                1、如果该从节点是第一次连接到主节点的,那么就会触发全复制( full resynchronization),此时主节点会开启一个后台线程,生成一个全量的RDB快照文件,
                2、此时从节点还会持续接受命令写入缓存中,当主节点将RDB 发生到从节点,从节点会先写入本地磁盘,然后从本地磁盘中加载到内存中
                3、主节点会将缓存中写的命令发送到从节点,因此从节点也会同步这部分数据的
                4、如果从节点跟主节点出现了网络故障,那么从节点会自动重连,主节点内将故障期间从节点缺失的这部分数据同步至从节点
       此方案的不足: 所有的slave节点数据的复制和同步都由master节点来处理,会照成master节点压力太大,使用主从从结构来解决
   
  • 可用性Sentinel ( 可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转移的功能
    
         基于主从复制的弱点,在主节点宕机的情况下,主动切换,将其中的一台从节点选举为主节点(通过选举算法,过半选举 Raft 算法),保证服务的高可用性。因此Redis通过设置一个哨兵来 监控这个些节点,来 保证服务的可用性
         Sentinel 本身没有主从之分,只有 Redis 服务节点有主从之分
        此方案不足: 主从切换的过程中会丢失数据,因为只有一个 master。 只能单点写,没有解决水平扩容的问题。 如果数据量非常大,这个时候我们需要多个 master-slave 的 group,把数据分布到 不同的 group 中。
  • 分布式  
              如果要实现 Redis 数据的分片,我们有三种方案。
 1、在客户端实现相关的逻 辑,例如用取模或者一致性哈希对 key 进行分片,查询和修改都先判断 key 的路由
 2、把做分片处理的逻辑抽取出来,运行一个独立的代理服务,客户端连接到 这个代理服务,代理服务做请求的转发。
 3、是基于服务端实现
客户端
         ShardedJedis  ->  客户端分片, 基于客户端的方案,不能实现动态的服务 增减,每个客户端需要自行维护分片策略,存在重复代码
代理Proxy  在第一个的基础上多了个中间层处理(代理)
     组件有: Twemproxy(Twitter 开源 )  Codis( Go 语言开发的
      Twemproxy 不足:   1、不能故障转移,需要依赖其他组件 2、扩容需要修改配置,数据需要重新分配等
     
      Codis  原理 :把所有的Keys分在N个(如:1024)槽中,即个槽为一个组        
分布式  ( Redis Cluster 是在 Redis 3.0 的版本正式推出的 ) 去中心化,客户端可以连接任何一个节点
  1. Cluster 解决分片的问题,数据怎么分布?
          hash取模  
                       如果是均匀分布的话,那就是hash取模 (hash(ksy)%Node) 根据余数决定分布到哪个节点,这是一种静态的分配方式,如果扩容(增加节点,减少),需要考虑数据重新部署。
         一致性hash
                  原理:将所有的hash值空间组织成虚拟的圆环(哈希环),顺时针排列
  数据分布,如图 
   
     对于 一致性hash,新增节点不影响数据,删除节点(只会影响到顺时针的下一个节点)
     一致性hash 不足之处就是;可能出现数据分布不均,因此虚拟节点就产生了
      Redis 既没有用哈希取模,也没有用一致性哈希,而是用 虚拟槽 来实现的。
               Redis 创建了 16384 个槽(slot),每个节点负责一定区间的 slot。比如 Node1 负 责 0-5460,Node2 负责5461-10922,Node3 负责 10923-16383
       步骤:
1.把16384槽按照节点数量进行平均分配,由节点进行管理 
2.对每个key按照CRC16规则进行hash运算 
3.把hash结果对16383进行取余 
4.把余数发送给Redis节点
5.节点接收到数据,验证是否在自己管理的槽编号的范围 如果在自己管理的槽编号范围内,则把数据保存到数据槽中,然后返回执行结果 如果在自己管理的槽编号范围外,则会把数据发送给正确的节点,由正确的节点来把数据保存在对应的槽中
      需要注意的是:Redis Cluster的节点之间会共享消息,每个节点都会知道是哪个节点负责哪个范围内的数据槽
 虚拟槽分布方式中,由于每个节点管理一部分数据槽,数据保存到数据槽中。当节点扩容或者缩容时,对数据槽进行重新   分配迁移即可,数据不会丢失。
     虚拟槽分区特点:
使用服务端管理节点,槽,数据:例如Redis Cluster 可以对数据打散,又可以保证数据分布均匀
为什么是16384个槽呢?
在redis节点发送心跳包时需要把所有的槽放到这个心跳包里,以便让节点知道当前集群信息,16384=16k,在发送心跳包时使用char进行bitmap压缩后是2k(2 * 8 (8 bit) * 1024(1k) = 2K),也就是说使用2k的空间创建了16k的槽数。
虽然使用CRC16算法最多可以分配65535(2^16-1)个槽位,65535=65k,压缩后就是8k(8 * 8 (8 bit) * 1024(1k) = 8K),也就是说需要需要8k的心跳包,作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。
分配槽  把16384个槽平均分配给节点进行管理,每个节点只能对自己负责的槽进行读写操作 , 由于每个节点之间都彼此通信,每个节点都知道另外节点负责管理的槽范围
    2、 怎么让相关的数据落到同一个节点上?
           在 key 里面加入{hash tag}即可。Redis 在计算槽编号的时候只会获取{}之间的字符 串进行槽编号计算,这样由于上面两个不同的键,{}里面的字符串是相同的,因此他们可 以被计算出相同的槽
  3、 客户端连接到哪一台服务器?访问的数据不在当前节点上,怎么办?
             重定向 ,Jedis在本地会备份一个 slot——node 的映射关系,通过客户端的重定向就可以了
  4、新增或下线了 Master 节点,数据怎么迁移(重新分配)?
         数据迁移。当新增节点时会把相应的数据迁移过去的(节点分配了,该节点负责的槽以及数据也会迁移的)。因为 key 和 slot 的关系是永远不会变的,当新增了节点的时候,需要把原有的 slot 分配给新的节点负责,并且把相关的数据迁移过来

   Redis集群最大节点个数是多少?   16384 

   你知道有哪些Redis分区实现方案? 客户端分区,代理分区,查询路由分区

    Reddis分区的缺点? 
  1.   涉及多个key  ,若取交集,那么可能因为存在不同的节点上而不能直接使用
  2.   同时操作多个key,使用不了事务
  3. 分区的粒度是key
  4. 分区数据备分变的更为复杂
  5. 分区时候缩容或扩容可更加复杂(如:客端的,代理分区不支持这种操作)
分布式问题
  
   实现分布式锁 ( 使用SETNX命令实现分布式锁),有失效时间,释放锁时,需要delete命令删除
  什么是RedLock
                 Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock,此种方式比原先的单节点的方法更安全。它可以保证以下特性:
  1.   安全特性:互斥访问,即永远只有一个 client 能拿到锁
  2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区
  3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务

持续更新中.. 

本文有部分内容是摘录  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值