当前位置: 首页 > article >正文

Redis常见面试题总结(上)

Redis 基础
什么是 Redis?

Redis (REmote DIctionary Server)是一个基于 C 语言开发的开源 NoSQL 数据库(BSD 许可)。与传统数据库不同的是,Redis 的数据是保存在内存中的(内存数据库,支持持久化),因此读写速度非常快,被广泛应用于分布式缓存方向。并且,Redis 存储的是 KV 键值对数据。

为了满足不同的业务场景,Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap、HyperLogLog、GEO)。并且,Redis 还支持事务、持久化、Lua 脚本、发布订阅模型、多种开箱即用的集群方案(Redis Sentinel、Redis Cluster)。

Redis 没有外部依赖,Linux 和 OS X 是 Redis 开发和测试最多的两个操作系统,官方推荐生产环境使用 Linux 部署 Redis。

Redis为什么这么快?

Redis 内部做了非常多的性能优化,比较重要的有下面 3 点:

  1. Redis 基于内存,内存的访问速度比磁盘快很多;
  2. Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,主要是单线程事件循环和 IO 多路复用(Redis 线程模式后面会详细介绍到);
  3. Redis 内置了多种优化过后的数据类型/结构实现,性能非常高。
  4. Redis 通信协议实现简单且解析高效。

除了 Redis,你还知道其他分布式缓存方案吗?

  • Dragonfly:一种针对现代应用程序负荷需求而构建的内存数据库,完全兼容 Redis 和 Memcached 的 API,迁移时无需修改任何代码,号称全世界最快的内存数据库。
  • KeyDB: Redis 的一个高性能分支,专注于多线程、内存效率和高吞吐量。

说一下 Redis和 Memcached 的区别和共同点

共同点

  1. 都是基于内存的数据库,一般都用来当做缓存使用。
  2. 都有过期策略。
  3. 两者的性能都非常高。

区别

  1. 数据类型:Redis 支持更丰富的数据类型(支持更复杂的应用场景)。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
  2. 数据持久化:Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 把数据全部存在内存之中。也就是说,Redis 有灾难恢复机制而 Memcached 没有。
  3. 集群模式支持:Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 自 3.0 版本起是原生支持集群模式的。
  4. 线程模型:Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。 (Redis 6.0 针对网络数据的读写引入了多线程)
  5. 特性支持:Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。
  6. 过期数据删除:Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。

为什么要用 Redis?

  1. 高性能与低延迟
    • Redis使用内存作为主存储介质,这使得读写操作非常快速,通常达到微秒级响应时间。
    • 它采用了多种优化策略,如多路复用IO和基于事件驱动的异步机制,进一步提升了性能。
  2. 丰富的数据结构支持
    • Redis不仅支持基本的键值对存储,还提供了列表、集合、有序集合、哈希表等多种数据结构。
    • 这为复杂的数据模型提供了便利,使得开发者能更灵活地处理数据。
  3. 持久化机制
    • 虽然Redis主要在内存中操作数据,但它提供了RDB(快照)和AOF(追加文件)两种持久化方式。
    • 这确保了数据不会因为服务器故障而丢失,提高了数据的可靠性和持久性。
  4. 高可用性
    • 通过复制(Replication)、哨兵(Sentinel)和集群(Cluster)模式,Redis能够实现数据的高可用性和自动故障转移。
    • 这增强了系统的稳定性和可靠性,即使在部分节点故障的情况下也能保证服务的连续性。
  5. 消息队列与发布/订阅
    • Redis可以作为简单的消息队列使用,利用发布/订阅模式进行消息的异步传递。
    • 这非常适合解耦系统组件,实现不同服务之间的异步通信。
  6. 缓存
    • Redis是最常用的缓存解决方案之一,能够显著加速数据读取速度,减少数据库负载。
    • 无论是网页缓存、会话缓存还是计算结果缓存,Redis都能提供良好支持。
  7. 会话管理
    • 网站或应用可以将用户会话信息存储在Redis中,以减少对后端数据库的频繁访问。
    • 这提高了响应速度,并简化了会话管理。
  8. 实时数据分析与统计
    • 利用Redis的有序集合,可以快速进行计数、排序等操作。
    • 这适用于实时排名、计数器等需求,为数据分析提供了有力支持。
  9. 限流与防刷
    • 利用Redis的原子操作特性,可以简单有效地实现访问频率限制。
    • 这有助于防止恶意刷请求,保护系统的安全性。
  10. 分布式锁
    • Redis提供SETNX、MULTI/EXEC等命令,可以方便地实现分布式环境下的锁机制。
    • 这解决了并发控制问题,确保了多个节点之间共享资源的一致性。

什么是 Redis Module?有什么用?

目前,被 Redis 官方推荐的 Module 有:

  • RediSearch:用于实现搜索引擎的模块。
  • RedisJSON:用于处理 JSON 数据的模块。
  • RedisGraph:用于实现图形数据库的模块。
  • RedisTimeSeries:用于处理时间序列数据的模块。
  • RedisBloom:用于实现布隆过滤器的模块。
  • RedisAI:用于执行深度学习/机器学习模型并管理其数据的模块。
  • RedisCell:用于实现分布式限流的模块。

Redis 应用

  • 分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。关于 Redis 实现分布式锁的详细介绍,可以看我写的这篇文章:分布式锁详解 。
  • 限流:一般是通过 Redis + Lua 脚本的方式来实现限流。如果不想自己写 Lua 脚本的话,也可以直接利用 Redisson 中的 RRateLimiter 来实现分布式限流,其底层实现就是基于 Lua 代码+令牌桶算法。
  • 消息队列:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
  • 延时队列:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
  • 分布式 Session :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
  • 复杂业务场景:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。

如何基于 Redis 实现分布式锁?

一、基于SETNX和EXPIRE命令

  1. SETNX命令:SETNX(Set if Not Exists)是Redis的一个原子操作,用于在key不存在时设置值。如果key已存在,则不做任何操作。这是实现分布式锁的关键命令,因为它能确保在同一时间只有一个客户端能够获得锁。
  2. EXPIRE命令:EXPIRE命令用于为key设置过期时间。这对于避免死锁非常重要,因为即使某个客户端崩溃,锁也会在一定时间后自动释放。

实现步骤

  1. 客户端尝试使用SETNX命令设置锁key。
  2. 如果SETNX命令返回成功(即key被成功设置),则客户端获得锁,并使用EXPIRE命令为锁key设置过期时间。
  3. 如果SETNX命令返回失败(即key已存在),则客户端未获得锁,可以等待一段时间后重试。

注意事项

  • SETNX和EXPIRE命令需要原子性执行,以避免在SETNX成功但EXPIRE失败时导致的死锁问题。通常可以通过Lua脚本来保证这两个命令的原子性。

二、基于SET命令的扩展参数

Redis的SET命令支持多个扩展参数,包括NX、EX/PX等,这些参数可以方便地实现分布式锁。

  • NX:表示key不存在时才能设值成功,即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁后才能获取锁。
  • EX/PX:用于设置key的过期时间,EX以秒为单位,PX以毫秒为单位。

实现步骤

  1. 客户端使用SET key value NX EX/PX命令尝试获取锁。
  2. 如果命令返回成功,则客户端获得锁。
  3. 如果命令返回失败,则客户端未获得锁,可以等待一段时间后重试。

三、Redisson分布式锁

Redisson是一个在Redis基础上实现的Java驻内存数据网格,它提供了高效的分布式锁机制。

实现原理

  1. Redisson使用Lua脚本来保证加锁和解锁操作的原子性。
  2. 在加锁时,Redisson会生成一个随机值作为锁的唯一标识,并将其与锁key一起存储。
  3. 解锁时,Redisson会检查锁的唯一标识是否匹配,只有匹配时才会释放锁。
  4. Redisson还提供了看门狗机制,用于在锁持有者崩溃或失联时自动延长锁的持有时间,防止死锁。

使用步骤

  1. 引入Redisson的依赖。
  2. 配置Redisson客户端。
  3. 使用Redisson提供的锁API(如RLock)进行加锁和解锁操作。

四、RedLock算法

RedLock是Redis官方推荐的一种基于多个Redis实例的分布式锁算法,旨在提供更高的安全性和容错能力。

实现原理

  1. 客户端向多个Redis实例发送加锁请求。
  2. 如果大部分(通常是过半数)Redis实例都成功返回加锁结果,则客户端认为加锁成功。
  3. 解锁时,客户端需要向所有参与加锁的Redis实例发送解锁请求。

注意事项

  • RedLock算法需要多个Redis实例的支持,并且这些实例之间需要保持一定的独立性(如部署在不同的物理节点上)。
  • 在使用RedLock算法时,需要注意网络分区和Redis实例故障等异常情况的处理。

Redis 可以做搜索引擎么?

  • 数据量限制:Elasticsearch 可以支持 PB 级别的数据量,可以轻松扩展到多个节点,利用分片机制提高可用性和性能。RedisSearch 是基于 Redis 实现的,其能存储的数据量受限于 Redis 的内存容量,不太适合存储大规模的数据(内存昂贵,扩展能力较差)。
  • 分布式能力较差:Elasticsearch 是为分布式环境设计的,可以轻松扩展到多个节点。虽然 RedisSearch 支持分布式部署,但在实际应用中可能会面临一些挑战,如数据分片、节点间通信、数据一致性等问题。
  • 聚合功能较弱:Elasticsearch 提供了丰富的聚合功能,而 RediSearch 的聚合功能相对较弱,只支持简单的聚合操作。
  • 生态较差:Elasticsearch 可以轻松和常见的一些系统/软件集成比如 Hadoop、Spark、Kibana,而 RedisSearch 则不具备该优势。

如何基于 Redis 实现延时任务?

Redisson 内置的延时队列具备下面这些优势:

  1. 减少了丢消息的可能:DelayedQueue 中的消息会被持久化,即使 Redis 宕机了,根据持久化机制,也只可能丢失一点消息,影响不大。当然了,你也可以使用扫描数据库的方法作为补偿机制。
  2. 消息不存在重复消费问题:每个客户端都是从同一个目标队列中获取任务的,不存在重复消费的问题。

Redis 数据类型

Redis 中比较常见的数据类型有下面这些:

  • 5 种基础数据类型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。
  • 3 种特殊数据类型:HyperLogLog(基数统计)、Bitmap (位图)、Geospatial (地理位置)。

String 的应用场景有哪些?

String 的常见应用场景如下:

  • 常规数据(比如 Session、Token、序列化后的对象、图片的路径)的缓存;
  • 计数比如用户单位时间的请求数(简单限流可以用到)、页面单位时间的访问数;
  • 分布式锁(利用 SETNX key value 命令可以实现一个最简易的分布式锁);
  • ……

http://www.kler.cn/a/377330.html

相关文章:

  • 【ue5学习笔记2】在场景放入一个物体的蓝图输入事件无效?
  • redis数据转移
  • 如何使用Edu邮箱获取免费福利
  • 代码随想录day23 | leetcode 39.组合总和 40.组合总和II 131.分割回文串
  • Vue CLI 脚手架创建项目流程详解 (2)
  • SAP HCM 考勤时间冲突到分 源码分析
  • yt-dlp下载视频
  • mac 安装tomcat
  • 从0开始学统计-数据类别与测量层次
  • Python软体中使用Pandas库读取数据并绘制柱状图的实用指南
  • 谷粒商城のsentinelzipkin
  • Blender进阶:着色器节点
  • 02- 模块化编程-002 DS1302数码显示时间与日期
  • 【AI开源项目】FastGPT- 快速部署FastGPT以及使用知识库的两种方式!
  • 探索无线网IP地址:定义、修改方法及实践指南
  • 搭建Apache web服务器实例
  • 「C/C++」C++11 之<thread>多线程编程
  • 二、基础语法
  • Java实战项目-基于微信小程序的养老院管理系统
  • 【读书笔记/深入理解K8S】集群网络
  • 超越百万年薪--应届毕业生程序员Ocaml职位235万年薪
  • Java是如何解决并发问题的?
  • 109. 工厂光源(环境贴图和环境光)
  • Maven(23)如何使用Maven进行集成测试?
  • 使用 Nuxt 快速初始化 shadcn-vue 项目
  • 海滨学院班级回忆录:技术与设计的融合