redis 面试题
# 6、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6 种数据淘汰策略见上面一条
# 7、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来
使用 keys 指令可以扫出指定模式的 key 列表。
对方接着追问:如果这个 redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?
这个时候你要回答 redis 关键的一个特性:redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。
# 8、Redis 常见的性能问题都有哪些?如何解决
Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以 Master 最好不要写内存快照。
Master AOF 持久化,如果不重写 AOF 文件,这个持久化方式对性能的影响是最小的,但是 AOF 文件会不断增大,AOF 文件过大会影响 Master 重启的恢复速度。Master 最好不要做任何持久化工作,包括内存快照和 AOF 日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个 Slave 开启 AOF 备份数据,策略为每秒同步一次。
Master 调用 BGREWRITEAOF 重写 AOF 文件,AOF 在重写的时候会占大量的 CPU 和内存资源,导致服务 load 过高,出现短暂服务暂停现象。
Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave 和 Master 最好在同一个局域网内
# 11、使用过 Redis 做异步队列么,你是怎么用的
在 Redis 中实现异步队列可以使用 Redis 的列表数据结构和发布/订阅功能。
使用 Redis 的列表数据结构:将任务添加到一个 Redis 列表中,即作为队列。生产者将任务添加到队列的尾部,消费者从队列的头部获取任务进行处理。
生产者将任务添加到队列:
LPUSH queue_name task_data
1消费者从队列获取任务:
BRPOP queue_name timeout
1BRPOP
命令会从队列的头部获取任务,如果队列为空,则会等待一段时间(timeout)直到有任务可获取。使用 Redis 的发布/订阅功能:可以使用 Redis 的发布/订阅功能实现生产者和消费者之间的消息传递。
生产者发布任务:
PUBLISH channel_name task_data
1消费者订阅任务:
SUBSCRIBE channel_name
1消费者通过订阅指定的频道(channel)来接收生产者发布的任务。
需要注意的是,Redis 是一个内存数据库,所以异步队列中的任务数据可能会占用较多的内存。可以通过设置合适的队列长度限制和定期清理过期任务来控制内存使用情况。
此外,还可以使用 Redis 的其他功能来增强异步队列的功能,例如使用 Redis 的持久化功能来保证任务的可靠性,使用 Redis 的集群功能来提高系统的可扩展性等。
- 发布/订阅模式的缺点
发布/订阅模式的缺点
发布/订阅模式在某些场景下可能存在一些缺点,包括:
丢失消息:发布/订阅模式是一种异步通信方式,发布者发布消息后,不会知道有多少订阅者接收到了消息。如果订阅者在消息发布之前或期间断开连接,那么它将无法接收到该消息,导致消息丢失。
消息顺序性:在发布/订阅模式中,消息的接收顺序是不确定的。多个订阅者可能以不同的顺序接收到消息,这可能会导致一些应用场景中的问题。
系统复杂性:使用发布/订阅模式需要管理订阅者和频道的关系,以及处理订阅者的连接和断开等。这增加了系统的复杂性,并且可能需要额外的开发和维护工作。
可靠性:发布/订阅模式中的消息传递是不可靠的,因为发布者发送消息后,不会得到确认或反馈。如果需要确保消息的可靠性传递,需要额外的机制来处理消息丢失或重复等问题。
扩展性:在大规模的发布/订阅系统中,可能需要考虑系统的扩展性问题。如果订阅者数量过多或消息频率过高,可能会导致系统负载过高,需要采取一些措施来提高系统的性能和扩展性。
总的来说,发布/订阅模式在某些场景下可能不适合或需要额外的机制来解决一些问题。在选择使用发布/订阅模式时,需要根据具体的需求和系统特点进行综合考虑。
- redis 如何实现延时队列
Redis 可以通过使用有序集合(Sorted Set)和定时任务来实现延时队列。
以下是一种基本的实现方式:
将任务数据作为有序集合中的成员,成员的分值(score)为任务的执行时间戳。
ZADD delay_queue <timestamp> <task_data>
1这样,任务会按照执行时间戳排序,成为一个有序的延时队列。
使用一个定时任务(例如每秒钟或每隔一段时间执行一次)来检查有序集合中是否有已经到期的任务。
ZRANGEBYSCORE delay_queue 0 <current_timestamp> WITHSCORES
1这个命令会返回所有执行时间戳小于当前时间戳的任务。然后可以将这些任务从有序集合中移除,并将它们添加到待处理队列中。
ZREMRANGEBYSCORE delay_queue 0 <current_timestamp>
1在待处理队列中处理任务。
RPOP processing_queue
1这个命令会从待处理队列中弹出一个任务进行处理。
如果任务需要延时执行,可以将任务重新添加到有序集合中,设置新的执行时间戳。
ZADD delay_queue <new_timestamp> <task_data>
1这样任务就会在新的执行时间到达时被重新加入到延时队列中。
需要注意的是,以上的实现方式是一种基本的思路,具体的实现可能还需要考虑一些细节问题,例如多个消费者之间的并发处理、任务的重试机制、任务的持久化等。同时,定时任务的频率也需要根据实际情况进行调整,以平衡系统的性能和实时性。
# 12、如果有大量的 key 需要设置同一时间过期,一般需要注意什么
当有大量的 key 需要设置同一时间过期时,需要注意以下几点:
性能影响:设置大量的 key 同一时间过期会对系统性能产生影响。一般需要在时间上加一个随机值,使得过期时间分散一些。过期键的处理可能会导致数据库的负载增加,因此需要评估系统的性能和资源消耗。
内存消耗:过期键需要占用内存来维护过期时间和相关数据结构。如果过期键的数量很大,会增加系统的内存消耗,需要确保系统有足够的可用内存。
过期策略:选择合适的过期策略是重要的。可以使用主动过期或惰性过期策略。主动过期是在设置 key 的同时设置过期时间,而惰性过期是在访问 key 时检查是否过期。根据实际需求选择合适的策略。
过期时间设置:需要根据业务需求合理设置过期时间。过期时间过短可能导致频繁的 key 过期和重新加载的操作,增加系统负载;过期时间过长可能导致过期键占用过多内存资源。
容错处理:在大量 key 同一时间过期的情况下,需要考虑容错处理。例如,设置备份机制或灾难恢复策略,以防止数据丢失或系统故障。
监控和调优:对于大量 key 同一时间过期的场景,需要进行监控和调优。监控系统的性能指标,如内存使用率、过期键的数量等,及时发现和解决问题。根据实际情况,进行性能调优,优化过期键的处理方式和过期时间的设置。
总之,在大量 key 同一时间过期的情况下,需要综合考虑性能、内存消耗、过期策略、过期时间设置、容错处理和监控调优等方面,以确保系统的稳定性和性能。
# 13、为什么 Redis 需要把所有数据放到内存中
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 redis 具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。在内存越来越便宜的今天,redis 将会越来越受欢迎。 如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
# 17、Pipeline 有什么好处,为什么要用 pipeline
Redis 中的 Pipeline 是一种批量执行多个 Redis 命令的机制,它可以将多个命令一次性发送给 Redis 服务器执行,而不需要等待每个命令的响应结果。使用 Pipeline 可以提高 Redis 的性能和效率。
以下是使用 Redis Pipeline 的好处:
- 减少网络延迟:由于 Pipeline 可以将多个命令一次性发送给 Redis 服务器执行,减少了每个命令的网络通信时间,从而减少了整体的网络延迟。
- 减少服务器负载:使用 Pipeline 可以将多个命令打包发送给 Redis 服务器,减少了服务器的负载。相比于逐个发送命令,使用 Pipeline 可以减少服务器的 CPU 和内存消耗。
原子性操作:Pipeline 中的命令是原子性执行的,要么全部执行成功,要么全部执行失败。这对于需要保持数据一致性的操作非常重要。- 提高吞吐量:由于 Pipeline 可以批量执行多个命令,可以在单位时间内处理更多的命令请求,从而提高 Redis 的吞吐量。
总结来说,Pipeline 可以通过减少网络延迟、减少服务器负载、提高原子性操作和提高吞吐量等方面来提高 Redis 的性能和效率。因此,在需要批量执行多个 Redis 命令的场景下,使用 Pipeline 是非常有益的。
# 18、Redis 的同步机制了解么
Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer,待完成后将 rdb 文件全量同步到复制节点,复制节点接受完成后将 rdb 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
Redis 集群是一种分布式解决方案,用于在多个 Redis 节点之间分布和复制数据,以提高系统的性能和可用性。Redis 集群采用分片(sharding)的方式将数据分布到多个节点上,并使用内置的数据复制机制来保持节点之间的数据一致性。
Redis 集群方案的实现有以下几个步骤:
分片策略:确定如何将数据分布到多个节点上。Redis 集群使用哈希槽(hash slot)的方式进行数据分片,将所有可能的键值对分配到 16384 个哈希槽中。
节点配置:配置多个 Redis 节点,每个节点都运行在不同的端口上,并在配置文件中指定节点的角色(主节点或从节点)和集群信息。
节点启动:启动所有的 Redis 节点,并通过配置文件中的集群信息将它们连接起来。节点会自动进行握手和数据同步,以建立集群的拓扑结构。
节点握手:当一个节点加入到集群中时,它会发送一个握手请求给其他节点,以建立与其他节点的通信。其他节点会验证握手请求,并将新节点添加到集群中。
数据迁移:当一个节点加入或离开集群时,会触发数据迁移过程。数据迁移将哈希槽从一个节点移动到另一个节点,以保持数据的分片一致性。
故障转移:当一个主节点失效时,集群会自动进行故障转移,将一个从节点升级为新的主节点。故障转移过程中会进行投票和选举,以保证新的主节点的可用性和数据一致性。
客户端访问:客户端可以通过连接任意一个节点来访问整个集群。当客户端发送命令时,节点会根据命令的键值对确定数据所在的哈希槽,并将命令转发到负责该哈希槽的节点上。
通过以上步骤,Redis 集群可以实现数据的分布和复制,提高系统的性能和可用性。同时,Redis 集群还提供了自动分片和故障转移的机制,使得集群的管理和维护变得更加简单和可靠。
# 19、Redis 集群方案与实现
Redis 的同步机制有两种:主从复制和哨兵模式。
主从复制:Redis 的主从复制机制通过将一个 Redis 实例的数据复制到其他实例来实现数据同步。其中一个实例为主节点(Master),负责写操作和处理客户端请求;其他实例为从节点(Slave),负责复制主节点的数据,并提供读操作。主节点将写操作记录在内存中的数据变化,然后将这些变化发送给从节点进行复制。从节点将主节点的数据复制到自己的内存中,保持与主节点的数据一致性。如果主节点失效,可以将一个从节点升级为新的主节点,保证系统的高可用性。
哨兵模式:Redis 的哨兵模式通过监控 Redis 实例的状态来实现高可用性。哨兵可以监控多个 Redis 实例,并在主节点失效时自动将一个从节点升级为新的主节点。哨兵通过心跳机制监控主节点的状态,如果主节点失效,则会选举一个新的主节点,并通知其他从节点切换到新的主节点。哨兵还可以监控从节点的状态,如果从节点失效,则会从其他从节点中选举一个新的从节点来替代。
这两种同步机制都可以实现 Redis 的数据同步和高可用性,但是主从复制适用于多个从节点的场景,可以提供更好的读性能;而哨兵模式适用于更高的可用性要求,可以自动切换主节点。
Redis Sentinal 着眼于高可用,在 master 宕机时会自动将 slave 提升为 master,继续提供服务。
Redis Cluster 着眼于扩展性,在单个 redis 内存不足时,使用 Cluster 进行分片存储。
# 20、一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set 他们最多能存放多少元素
一个 Redis 实例最多可以存放约 2^32 - 1 个 keys。
List、Set、Sorted Set 分别可以存放的最大元素数量如下:
- List:最多可以存放约 2^32 - 1 个元素。
- Set:最多可以存放约 2^32 - 1 个元素。
- Sorted Set:最多可以存放约 2^32 - 1 个元素。
换句话说,Redis 的存储极限是系统中的可用内存值。
# 21、Redis 持久化数据和缓存怎么做扩容
如果 Redis 被当做缓存使用,使用一致性哈希实现动态扩容缩容。
如果 Redis 被当做一个持久化存储使用,必须使用固定的 keys-to-nodes 映射关系,节点的数量一旦确定不能变化。否则的话(即 Redis 节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有 Redis 集群可以做到这样。
在 Redis 中,可以通过以下方式对持久化数据和缓存进行扩容:
持久化数据的扩容:
- 使用 Redis 的 RDB 持久化方式:在 RDB 持久化方式下,可以通过增加更多的 Redis 实例来扩容数据。可以使用主从复制或者集群模式来实现数据的复制和分片,从而实现数据的扩容。当需要扩容时,可以添加新的 Redis 实例并将数据进行分配和复制。
- 使用 Redis 的 AOF 持久化方式:在 AOF 持久化方式下,可以通过增加更多的 Redis 实例来扩容数据。同样可以使用主从复制或者集群模式来实现数据的复制和分片,从而实现数据的扩容。当需要扩容时,可以添加新的 Redis 实例并将数据进行分配和复制。
缓存的扩容:
- 使用 Redis 的主从复制:可以通过增加更多的 Redis 实例作为从节点来扩容缓存。将新的 Redis 实例配置为从节点,并将数据进行复制。这样可以增加缓存的读取能力和容量。
- 使用 Redis 的集群模式:可以通过将 Redis 实例组成集群来扩容缓存。将数据进行分片和复制,从而实现缓存的扩容。集群模式可以提供更高的读写性能和容量。
无论是对持久化数据还是缓存的扩容,需要注意以下几点:
- 在扩容过程中,需要确保数据的一致性。可以使用 Redis 的复制机制来实现数据的同步和复制。
- 在扩容过程中,需要考虑数据的迁移和重新分配。可以使用工具来帮助实现数据的迁移和分配。
- 在扩容过程中,需要考虑系统的可用性。可以使用负载均衡器来实现请求的分发和高可用性。
# Redis 如何做内存优化
尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。
比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面。
# Redis 回收进程如何工作的
在 Redis 中,每个键值对都是一个对象,包含了键、值以及一些额外的元数据。这些对象是通过 Redis 的内存分配器进行分配和管理的。Redis 的内存分配器会根据需要动态分配和释放内存,以适应数据的增长和减少。
Redis 通过使用一些其他的技术来管理内存,例如内存池和写时复制(Copy-on-Write)。内存池是一种预分配内存的机制,可以减少内存分配和释放的开销。写时复制是一种在对象被修改时,先复制一个新的对象,然后再进行修改的技术,可以避免修改原始对象时的并发冲突。
另外,Redis 还通过设置最大内存限制来控制内存的使用情况。当内存使用超过限制时,Redis 会根据配置的策略进行内存回收,例如删除一些键值对或者使用 LRU(最近最少使用)算法进行淘汰。
# 大 key 与热 key 问题
热点 key
热点 key 导致单机 redis 压力陡增,通过 key hash 分散热点或者使用本地缓存的方式(多级缓存),减小 redis 压力
可以将对应热 Key 进行复制并迁移至其他数据分片,例如将热 Key foo 复制出 3 个内容完全一样的 Key 并名为 foo2、foo3、foo4,将这三个 Key 迁移到其他数据分片来解决单个数据分片的热 Key 压力。 :::说明 该方案的缺点在于需要联动修改代码,同时带来了数据一致性的挑战(由原来更新一个 Key 演变为需要更新多个 Key),仅建议该方案用来解决临时棘手的问题。 :::
大 key
对大 Key 进行拆分 例如将含有数万成员的一个 HASH Key 拆分为多个 HASH Key,并确保每个 Key 的成员数量在合理范围。在 Redis 集群架构中,拆分大 Key 能对数据分片间的内存平衡起到显著作用。
对大 Key 进行清理 将不适用 Redis 能力的数据存至其它存储,并在 Redis 中删除此类数据。 :::说明 Redis 4.0 及之后版本:您可以通过 UNLINK 命令安全地删除大 Key 甚至特大 Key,该命令能够以非阻塞的方式,逐步地清理传入的 Key。 Redis 4.0 之前的版本:建议先通过 SCAN 命令读取部分数据,然后进行删除,避免一次性删除大量 key 导致 Redis 阻塞。 :::
监控 Redis 的内存水位 您可以通过监控系统设置合理的 Redis 内存报警阈值进行提醒,例如 Redis 内存使用率超过 70%、Redis 的内存在 1 小时内增长率超过 20%等。通过此类监控手段,可以提前规避许多问题,例如 LIST 数据类型的消费程序故障造成对应 Key 的列表数量持续增长,将告警转变为预警从而避免故障的发生,更多信息,请参见报警设置。
对过期数据进行定期清理 堆积大量过期数据会造成大 Key 的产生,例如在 HASH 数据类型中以增量的形式不断写入大量数据而忽略了数据的时效性。可以通过定时任务的方式对失效数据进行清理。 :::说明 在清理 HASH 数据时,建议通过 HSCAN 命令配合 HDEL 命令对失效数据进行清理,避免清理大量数据造成 Redis 阻塞。 :::
如何找出优化大 Key 与热 Key,产生的原因和问题_云数据库 Redis-阿里云帮助中心 (opens new window)