Redis 面试题

概述

什么是Redis

Redis是一个用C语言编写的高性能非关系型(NoSQL)键值对数据库,它与传统数据库的一个明显区别是数据是存储在内存

优缺点

  1. 优点:
  • 读写性能优秀,读的速度是110000/S,写的速度是81000/S
  • 支持数据持久化(RDB和AOF两种)
  • 支持事务,Redis的所有操作都是原子性的,同时,Redis还支持对几个操作的组合的原子操作
  • 数据结构丰富,value支持的类型有:string、hash、set、zset、list等
  • 支持主从复制,主机对自动将数据同步到从机,可以进行读写分离
  1. 缺点:
  • 数据库容量收物理内存影响,不能做海量数据的高性能读写,因此Redis的使用场景主要局限于较小数据的高性能操作和运算
  • 不具备自动容错和恢复功能,主机从机的宕机会造成前端的部分请求失败,需要等机器重启或者手动切换前端IP
  • 主机宕机,如果在宕机前有部分数据没有同步到从机,那么切换IP后会存在数据不一致问题
  • 较难支持在线扩容

为什么要使用Redis

  1. 高性能:在内存中读写数据的速度非常快,就算数据库的数据发生改变,只要同步缓存中的数据即可
  2. 高并发:直接操作缓存可以承受的并发度要高于直接操作数据,将热点数据转移到缓存中,这样请求就不需要打到数据库上

为什么使用Redis而不是Map/guava做缓存

  • map和guava是Java自带的,可以实现本地缓存,特点是轻量与快速,声明周期随JVM的销毁而销毁,在多例情况下,每个实例都需要各自保存一份缓存,不具备一致性
  • Redis作为分布式缓存,在多例情况下也仅需要保存一份数据,缓存具有一致性

Redis的速度为什么那么快

  • 完全基于内存,绝大部分请求都是内存操作
  • 数据结构简单,对数据的操作也简单
  • 采用单线程,避免了线程切换的消耗和竞争资源的消耗,不存在锁的验证和操作,不存在死锁等问题
  • 使用多路io复用模型,非阻塞IO
  • 使用的底层模型不同,Redis直接构建了自己的VM机制,不需要在调用系统函数上花费时间

数据类型

Redis有哪些数据类型

  • String:做简单的键值对缓存
  • list:列表,存储列表类型的数据,比如粉丝列表,文章评论列表
  • set:无序集合,交集并集的操作,比如交集,可以将两个人的粉丝列表整一个交集获取共同粉丝
  • hash:结构化的数据,如一个对象
  • zset:有序集合,去重但有序,可以用来获取前几名的用户数据等,排序

Redis的应用场景

  • 计数器:可以对string进行自增自减实现计数器功能
  • 缓存:将热点数据存在Redis中,可以提高读写效率,减少数据库压力
  • 消息队列:list是一个双向链表
  • 分布式锁:在分布式场景下,无法使用单机环境下的锁来对多个节点的数据进行同步,可以使用Redis自带的setnx命令实现分布式锁
  • 获取共同数据,排行榜功能等

持久化

Redis的持久化机制

  • RDB

    • Redis DataBase,快照,这是Redis的默认持久化机制,按照一定的时间将内存中的数据以快照的形式保存到硬盘中,对应产生的文件是dump.rdb。通过配置文件中的save参数来定义快照的周期

    • 优点:

      • 只有一个文件,持久化方便
      • 容灾性好,一个文件方便保存到安全的磁盘
      • 性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化,使用单独的子进程来进行持久化,主进程没有IO操作,保证Redis的高性能
      • 相对于数据集大时,比AOF机制启动速度更快
    • 缺点:数据安全性低,RDB是隔一段时间进行持久化,如果在持久化之间Redis发生故障,会发生数据丢失,所以这种方式更适合数据要求不严谨的情况

  • AOF:

    • Append Only File,将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新从持久化的日志中恢复数据,当两种方式同时存在是,会优先通过AOF机制恢复

    • 优点:

      • 数据安全,不存在RDB在持久化之间出现故障会发生数据丢失的问题,AOF可以配置appendfsync属性的值为always,每进行一次写操作,就记录一次到AOF文件中
      • 通过append模式写文件,即使在写过程中发生故障,也可以通过Redis-check-aof工具解决数据一致性问题
    • 缺点:

      • 文件比较大,恢复速度比RDB慢
      • 启动速度同样较慢

Redis持久化数据和缓存怎么实现扩容

  • 如果Redis被当做缓存使用,可以使用一致性哈希实现动态扩容
  • 如果Redis被当做一个持久化存储容器使用,必须使用固定的keys - to - nodes映射关系,节点的数量一旦确定就不可以变化,否则,必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到

过期键的删除策略

Redis是key-value数据库,key是key设置过期时间的,Redis的过期策略就是指,当key过期了,如果进行处理

过期策略(删除策略)

  • 定时过期:每一个设置了过期时间的key都要创建一个计时器,一旦过期就会立即清除,这种策略可以立即清除过期key,对内存友好,但是会占用大量CPU资源去处理过期的数据,从而影响缓存的响应和吞吐量
  • 惰性过期:只有一个key被访问的时候会被去判断它的过期时间,优点是节省CPU资源,缺点则是会占用内存空间,可能出现大量过期key没有被访问而不会清除,占用内存的情况
  • 惰性过期:只有一个key被访问的时候会被去判断它的过期时间,优点是节省CPU资源,缺点则是会占用内存空间,可能出现大量过期key没有被访问而不会清除,占用内存的情况

Redis同时采用了惰性过期和定期过期两种策略

Redis的过期时间和永久有效怎么设置

exprie、persist

内存相关

如何保证Redis中的数据都是热点数据

Redis内存数据集大小上升到一定大小的时候,会实行数据淘汰策略

内存淘汰策略

内存淘汰策略是指,Redis用于缓存的内存空间不足时,怎么处理新写入且需要额外申请空间的数据:

  • 全局的键空间选择性移除
  1. noeviction:当空间不足以容纳新写入的时候,写入会报错
  2. allkeys-lru:从键空间中移除最近最少使用的key(最常用)
  3. allkey-random:随机移除某个key
  • 设置过期时间的键空间选择性移除
  1. volatile-lru:在设置了过期时间的键空间中移除最近最少使用的key
  2. volatile-random:从设置了过期时间的键空间中随机移除某个key
  3. volatile-ttl:有更早过期时间的key会被移除

Redis的内存淘汰策略不会影响过期策略

Redis内存用完会发生什么

写命令会报错,但是读命令可以正常返回,这种时候可以配置内存淘汰机制,在内存满的时候移除某些内容

Redis如何做内存优化

好好利用hash,list,set这些集合类型的数据,因为通常很小的key-value可以以更紧凑的方式存放到一起,尽可能使用散列表

线程模型

Redis基于Reactor开发了网络事件处理器,这个处理器被称为文件事件处理器,它的组成部分包括:多个套接字,IO多路复用程序,文件事件分派器,事件处理器。因为文件事件分派器队列的消费队列是单线程的,所以Redis是单线程的

  • 文件事件处理器使用IO多路复用程序监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器
  • 被监听的套接字准备好执行连接应答、读取、写入、关闭等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用之前套接字关联的事件处理器来处理这些事件
  • 文件事件处理器虽然是单线程的,但是通过使用IO多路复用程序来监听多个套接字,即实现了高性能的网络通信模型,有可以很好的与Redis服务器中的其他同样以单线程方式运行的模块进行对接,这保持了Redis内部单线程设计的简单性

事务

redis事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合,事务支持一次执行多个命令,一个事务中所有的命令都会被序列化,在事务执行过程中,会按照顺序串行化执行序列中的命令,其他客户端提交的命令请求不会被插入到事务执行序列中;它就是一次性的,顺序性的,排他性的执行一个队列中的一系列命令

事务的三个阶段

  • 事务开始multi
  • 命令入队
  • 事务执行exec

事务相关命令

  • redis不支持回滚,redis在事务失败时不会进行回滚,而是继续执行余下的命令,所以redis内部可以保持简单且快速
  • 如果在一个事务中的命令出现错误,那么所有的命令都不会执行
  • 如果在一个事务中的命令出现错误,那么所有的命令都不会执行
  • 如果一个事务中,出现了运行错误,那么正确的命令会被执行
  • watch:watch命令是一个乐观锁,可以为redis事务提供cas行为,可以监控一个或多个键,一旦有一个键被修改,之后的事务就不会继续执行,监控可以一直持续到exec命令
  • multi:multi命令用于开启一个事务,它总是返回OK,multi命令执行后,客户端仍然可以向服务器发送多条命令,但是这些命令不会被立即执行,而是被放到一个队列中,只有exec命令被调用时,所有队列中的命令才会被执行
  • exec:exec命令用于执行所有事务块内的命令,返回事务块内所有命令的返回值,按命令执行的先后顺序排列,当操作被打断时,返回空值null
  • discard:discard命令可以清空事务队列,并放弃执行事务,并且客户端会从事务状态中退出
  • unwacth:unwacth命令可以取消对所有key的监控

事务管理(ACID)

redis事务总是具有一致性和隔离性,其他特性是不支持的,当服务器运行在aof机制下,并且,appendFsync选项的值一直为always时,事务也具有持久性

  • redis事务支持隔离性吗
    redis是单进程程序,并且它保证在执行事务时,不会被其他事务打断,事务可以运行直到执行完所有事务队列中的命令为止

  • redis事务保证原子性吗
    redis中的单条命令是原子性执行的,但是,事务不保证原子性,且没有回滚,在事务中又命令执行失败的时候,其他命令还是会被执行

  • redis事务的其他实现

    • 基于lua脚本,redis可以保证脚本内的命令一次性按顺序执行,但是不提供回滚,命令执行出错的时候,不影响其他命令的执行
    • 基于中间变量,通过其他中间变量来标识事务是否完成,读取数据时先读取该标记变量来判断事务执行是否完成

集群方案

哨兵模式

  • 功能

    1. 集群监控:负责监控redis的master和slave进程是否正常工作
    2. 消息通知:如果某个实例发生故障,那么哨兵会向管理员发送警报消息
    3. 故障转移:如果master挂掉,会自动转移到slave上
    4. 配置中心:如果故障转移发生,会通知客户端新的master地址
  • 哨兵用于实现redis集群的高可用,它本身也是分布式的,作为一个哨兵集群去运行,互相协同工作

  • 故障转移时,判断一个master node是否宕机,需要大部分哨兵同意才行,这涉及到了分布式选举的问题

  • 即使部分哨兵节点挂掉了,哨兵集群还是可以正常工作,因为作为一个高可用机制的重要组成部分,故障转移系统本身不会是单点的

  • 核心知识

    1. 哨兵至少需要三个实例来保证自己的健壮性
    2. 哨兵 + redis主从的部署架构是不能保证数据零丢失的,但是可以保证集群的高可用性
    3. 对于哨兵 + redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足测试和演练在这里插入图片描述

主从架构

主从架构中,主节点负责写,从节点负责读

  • redis replication的核心机制

    • redis采用异步的方式复制数据到slave节点,不过从redis2.8开始,slave节点会周期性地确认自己每次复制的数据量
    • 一个master可以配置多个slave
    • slave之间是可以相互连接的
    • slave在做复制的时候,不会block自己的查询操作,它会用旧的数据集来提高服务,但是复制完成后,需要删除旧数据集,加载更新数据集,这时候会暂停服务
    • slave node主要用来进行横向扩展,做读写分离,扩容的slave node可以提高读的吞吐量
  • 注意:如果采用了主从架构,那么master需要开启持久化,否则如果master宕机重启后,数据是空的,而经过复制,slave也会变成空;另外,master的其他备份方案也需要做,如果master宕机恢复,则从备份文件中挑选一份去恢复,这样才能保证启动的时候,master是由数据的,否则即使采用了高可用机制,slave会自动接管master,但是也会发生哨兵没有检测到master宕机的时候,master就自动重启了,这样就又可能导致slave复制空数据

  • 主从复制的核心原理

    • 当启动一个slave节点的时候,slave节点会发送一个PSYNC命令给master

    • 如果这是首次连接到master,那么会触发一次full resyinchronization的全量复制,此时,master会启动一个后台进程,开始生成一份RDB快照

    • 同时,将从客户端接收到的新的血命令缓存到内存中,RDB文件生成后,master会将RDB发送给slave

    • slave将快照写入本地磁盘,然后从磁盘中加载到内存

    • 接着master会将内存中缓存的写命令发送给slave,slave也会同步这些命令

    • 主从之间如果发生网络故障,断开了连接,会自动重连,重连后,master仅仅会复制slave部分缺少的数据在这里插入图片描述

    • 过程:

      1. 主从建立MS关系后,会向主数据库发生PSYNC命令
      2. 主库接收到SYINC命令后开始在后台生成快照,并将期间接收到的写命令放入缓存
      3. 当快照完成后,主节点会将快照以及缓存发送给从节点
      4. 从节点接收到后,载入快照并执行接收到的命令
      5. 之后,主节点每次接收到写命令,都会将命令发送给从节点,从而保证数据的一致性
    • 缺点:所有的从节点数据的复制和同步都是又主节点来处理,会造成主节点压力过大,可以通过主从从结构来解决

哈希槽

redis引入了哈希槽的概念,redis集群又16384个哈希槽(因此redis最多又16384个节点),每个key通过CRC16校验后对16384取模来决定放哪个槽

分区

redis是单线程的,那如何提高多核CPU的利用率

可以在同一个服务器部署多个redis实例,并把他们当作不同的服务器来使用

为什么要做分区

分区是为了让Redis管理更多的内存,redis将可以使用所有的机器的内存,如果没有分区,最多只能使用一台机器的内存,分区使redis的计算能力通过简单的增加计算机得到成倍的提升,reids的网络带宽也会随计算机和网卡的增加而成倍增加

分区方案

  • 客户端分区:在客户端就决定数据会被存储到哪个节点,或从哪个节点读取
  • 代理分区:客户端将请求发送给代理,代理决定去哪个节点读写数据,代理根据分区规则决定请求那些redis实例,然后根据响应结果返回给客户端,redis和memcached的一种代理实现就是Twemproxy
  • 查询路由:客户端随机请求一个redis实例,再由这是实例转发给正确的redis节点

分布式问题

redis实现分布式锁

  • 当且仅当key不存在时,将key的值设为value,若给定的key已经存在,则setNX不做任何操作

  • setNX(set if not exists)

  • 返回值:设置成功返回1,设置失败返回0

  • 使用setnx完成同步锁的流程及事项如下:

    • 使用setnx命令获取锁,若返回0则说明key已经存在,锁已存在,获取失败,反之成功
    • 为了防止获取锁后程序出现异常,导致其他线程/进程调用setnx命令总是返回0而进入死锁状态,需要为key设置一个“合理”的过期时间释放锁,使用DEL命令将锁数据删除
  • 如何解决redis的并发竞争key问题

    • 所谓并发竞争key的问题也就是多个系统同时对一个key进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了不同的结果
    • 解决方案:分布式锁(redis和zookeeper)

redLock

redis官方提出了一种权威的基于redis分布式锁的方式叫redLock,它可以保证一下特性

  • 安全特性:互斥访问,即永远只有一个客户端能拿到锁
  • 避免死锁:最终客户端都能拿到锁
  • 容错性:只要大部分redis节点存活就能正常提供服务

缓存异常

缓存雪崩

缓存在同一时间大面积失效,导致请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉

  • 解决方案

    • 缓存数据的过期时间设置随机,防止同一时间大量数据过期失效
    • 一般并发量不是特别多的时候,使用最多的是加锁排队
    • 给每个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效,则更新数据缓存

缓存穿透

请求缓存和数据库中都没有的数据,导致所有的请求都落到了数据库上,造成数据库短时间内承受大量请求而崩掉

  • 解决方案

    • 接口层增加校验,如用户鉴权校验,不合理的数据直接拦截不请求
    • 从缓存取不到的数据,在数据库中也没有,可以将key-value写为key-null,缓存有效时间可以设置短一些,如30s,(设置太长会导致正常情况也无法使用)这样可以防止攻击用户反复用同一个id暴力攻击
    • 布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitMap中,一个一定不存在的数据会被bitmap拦截掉,从而避免了对底层存储系统的查询压力

缓存击穿

缓存中没有的数据,而数据库中又(一般是缓存过期)这时由于并发用户特别多,同时缓存没有读取到数据,又同时去数据库取数据,引起数据库压力瞬间大增,和缓存雪崩不同的是,缓存击穿值并发查同一条数据,缓存雪崩是不同数据过期产生的,很多数据查不到才去查数据库的;缓存击穿和缓存穿透的区别在于,缓存穿透是缓存与数据库都不存在那个数据,而缓存击穿是缓存中没有而数据库有

  • 解决方案

    • 设置热点数据永不过期
    • 加互斥锁

缓存预热

系统上线后,将相关缓存数据直接加载到缓存系统,这样就可以避免用户请求时,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据

缓存降级

当访问量剧增、服务出现问题时、非核心服务影响到核心流程的性能时,仍然需要保证服务可用,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级

redis支持的Java客户端都有哪些

redisson、jedis、lettuce

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值