Redis 面试核心知识点总结,一文读懂 Redis!

在当今的技术领域,Redis 作为一款高性能的键值对存储数据库,广泛应用于各类项目中。无论是缓存、分布式锁,还是消息队列等场景,都能看到 Redis 的身影。下面为大家梳理了 Redis 面试中常见的核心知识点,希望能帮助大家更好地理解和掌握 Redis。

一、Redis 为什么快

  1. 基于内存存储:数据存储在内存中,内存的读写速度远高于磁盘,这使得 Redis 在数据访问时能获得极快的响应速度。
  2. 合理的数据编码:Redis 针对不同的数据类型采用了优化的编码方式,如整数类型采用紧凑的编码存储,节省内存的同时提高了操作效率。
  3. 高效的数据结构:Redis 支持多种数据结构,如 String、List、Hash、Set、ZSet 等,这些数据结构针对不同的应用场景进行了优化,例如 Hash 结构适合存储对象,ZSet 常用于实现排行榜功能。
  4. 合理的线程模型
    • 单线程模型:早期 Redis 采用单线程模型,避免了多线程带来的线程上下文切换和竞争问题,使得代码逻辑简单,易于维护,保证了数据的一致性。
    • IO 多路复用:通过 IO 多路复用技术,多个网络连接可以复用同一个线程来处理。Redis 利用这一技术,在单线程的情况下,高效地处理大量的并发网络请求。
  5. 虚拟内存机制:Redis 的虚拟内存机制允许将部分不常用的数据交换到磁盘上,从而在有限的内存空间中存储更多的数据,同时保证热数据的快速访问。

二、Redis 的应用场景

  1. 缓存:这是 Redis 最常见的应用场景,用于缓存数据库查询结果、页面片段等,减少数据库的访问压力,提高系统的响应速度。
  2. 排行榜:利用 Redis 的 ZSet 数据结构,可以轻松实现各种排行榜功能,如游戏排行榜、商品销量排行榜等。
  3. 共享 session:在分布式系统中,通过 Redis 存储用户的 session 信息,实现不同服务器之间的 session 共享。
  4. 消息队列:Redis 的 List 数据结构可以用作简单的消息队列,生产者将消息放入 List,消费者从 List 中取出消息进行处理。
  5. 分布式锁:在分布式系统中,当需要控制同一时间修改数据的进程数时,可使用 Redis 实现分布式锁,如在商品秒杀、抢优惠券等场景中。
  6. 位操作:Redis 支持位操作,可用于统计用户的在线状态、签到情况等,通过位运算实现高效的统计和存储。
  7. 社交网络:利用 Redis 的 Set 数据结构可以实现好友关系、共同关注等社交网络功能。
  8. 计数器应用:常用于统计播放量、浏览量等,通过 Redis 的原子操作,保证计数的准确性和高效性。

三、Redis 早期用单线程及后期引入多线程的原因

  1. 早期单线程:主要是为了保证数据的一致性和简单性。单线程模型避免了多线程编程中的复杂问题,如死锁、竞态条件等,使得 Redis 的开发和维护更加容易。
  2. 后期引入多线程:随着业务的发展和硬件资源的提升,为了进一步提高性能和充分利用硬件资源,优化 I/O 操作,适应复杂业务场景,Redis 引入了多线程。多线程可以利用多核 CPU 的优势,提高 Redis 在处理大量并发请求时的性能。

四、基于 Redis 实现分布式锁

  1. 分布式锁概念:在分布式系统下,当数据为一份或有限份时,需要利用锁技术来控制同一时间修改数据的进程数,这种锁技术即为分布式锁。常见应用场景有商品秒杀、抢优惠券等。
  2. Redisson 框架:它是一个在 Redis 的基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。在实现分布式锁时,Redisson 解决了锁过期释放但业务还没有执行完的问题,通过提供可重入锁、公平锁、联锁等多种锁实现方式,满足不同业务场景的需求。

五、Redis 持久化方式

  1. RDB(快照):Redis 提供了两个指令生成 RDB 快照。
    • Save:该指令由主线程执行,在生成快照期间会阻塞主线程,影响 Redis 对客户端请求的处理。
    • Bgsave:通过 fork 一个子进程来写入 RDB 文件,将快照持久化任务交给子进程处理,父进程可以继续处理客户端的其他请求。
  2. AOF(追加日志):其过程包括命令写入、文件同步和文件加载。
    • 命令写入:将写命令追加到 AOF 文件中。
    • 文件同步:通过配置项(如 always、everysec、no)设置同步策略,控制数据写入磁盘的时机。
    • 文件加载:在 Redis 启动时,重新执行 AOF 文件中的所有写命令来恢复数据。
  3. 比较
    • 写入性能:RDB 在进行快照时,fork 子进程处理数据写入,不影响主进程读写操作,写入性能较好。
    • 恢复速度:RDB 恢复数据时,直接将快照文件加载到内存中,速度较快;AOF 则需要重新执行文件中的所有写命令,恢复速度相对较慢。
    • 安全性:AOF 是追加日志的方式,只要配置合理,能保证数据的安全性;RDB 是定期进行快照,两次快照之间若发生服务器故障,可能会丢失部分数据。
  4. 混合式持久化
    • 写入时:先按照 RDB 方式快照(fork 子进程不阻塞主进程)写入数据到磁盘,对应快照后的写操作再用 AOF 方式追加到文件中。
    • 恢复时:先加载 RDB 部分数据,快速恢复大部分数据,再重放 AOF 部分的增量写命令以保证数据的完整性。这种方式兼具了 AOF 和 RDB 的安全性特点。
  5. 修改持久化方式
    • RDB:修改 redis.conf 配置文件中的 save 部分,如 “save 900 1” 表示在 900 秒(15 分钟)之内,如果至少有 1 个 key 发生变化,则进行快照。
    • AOF:开启 AOF 需修改 redis.conf 文件中的 appendonly 值为 yes;设置 AOF 的持久化策略通过 appendfsync 配置项,有 always(每次写操作都立即将数据写入磁盘,最安全但性能较差)、everysec(每秒执行一次数据写入磁盘,折衷方案,推荐)、no(让操作系统来决定何时将数据写入磁盘,性能较好但可能会有数据丢失的风险)。
    • 混合持久化:修改 redis.conf 配置文件中的 aof-use-rdb-preamble 的值为 yes,或使用 “config set aof - use - rdb - preamble yes” 命令设置。
  6. 持久化的目的:主要是为了在 Redis 服务器重启后,能够恢复之前存储的数据,保证数据的持久性和可靠性。

六、实现 Redis 的高可用、扩展性和可靠性

  1. 主从模式
    • 部署方式:部署多台机器,主节点负责读写操作,从节点负责读操作。从节点的数据来自主节点,通过主从复制机制实现数据同步,包括全量复制和增量复制。
    • 全量复制:从节点第一次连接主节点(或者认为是第一次连接主节点)时采用全量复制,主节点将所有数据发送给从节点。
    • 增量复制:从节点与主节点完成全量复制之后,如果主节点的数据再次发生变化,则触发增量复制,主节点只将变化的数据发送给从节点。
    • 作用:数据冗余,提供多个副本防止单节点故障导致数据丢失;实现读负载均衡,多个节点共同处理读请求,减轻主节点的读压力,提升系统的并发处理能力。
  2. 哨兵模式
    • 架构:在主从模式的基础上增加了一个或多个哨兵(Sentinel)。哨兵可以监视所有的主从节点,并且在被监视的主节点下线时自动将下线服务器下的某个从节点升级为新的主节点,哨兵之间还会相互监控,从而达到高可用。
    • 切换过程:假设主服务器宕机,哨兵 1 先检测到这个结果,此时系统并不会马上进行 failover 过程,仅仅是哨兵 1 主观地认为主服务器不可用,这称为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,哨兵之间会进行一次投票,投票的结果由一个哨兵发起,进行 failover 操作。切换成功后,通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线,对于客户端而言,一切都是透明的。
    • 作用:自动故障处理,在主节点发生故障时能快速自动处理,减少了系统的停机时间,确保了系统的可用性;实时监控节点状态,出现异常时可以通过配置好的通知机制(如邮件、短信等)及时通知管理员。
  3. 集群模式
    • 数据分片:对数据进行分片,每台 Redis 节点上储存不同的内容。在 Redis 3.0 加入,实现了 Redis 的分布式存储。节点间通过 Gossip 协议进行通信,常见的四种消息类型为 ping、pong、meet、fail。
    • 消息类型:Ping 用于检测节点是否在线和交换节点的状态信息;Pong 作为 ping、meet 消息的响应消息回复给发送方确认消息正常通信;Meet 通知新节点加入到集群;Fail 判定节点下线时的广播消息,其他节点收到消息后更新对应节点的状态为 “下线”。
    • 作用:水平扩展,可以通过添加更多的节点来扩展集群的存储容量和处理能力;高可用性,集群中的每个节点都有自己的数据副本,当某个节点出现故障时,集群可以自动将请求转发到其他健康的节点上,保证系统的正常运行。

七、使用 Redis 时的常见问题

  1. 缓存击穿:某个热点 key 过期时恰好有大量对这个 key 的并发请求,进而导致大量请求转到数据库,可能会使数据库压力过大。
  2. 缓存穿透:查询一个一定不存在的数据,由于缓存不命中而查询数据库,又因为数据库也没有数据所以不写入缓存。
    • 避免方法:在 API 入口对参数进行校验,过滤非法值;如果查询数据库为空,给缓存设置默认值(有写请求输入时需要及时更新缓存);使用布隆过滤器,传入查询请求时先用布隆过滤器判断值是否存在,值存在再继续查询。
    • 布隆过滤器:一种占用空间很小的数据结构,由一个很长的二进制向量和一组 hash 映射函数组成。对一个 key 进行 N 个 hash 算法获取 N 个值,在比特数组中将这 N 个值散列后设定为 1,查询时如果特定的这几个位置都为 1,那么布隆过滤器判断该 key 存在。
  3. 缓存雪崩:缓存中数据大量到期,而查询量又很大,请求都直接访问数据库,导致数据库压力过大甚至宕机。
    • 避免方法:合理设置缓存的过期时间(TTL,time to live),避免大量缓存同时过期;可以采用随机过期时间,让缓存过期时间分散开来。

八、Redis 的数据类型

Redis 支持多种数据类型,包括 String、List、Hash、Set、ZSet、Geo、BitMap、HyperLogLog。不同的数据类型适用于不同的应用场景,如 String 常用于存储简单的键值对,List 适用于实现队列和栈,Hash 适合存储对象等。

九、Redis 集群的原理

Redis 集群主要用于处理高并发问题、实现扩容,具备高并发、高可用、分布式的特点。通过数据分片将数据存储在不同的节点上,节点间通过 Gossip 协议进行通信和数据同步,实现了集群的水平扩展和高可用性。

十、Redis 的事务原理

Redis 事务是一组命令的集合,它可以保证这组命令在执行过程中不会被其他客户端的命令打断,即这些命令要么全部执行,要么全部不执行。事务的生命周期包括以下几个阶段:

  1. 事务开启阶段:客户端发送 MULTI 命令,Redis 服务器将客户端状态标记为事务状态,开始接收并缓存后续的命令。
  2. 命令入队阶段:客户端在事务状态下发送各种命令,这些命令会依次被添加到事务队列中。在这个阶段,服务器会对命令进行语法检查,但不会执行命令。
  3. 事务执行或取消阶段
    • 执行事务:客户端发送 EXEC 命令,服务器开始依次执行事务队列中的所有命令。如果使用了 WATCH 命令,服务器会先检查被监视的键是否被修改,如果没有修改,则执行事务;如果有修改,则取消事务。
    • 取消事务:客户端发送 DISCARD 命令,服务器清空事务队列,取消事务,客户端状态恢复到正常状态。
  4. 事务结束阶段:事务执行完成后,无论执行结果是成功还是失败,客户端的状态都会从事务状态恢复到正常状态,事务生命周期结束。

十一、延时双删

在更新数据时,先删除缓存,再更新数据库,然后休眠一段时间后再次删除缓存。这种方式主要用于解决在更新数据库后,缓存中的旧数据可能导致的数据不一致问题。通过延时再次删除缓存,可以确保在数据库更新完成后,缓存中不会存在旧数据。

以上就是 Redis 面试中常见的核心知识点总结,希望对大家有所帮助。在实际学习和工作中,还需要结合具体的场景深入理解和应用 Redis 的各项特性。想要了解更详细的redis面试问题可以看看这个链接20道经典Redis面试题(本文大部分内容也总结于这篇文章)

你可能感兴趣的:(redis,笔记)