logo头像

生而无畏,战至终章

Redis面经(持续更新)

1. Redis有哪些数据结构?

字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。

如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。

如果你说还玩过Redis Module,像BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。

2. 使用过Redis分布式锁么,它是什么回事?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。

这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。

3. 假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。

对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

4. 使用过Redis做异步队列么,你是怎么用的?

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

如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

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

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

如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。

5. 如果有大量的key需要设置同一时间过期,一般需要注意什么?

如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

6. Redis如何做持久化的?

bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。

对方追问那如果突然机器掉电会怎样?取决于aof日志sync属性的配置,如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。

对方追问bgsave的原理是什么?你给出两个词汇就可以了,fork和cow。fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

7. Redis提供了哪几种持久化方式?

RDB持久化
RDB 持久化是 Redis 默认的持久化方式,能够在指定的时间间隔对你的数据进行快照存储
它所生成的 RDB 文件是一个压缩的二进制文件,通过该文件可以还原生成 RDB 文件时的数据库状态

  • RDB文件的创建
    有两个命令可以生成 RDB 文件,一个是 SAVE、另一个是 BGSAVE
    两者的区别在于:前者会阻塞 Redis 服务器进程,直到 RDB 文件创建完毕为止,后者则不会阻塞服务器进程,因为是通过 fork 一个子进程,并让其去创建 RDB 文件,而服务器进程(父进程)继续则继续处理命令请求
  • RDB文件的载入
    RDB 文件的载入是在服务器启动时自动执行的,所以没有用于载入的命令,期间阻塞主进程。

只要没有开启 AOF 持久化功能,在启动时检测到有 RDB 文件,就会自动载入
当服务器有开启 AOF 持久化功能时,服务器将会优先使用 AOF 文件来还原数据库状态。原因是 AOF 文件的更新频率通常比 RDB 文件的更新频率高。

  • 自动间隔性保存
    对于 RDB 持久化而言,我们一般都会使用 BGSAVE 来持久化,毕竟它不会阻塞服务器进程。
    在 Redis 的配置文件,有提供设置服务器每隔多久时间来执行 BGSAVE 命令

AOF持久化
RDB 持久化通过保存数据库状态来持久化。而 AOF 与之不同,它是通过保存对数据库的写命令来记录数据库状态。
比如执行了 set key 123,Redis 就会将这条写命令保存到 AOF 文件中。
在服务器下次启动时,就可以通过载入和执行 AOF 文件中保存的命令,来还原服务器关闭前的数据库状态了

AOF 持久化功能的实现可以分为 3 个步骤:命令追加、文件写入、文件同步
命令追加:将写命令追加到 AOF 缓冲区的末尾
文件写入:将缓冲区内容写到 AOF 文件
文件同步:将 AOF 文件保存到磁盘

8. Pipeline有什么好处,为什么要用pipeline?

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

9. Redis的同步机制了解么?

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

10. 是否使用过Redis集群,集群的原理是什么?

Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

11. Redis key的过期时间和永久有效分别怎么设置?

设置过期时间:EXPIRE key seconds
设置永久有效:使用PERSIST命令可以清除超时,使其变成一个永久的key

如果redis没有设置expire,他是否默认永不过期?
是由删除机制决定的,在配置文件里面设置:
redis最大内存不足”时,数据清除策略,默认为”volatile-lru”

  • volatile-lru:对”过期集合”中的数据采取LRU(近期最少使用)算法.如果对key使用”expire”指令指定了过期时间,那么此key将会被添加到”过期集合”中。将已经过期/LRU的数据优先移除.如果”过期集合”中全部移除仍不能满足内存需求,将OOM.
  • allkeys-lru:对所有的数据,采用LRU算法
  • volatile-random:对”过期集合”中的数据采取”随即选取”算法,并移除选中的K-V,直到”内存足够”为止. 如果如果”过期集合”中全部移除全部移除仍不能满足,将OOM
  • allkeys-random :对所有的数据,采取”随机选取”算法,并移除选中的K-V,直到”内存足够”为止
  • volatile-ttl:对”过期集合”中的数据采取TTL算法(最小存活时间),移除即将过期的数据.
  • noeviction:不做任何干扰操作,直接返回OOM异常

使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试
缺点:在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

12. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

限定 Redis 占用的内存,Redis 会根据自身数据淘汰策略,加载热数据到内存。
所以,计算一下 20W 数据大约占用的内存,然后设置一下 Redis 内存限制即可

13. Redis支持的Java客户端都有哪些?官方推荐用哪个?

Redisson、Jedis、lettuce等等,官方推荐使用Redisson。

14. Redis和Redisson有什么关系?

Redisson是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。

15. Jedis与Redisson对比有什么优缺点?

Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

16. Redis如何设置密码及验证密码?

yum方式安装的redis配置文件通常在/etc/redis.conf中,打开配置文件找到

#requirepass foobared

去掉行前的注释,并修改密码为所需的密码,保存文件

requirepass myRedis

重启redis,然后用密码登陆的命令为

redis-cli -h 127.0.0.1 -p 6379 -a myRedis

17. 说说Redis哈希槽的概念?

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

18. Redis集群的主从复制模型是怎样的?

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.

19. Redis集群会有写操作丢失吗?为什么?

Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。 例如cluster接受了一个写请求,给client返回ok,这个写请求的内容也可能丢失。因为其写流程如下:

master B接受了一个写请求;

B写成功,返回ok给client;

B把数据广播给slaves(B1、B2、B3)

如果第二步执行完毕后,B crash了,则会发生数据不一致现象。

20. Redis集群之间是如何复制的?

异步复制

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

16384个

22. Redis集群如何选择数据库?

Redis集群目前无法做数据库选择,默认在0数据库

23. 怎么测试Redis的连通性?

ping

24. 怎么理解Redis事务?

务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

25. Redis事务相关的命令有哪几个?

开始事务:MULTI
命令的执行标记着事务的开始:这个命令唯一做的就是, 将客户端的 REDIS_MULTI 选项打开, 让客户端从非事务状态切换到事务状态。
执行事务: EXEC
监视事务: WATCH
WATCH 命令用于在事务开始之前监视任意数量的键。 当调用 EXEC 命令执行事务时, 如果任意一个被监视的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败。
取消事务 DISCARD
DISCARD 命令用于取消一个事务, 它清空客户端的整个事务队列, 然后将客户端从事务状态调整回非事务状态, 最后返回字符串 OK 给客户端, 说明事务已被取消。

26. Redis key的过期时间和永久有效分别怎么设置?

EXPIRE和PERSIST命令

27. Redis如何做内存优化?

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

28. Redis回收进程如何工作的?

一个客户端运行了新的命令,添加了新的数据。

Redi检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。

一个新的命令被执行,等等。

所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。

如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

29. Redis回收使用的是什么算法?

LRU算法

30. Redis如何做大量数据插入?

官方在2.6版本推出了一个新的功能-pipe mode,即将支持Redis协议的文本文件直接通过pipe导入到服务端。 具体示例:

  1. 新建一个文本文件,包含redis命令
    SET Key0 Value0
    SET Key1 Value1

    SET KeyN ValueN
    如果有了原始数据,其实构造这个文件并不难,譬如shell,python都可以
  2. 将这些命令转化成Redis Protocol。

因为Redis管道功能支持的是Redis Protocol,而不是直接的Redis命令。

如何转化,可参考后面的脚本。

  1. 利用管道插入
    cat data.txt | redis-cli –pipe

31. 为什么要做Redis分区?

  • 性能的提升,单机Redis的网络I/O能力和计算资源是有限的,将请求分散到多台机器,充分利用多台机器的计算能力可网络带宽,有助于提高Redis总体的服务能力。
  • 存储的横向扩展,即使Redis的服务能力能够满足应用需求,但是随着存储数据的增加,单台机器受限于机器本身的存储容量,将数据分散到多台机器上存储使得Redis服务可以横向扩展。

32. 你知道有哪些Redis分区实现方案?

  • 范围分区

    • 就是将一个范围内的key都映射到同一个Redis实例中,加入数据集还是上面提到的用户数据,
    • 我们需要一张表,这张表用来存储用户ID范围到Redis实例的映射关系,比如用户ID0-10000的是映射到R0实例
    • 们不仅需要对这张表进行维护,而且对于每种对象类型我们都需要一个这样的表,比如我们当前存储的是用户信息,如果存储的是订单信息,我们就需要再建一张映射关系表
      如果我们想要存储的数据的key并不能按照范围划分怎么办,比如我们的key是一组uuid,这个时候就不好用范围分区了
  • 哈希分区

    • id=hash(key)%N
  • 客户端实现:客户端分区就是在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。大多数客户端已经实现了客户端分区。

  • 代理实现:客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis实例,然后根据Redis的响应结果返回给客户端。redis和memcached的一种代理实现就是Twemproxy

  • 查询路由:客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。

33. Redis分区有什么缺点?

  • 涉及多个key的操作通常不支持,例如求交集,因为被分配到不同实例
  • 同时操作多个key,则不能使用redis事务
  • 分区粒度是key,不能使用很长的排序key存储一个数据集
  • 备份复杂,因为需要从不同实例手机RDB/AOF文件

34. Redis持久化数据和缓存怎么做扩容?

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

35. 分布式Redis是前期做还是后期规模上来了再做好?为什么?

  • 既然Redis是如此的轻量(单实例只使用1M内存),为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便只有一台服务器,也可以一开始就让Redis以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。一开始就多设置几个Redis实例,例如32或者64个实例,对大多数用户来说这操作起来可能比较麻烦,但是从长久来看做这点牺牲是值得的。这样的话,当你的数据不断增长,需要更多的Redis服务器时,需要做的就是仅仅将Redis实例从一台服务迁移到另外一台服务器而已(而不用考虑重新分区的问题)。一旦你添加了另一台服务器,你需要将你一半的Redis实例从第一台机器迁移到第二台机器

36. Twemproxy是什么?

  • Twemproxy是Twitter维护的(缓存)代理系统,代理Memcached的ASCII协议和Redis协议。它是单线程程序,使用C语言编写,运行起来非常快。Twemproxy支持自动分区,如果其代理的其中一个Redis节点不可用时,会自动将该节点排除(这将改变原来的keys-instances的映射关系,所以应该仅在把Redis当缓存时使用Twemproxy)。 Twemproxy本身不存在单点问题,因为可以启动多个Twemproxy实例,然后让客户端去连接任意一个Twemproxy实例。Twemproxy是Redis客户端和服务器端的一个中间层,由它来处理分区功能应该不算复杂,并且应该算比较可靠的。

37. 支持一致性哈希的客户端有哪些?

  • Redis-rb、Predis等。

38. Redis与其他key-value存储有什么不同?

  • Redis有着更为复杂的数据结构并且提供对它们的原子性操作,这是一个不同于其它数据库的进化路径。
  • Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象
  • Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,应为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面它们是紧凑的以追加的方式产生的,因为它们并不需要进行随机访问。

39. Redis的内存占用情况怎么样?

  • Redis会记录类型信息引用计数等等。64位的系统比32位的需要更多的内存开销,尤其是键值对都较小时,这是因为64位的系统里指针占用了8个字节。 但是当然,64位系统支持更大的内存,所以为了运行大型的Redis服务器或多或少的需要使用64位的系统

40. 都有哪些办法可以降低Redis的内存使用情况呢?

  • 如果使用的是32位的Redis实例,可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。

41. 查看Redis使用情况及状态信息用什么命令?

  • info

42. Redis的内存用完了会发生什么?

  • 如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者可以将Redis当缓存来使用配置淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。

43. Redis是单线程的,如何提高多核CPU的利用率?

  • 可以在同一个服务器部署多个Redis的实例,并当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的, 所以,如果想使用多个CPU,可以考虑一下分片(shard)。

44. 一个Redis实例最多能存放多少的keys?List、Set、Sorted Set他们最多能存放多少元素?

  • 理论上Redis可以处理多达2的32次方的keys,并且在实际中进行了测试,每个实例至少存放了2亿5千万的keys。任何list、set、和sorted set都可以放2的32次方个元素。换句话说,Redis的存储极限是系统中的可用内存值。

45. Redis常见性能问题和解决方案?

  • Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
  • 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
  • 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
  • 尽量避免在压力很大的主库上增加从库
  • 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3… 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变

46. 修改配置不重启Redis会实时生效吗?

可以通过以下命令动态设置:

config set *

查看哪些参数可以动态设置,命令如下:

config get *

47. Redis集群方案什么情况下会导致整个集群不可用?

Redis 集群的主从复制模型

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.

在我们例子中具有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.

然而如果在集群创建的时候(或者过一段时间)我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了

不过当B和B1 都失败后,集群是不可用的.

48. 一个字符串类型的值能存储最大容量是多少?

512MB

49. Redis有哪几种数据淘汰策略

  • 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(驱逐):禁止驱逐数据;返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令

50. Redis有哪些算法