Redis 常见面试问题汇总

基础概念

Redis 是一个基于内存的高性能 Key-Value 数据库。

Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。

优点

  • 因为是纯内存操作,Redis 的性能非常出色,每秒可以处理超过 10 万次读写操作
  • 支持保存多种数据结构,此外单个 value 的最大限制是 1GB,memcached 只能保存 1MB 的数据
  • 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
  • 可以选择持久化数据

缺点

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写

常见性能问题和解决方案

  1. Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以 Master 最好不要写内存快照。
  2. Master AOF 持久化,如果不重写 AOF 文件,这个持久化方式对性能的影响是最小的,但是 AOF 文件会不断增大,AOF 文件过大会影响 Master 重启的恢复速度。Master 最好不要做任何持久化工作,包括内存快照和 AOF 日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个 Slave 开启 AOF 备份数据,策略为每秒同步一次。
  3. Master 调用 BGREWRITEAOF 重写 AOF 文件,AOF 在重写的时候会占大量的CPU和内存资源,导致服务 load 过高,出现短暂服务暂停现象。
  4. Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave 和 Master 最好在同一个局域网内。

数据淘汰(回收)策略

该策略存在的意义是保证 Redis 中均为热点数据,Redis 共提供 6 种策略:

  1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-enviction(驱逐):禁止驱逐数据

线程和并发

  • Redis 是单进程单线程的
  • 利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。
  • 本身没有锁的概念,对于多个客户端连接不存在竞争
  • 部分客户端对 Redis 进行并发访问时发生的连接超时、数据转换错误、阻塞、客户端连接关闭等问题,这些都是客户端的问题

如何解决并发竞争的问题

  1. 客户端角度,为保证每个客户端间正常有序与 Redis 进行通信,对连接进行池化,同时对客户端读写 Redis 操作采用内部锁 synchronized
  2. 服务器角度,利用 setnx 实现锁

对于第一种,需要应用程序自己处理资源的同步,可以使用的方法比较通俗,可以使用 synchronized 也可以使用 lock。第二种需要用到 Redis 的 setnx 命令,但是需要注意一些问题。

事务 CAS check-and-set 操作实现乐观锁

Redis 作为 NoSQL 数据库也同样提供了事务机制。在 Redis 中,MULTI/EXEC/DISCARD/WATCH 这四个命令是我们实现事务的基石。

简要的列出 Redis 中事务的实现特征:

  1. 在事务中的所有命令都将会被串行化的顺序执行,事务执行期间,Redis 不会再为其它客户端的请求提供任何服务,从而保证了事物中的所有命令被原子的执行。
  2. 和关系型数据库中的事务相比,在 Redis 事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
  3. 我们可以通过 MULTI 命令开启一个事务,有关系型数据库开发经验的人可以将其理解为 BEGIN TRANSACTION 语句。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过执行EXEC/DISCARD 命令来提交/回滚该事务内的所有操作。这两个 Redis 命令可被视为等同于关系型数据库中的 COMMIT/ROLLBACK 语句。
  4. 在事务开启之前,如果客户端与服务器之间出现通讯故障并导致网络断开,其后所有待执行的语句都将不会被服务器执行。然而如果网络中断事件是发生在客户端执行 EXEC 命令之后,那么该事务中的所有命令都会被服务器执行。
  5. 当使用 Append-Only 模式时,Redis 会通过调用系统函数 write 将该事务内的所有写操作在本次调用中全部写入磁盘。然而如果在写入的过程中出现系统崩溃,如电源故障导致的宕机,那么此时也许只有部分数据被写入到磁盘,而另外一部分数据却已经丢失。

Redis 服务器会在重新启动时执行一系列必要的一致性检测,一旦发现类似问题,就会立即退出并给出相应的错误提示。

此时,我们就要充分利用 Redis 工具包中提供的 redis-check-aof 工具,该工具可以帮助我们定位到数据不一致的错误,并将已经写入的部分数据进行回滚。修复之后我们就可以再次重新启动 Redis 服务器了。

WATCH 命令和基于 CAS 的乐观锁

在 Redis 的事务中,WATCH 命令可用于提供 CAS(check-and-set) 功能。假设我们通过 WATCH 命令在事务执行之前监控了多个 Keys,倘若在 WATCH 之后有任何 Key 的值发生了变化,EXEC 命令执行的事务都将被放弃,同时返回 Null multi-bulk 应答以通知调用者事务。

执行失败。例如,我们再次假设 Redis 中并未提供 incr 命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。

适宜的场景

Redis 最适合所有数据 in-momory 的场景,虽然 Redis 也提供持久化功能,但实际更多的是一个 disk-backed 的功能,跟传统意义上的持久化有比较大的差别。

  • 会话缓存 Session Cache,Redis 提供持久化,用其来缓存购物车和 Token 之类的信息就很合适了
  • 全页缓存 FPC
  • 队列,Redis queues
  • 排行榜/计数器
  • 发布订阅

原文

如果觉得我的文章对您有用,请在支付宝公益平台找个项目捐点钱。 @Victor Sep 9, 2018

奉献爱心