redis面试题

1.什么是Redis

(1)全称 Remote Dictionary Server(远程字典服务器),使用ANSI C语言编写,是一种内存型键值存储数据库,以字典结构(即key-value形式)存储数据。
(2)支持五种主要的数据类型。

String:最基本的数据类型,可以存储任何形式的字符串。
Hash:键值对的集合,适合用于存储对象。
List:简单的字符串列表,按插入顺序排序。
Set:无序且不重复的字符串集合。
ZSet(Sorted Set):有序且不重复的字符串集合,每个元素都会关联一个double类型的分数,Redis正是通过分数来为集合中的成员进行从小到大的排序。

2.为什么Redis很快

(1)Redis将数据存储在内存中,而非硬盘上。
(2)数据结构简单且高效。支持字符串(String)、列表(List)、哈希表(Hash)、集合(Set)和有序集合(Sorted Set)等数据结构,能够以极低的时间复杂度(通常为O(1))执行常见的操作,如获取、更新和删除数据。

3.Redis是否支持事务

(1)Redis支持事务。Redis事务允许你将多个命令打包,然后一次性、顺序地执行。在事务执行期间,服务器不会中断事务而改去执行其他命令,而会将事务中的所有命令都执行完毕。
(2)必要性:保证数据的一致性和完整性。在Redis中,虽然单个命令的执行是原子性的,但多个命令的组合执行则不是。事务机制解决了这个问题,使得多个命令的组合执行也能保持原子性。
(3)如何使用/实现:通过MULTI、EXEC、DISCARD和WATCH四个命令来实现。
MULTI命令:用于标记一个事务块的开始,在MULTI命令之后,客户端发送的任意命令都将被Redis服务器放入队列中,而不是立即执行;
EXEC命令:当客户端发送EXEC命令时,Redis服务器会顺序执行事务块中的所有命令,并将所有命令的返回结果打包返回给客户端;
DISCARD命令:如果在事务执行过程中,客户端发送了DISCARD命令,那么Redis将取消事务,放弃执行事务块中的所有命令;
WATCH命令:用于在事务执行之前监视一个或多个键,如果在WATCH之后、EXEC执行之前这些键被其他命令修改了,那么当前事务将被打断,EXEC命令将返回空回复以表示事务执行失败。

值得注意的是,Redis的事务并不提供传统意义上的回滚机制。如果事务中的某个命令执行失败,Redis不会回滚已经执行成功的命令,而是会继续执行后续的命令,并返回错误。

4.redis有哪些常见的应用场景(解决哪些问题及原因)

(1)缓存

用于存储频繁访问的数据,如网页内容、用户会话、API 调用结果等,以减少对后端数据存储的请求。
原因:Redis 的数据存储在内存中,读取速度非常快,可以显著降低数据库的负载,提高数据访问速度。

(2)会话存储

在 Web 应用中,Redis 可用于存储用户的会话信息,如登录状态、购物车内容等。
原因:Redis 的快速读写能力非常适合需要频繁访问和更新的会话数据,同时其持久化特性也确保了数据的安全性。

(3)消息队列

Redis 支持发布/订阅模式,可用作轻量级的消息队列系统,用于异步任务处理、事件处理等。
原因:Redis 的发布/订阅模式简单易用,无需复杂的消息中间件即可实现消息的可靠传递。

(4)计数器和排行榜

如社交媒体的点赞数、阅读数、排名等。
原因:Redis 的原子增减操作非常适合用于计数器和排行榜应用,能够快速准确地处理高并发的计数请求。

(5)实时分析

利用 Redis 的有序集合和位图数据结构进行实时分析和计数,如用户活动记录、页面访问量等。
原因:Redis 的数据结构专为高效分析和计数设计,能够满足实时分析系统对性能和数据准确性的要求。

(6)分布式锁

在分布式系统中,Redis 可用于实现分布式锁,确保多个节点之间共享资源的一致性。
原因:Redis 的原子操作和持久化特性使其成为实现分布式锁的理想工具,能够有效地解决分布式环境下的数据一致性问题。

(7)实时通知

通过发布/订阅模式,Redis 可用于实时通知和事件处理,如聊天应用程序、即时通讯等。
原因:Redis 的实时性能够满足即时通讯和实时通知系统对消息传递速度和可靠性的要求。

(8)存储临时数据

可以用来存储临时数据,如临时验证码、令牌等。
原因:Redis 支持数据的过期时间设置,能够自动清理过期数据,非常适合存储临时数据

5.Redis的持久化方案

(1)RDB持久化

RDB是一种快照式的持久化方式,它会在指定的时间间隔内,将内存中的数据集的快照写入磁盘中。

(2)AOF持久化

AOF是一种追加式的持久化方式,它会实时地将Redis的写操作记录到磁盘中的AOF文件中。

对比

RDB持久化恢复数据库速度快,适合大规模的数据恢复;RDB文件紧凑单一,体积小,适合存储和传输。但快照方式缺乏实时性,在两次快照之间发生的数据变更可能会丢失。
AOF可以记录每一次写操作,数据恢复时尽可能少地丢失数据;同时通过AOF文件恢复数据可以保持数据的高完整性;AOF文件格式清晰易懂,便于分析和修复。但随着写操作的增加,AOF文件会越来越大,影响磁盘空间使用,同时与RDB相比,AOF文件的恢复速度较慢。

触发方式

RDB:

自动触发:在redis.conf配置文件中设置save指令,当满足指定的时间间隔和写操作次数时,自动触发bgsave命令进行持久化。
手动触发:通过执行savebgsave命令手动触发持久化。save命令会阻塞Redis服务进程,直到RDB文件创建完毕;而bgsave命令则会在后台异步执行,不会阻塞Redis服务进程。

AOF

自动触发:Redis会根据配置文件的设置,自动将写操作追加到AOF文件中。
手动触发:通过执行BGREWRITEAOF命令可以手动触发AOF文件重写,以优化AOF文件的大小和恢复效率。

6.Redis的热点key问题

(1)什么是Redis的热点key问题

在Redis数据库使用过程中,某个或某些key被极其频繁地访问,远远超出其他key的访问频率。

(2)为什么出现

数据访问不平衡:应用中,某些数据因其高关注度或高频访问特性而成为热点数据,例如秒杀商品、热点新闻等。;
数据分发不均匀:使用Redis集群时,如果数据分片不均,导致某个key落在单个节点上,该节点的负载将急剧增加,成为热点key;
缓存击穿:当某个热点key在Redis中不存在(已过期或被删除)时,所有访问该key的请求都将直接访问数据库,形成缓存击穿,数据库压力增大。。

(3)可能导致问题

Redis服务器的性能受影响,进而导致其他正常的key访问变慢或无法及时处理;可能导致Redis服务器宕机,影响整个服务的可用性;在热点key过期或失效后,所有的访问请求将直接转发到后端数据库,可能给数据库带来巨大压力。

(4)解决方案

通过数据分析和预测提前将可能的热点数据加载到Redis中,减轻热点key出现时的数据库压力;
在Redis集群中,合理调整数据分片策略,避免单点过热;同时根据负载情况增加节点,分散热点key的访问压力;
通过限流措施控制访问热点key的请求量,防止服务器过载;在Redis不可用时,实施降级策略,引导部分请求直接访问数据库或使用其他备用服务。

7.缓存击穿、缓存穿透、缓存雪崩

定义

(1)缓存击穿指的是缓存中某个热点数据在过期或被删除后,由于大量并发请求同时访问该数据,导致这些请求直接穿透到数据库或其他后端存储系统,从而增加了后端系统的负载,甚至可能引发数据库崩溃。
(2)缓存穿透指的是查询一个数据库中不存在的数据,由于缓存中也没有这个数据,所以每次查询都会直接访问数据库,如果有大量此类请求,就会对数据库造成很大压力,这种现象被称为缓存穿透。
(3)缓存雪崩指的是在某一时刻,大量的缓存数据同时失效,导致大量的请求直接打到数据库上,从而引发数据库的压力激增,可能导致整个系统的崩溃。

原因

(1)击穿原因热点数据过期,缓存中的热点数据设置了过期时间,过期后被自动删除或失效;高并发访问,在高并发场景下,即使缓存中的数据没有过期,也可能由于请求量巨大而导致部分请求穿透到数据库。
(2)穿透原因非法请求,黑客可能故意发送大量不存在的数据请求,以绕过缓存直接攻击数据库;数据不存在,用户正常请求的数据在数据库中不存在,但由于缓存机制不完善,这些请求仍然会穿透到数据库
(3)雪崩原因缓存过期策略不当,大量缓存数据设置了相同的过期时间,导致在同一时间失效;缓存服务器故障,缓存服务器宕机或网络故障导致缓存不可用。

解决方案

(1)击穿 :为热点数据设置合理的过期时间,避免在同一时间大量数据同时失效;在系统启动或缓存失效时提前加载数据到缓存中,以减少数据库的压力;当某个数据的缓存失效时,使用分布式锁来确保只有一个请求能去查询数据库并更新缓存,其他请求则等待或返回默认值;不设置数据的TTL(生存时间),而是使用逻辑上的过期标识。当需要过期时,直接删除该标识并重新加载数据到缓存中。
(2)穿透:缓存空值,即使数据库中没有某个值,可以在缓存中存储一个空值或特殊标记,这样当查询这个值时,就可以直接从缓存中获取,而不需要访问数据库;使用布隆过滤器,布隆过滤器是一种空间效率极高的概率型数据结构,可以用来判断一个元素是否在一个集合中,将所有可能存在的数据哈希到布隆过滤器中,当查询一个数据时,先判断它是否在布隆过滤器中,如果不在,就可以直接返回,而不需要访问数据库;增强数据校验,对请求的参数进行合法性检查,如过滤非法字符、判断参数范围等,以减少非法请求对数据库的冲击
(3)雪崩:为每个缓存项设置稍微不同的过期时间,可以避免大量的缓存数据同时失效;部署多个缓存服务器,实现热备份。当主缓存服务器出现问题时,可以立即切换到备份服务器。

8.Redis集群和哨兵机制

(1)Redis集群
主节点(Master):提供读写服务的Redis实例,所有数据的写操作都由主节点处理。
从节点(Slave):从主节点复制数据的Redis实例,通常用于分担读取请求和提供数据冗余。
(2)哨兵
哨兵是一个独立的进程,负责监控Redis主从集群中各个节点的运行状态。
监控:哨兵会定期向Redis的主节点、从节点以及其他哨兵实例发送心跳消息(如PING命令),以检查它们是否正常运行;
自动故障转移:当主节点出现故障时,哨兵会自动进行故障转移操作,从从节点中选举一个新的主节点,并将其他从节点重新配置为指向新的主节点;
通知:哨兵在检测到故障或进行故障转移时,可以向管理员或其他系统发送通知;
配置提供:客户端可以通过哨兵来获取当前Redis集群的主节点地址,以便在故障转移后仍然能够连接到新的主节点。
(3)为什么有哨兵机制
哨兵是Redis的高可用性解决方案中的一部分,通过哨兵机制,在主节点发生故障时,集群能够自动切换到新的主节点,保证服务的连续性;
实现自动化运维,减少人工介入,集群可以自动完成故障转移和集群重配置
动态配置,客户端可以通过哨兵获取最新的主节点信息,无需手动调整。

注意:通常建议部署奇数个哨兵实例(至少3个),以确保在发生故障时能够达成共识

9.redis的lua脚本功能

(1)介绍

Redis的Lua脚本功能允许用户在Redis服务器上执行一段Lua代码,这允许开发者将复杂的操作逻辑封装在单个脚本中,然后由Redis服务器原子性地执行这些操作。

(2)优点

Redis通过Lua脚本提供的原子性操作,确保了脚本内的多个命令在执行过程中不会被其他命令打断,从而避免了数据竞争和不一致性的风险;Lua脚本可以被存储和重用,提高了代码的可维护性和可重用性;将多个操作封装在单个脚本中,可以减少客户端与服务器之间的通信次数,从而降低了网络延迟和带宽消耗

(3)用法

EVAL:执行Lua脚本,其中脚本内容通过字符串参数提供

EVAL "script" numkeys key [key ...] arg [arg ...]
//script 是要执行的Lua脚本。
//numkeys 指定后续参数中有多少个是键(key),剩余的参数将被视为脚本的附加参数,在Lua中通过ARGV数组访问。
//key [key ...] 是脚本将访问的Redis键。
//arg [arg ...] 是传递给脚本的附加参数。

EVALSHA:与EVAL类似,但它通过脚本的SHA1摘要来执行脚本,而不是完整的脚本内容,这可以提高性能,因为Redis可能已经缓存了脚本的执行计划。

EVALSHA sha1 numkeys key [key ...] arg [arg ...]
//sha1 是脚本的SHA1摘要。
//其余参数与EVAL命令相同。

SCRIPT LOAD:用于将Lua脚本加载到Redis中,但不立即执行这返回脚本的SHA1摘要,这个摘要可以用于后续的EVALSHA命令中。

SCRIPT LOAD script

10.Redis如何实现分布式锁

(1)直接使用Redis命令

SETNX命令
SETNX是"SET if Not eXists"的缩写,当且仅当key不存在时,设置key的值,这可以用来尝试获取锁。
生成锁的key,一般为业务相关的业务key。
生成锁的value,一般为UUID或线程ID,以确保锁的唯一性。
设置锁的过期时间,避免锁过程中Redis宕机导致锁无法释放。
使用SETNX命令尝试设置key,如果返回1表示设置成功,即获取锁成功;如果返回0表示key已存在,即锁已被其他客户端持有。
释放锁时,通过删除key来实现
Lua脚本
Redis支持使用Lua脚本执行多条命令,Lua脚本在执行过程中不会被其他命令打断,从而保证了操作的原子性。
编写一个Lua脚本,该脚本首先尝试使用SET命令(带上NX和PX选项)来设置key和过期时间,如果设置成功则返回1表示获取锁成功,否则返回0。
使用Redis的EVAL命令执行该Lua脚本。
优点:操作原子性高,避免了单独使用SETNX时可能存在的问题。

(2)使用Redisson

定义:Redisson是一个基于Redis的Java驻留对象服务(Remote Service)和分布式锁框架,提供了简单而强大的方式来实现分布式锁。
实现步骤
引入Redisson依赖;使用配置信息(如Redis服务器的地址和端口)来创建Redisson客户端;通过Redisson客户端获取分布式锁对象(RLock);使用RLock对象的lock()和unlock()方法来加锁和释放锁,Redisson还提供了看门狗(Watchdog)功能,可以在客户端断开连接后自动释放锁,避免了锁被永久占用的问题;Redisson还支持可重入锁、公平锁、读写锁等高级特性,以及联锁和红锁等分布式锁的高可用性方案。

11.Redis跳表

(1)跳表

跳表是一种可以替代平衡树的数据结构,它通过在多层链表上加上额外的向前指针来提高查询效率。在Redis中,跳表主要用于**实现有序集合(sorted set)**的底层数据结构。

(2)存在原因

用于实现有序集合这一数据结构,同时要求这个数据结构能够高效地支持范围查询、插入、删除和排名等操作。跳表能够以O(log n)平均时间复杂度来执行插入、删除和查找操作,这比完全基于链表的实现(时间复杂度为O(n))要快得多,同时它也比平衡树(如AVL树或红黑树)的实现要简单得多。

(3)实现

跳表的实现方式主要包括节点的定义、层的构建和查找、插入、删除等操作的实现;节点通常包含多个前向指针,分别指向同一层中更右边的节点和下一层中更右边的节点;层的构建通常基于随机化的过程,以确保跳表的平衡性。

跳表节点

每个跳表节点不仅包含数据本身,还包含多个层(level)的指针,这些指针用于快速跳过列表中的某些元素。

typedef struct zskiplistNode {  
    // 成员对象  
    robj *obj;  
    // 分值  
    double score;  
    // 后退指针  
    struct zskiplistNode *backward;  
    // 层  
    struct zskiplistLevel {  
        // 前进指针  
        struct zskiplistNode *forward;  
        // 跨度  
        unsigned int span;  
    } level[];  
} zskiplistNode;
跳表层

跳表节点中的level数组存储了多个层的信息,每一层都包含了一个forward指针(指向前一个节点在同一层的下一个节点)和一个span(记录了两个节点之间的距离,用于计算排名)

跳表

跳表本身是一个结构体,包含了头节点、尾节点、层数最大的节点的层数、长度等信息。

typedef struct zskiplist {  
    // 跳表的头节点和尾节点  
    struct zskiplistNode *header, *tail;  
    // 跳表中层数最大的节点的层数  
    unsigned long level;  
    // 跳表长度  
    unsigned long length;  
} zskiplist;

在查找操作中,跳表从最高层开始查找,逐步向下层移动,直到找到目标元素或确定目标元素不存在,插入和删除操作也遵循类似的过程,但需要在适当的位置插入或删除节点,并可能需要更新跳表的其他部分以保持其平衡性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值