目录
1.为什么 Redis 这么比较快?
答:Redis 运行比较快的原因有以下几个:
- 1基于内存:Redis 是一种基于内存的数据存储系统,所有的数据都存储在内存中。相比传统的磁盘存储系统,内存访问速度更快,这使得 Redis 能够在毫秒级别快速地读取和写入数据
- 单线程模型:Redis 使用单线程模型来处理客户端请求。这可能听起来似乎效率不高,但实际上,这种设计有助于避免多线程的竞争条件和锁开销。Redis 通过非阻塞的方式处理多个客户端请求,每个请求的执行时间很短,因此在单线程下,Redis 能够处理大量的并发请求
- 高效数据结构: Redis 提供了多种高效的数据结构,如哈希表、有序集合等。这些数据结构的实现都经过了优化,使得 Redis 在处理这些数据结构的操作时非常高效。
- 非阻塞 I/0:Redis 使用了非阻塞/0 模型,这意味着当进行磁盘读写或者网络通信时,Redis 不会等待数据的返回,而是继续处理其他请求。这样可以充分利用 CPU 的时间,提高整体的吞吐量。
2.Redis 可以实现什么功能?
答: Redis 常见的使用场景有以下几个
- 会话存储:保存用户的登录信息
- 存储普通缓存:例如详情页等数据的缓存信息存储
- 实现分布式锁:Redis 可以非常方便的实现微服务下的分布式锁,Redis 天然就支持分布式服务4.简单的消息队列: Redis 自身提供的发布订阅模式,可以用来实现简单的消息队列。
3.Redis 常用数据类型有哪些?
答: Redis 常用的数据类型如下
- String(字符串):常见使用场景是存储 Session 信息、存储缓存信息(如详情页的缓存)、存储整数信息,可使用 incr 实现整数+1,和使用 decr 实现整数-1。
- List(列表类型):常见使用场景是实现简单的消息队列、存储某项列表数据
- Hash(哈希表):常见使用场景是存储 Session 信息、存储商品的购物车,购物车非常适合用哈希字典表示,使用人员唯一编号作为字典的 key,value 值可以存储商品的 id 和数量等信息、存储详情页信息。
- Set(集合):一个无序并唯一的键值集合,它的常见使用场景是实现关注功能,比如关注我的人和我关注的人,使用集合存储,可以保证人员不会重复。
- Sorted Set (有序集合): 相比于 Set 集合类型多了一个排序属性 score (分值),它的常见使用场景是可以用来存储排名信息、关注列表功能,这样就可以根据关注实现排序展示了。(有序集合 Sorted Set 也被称为Zset,原因是有序列表的底层数据库实现是zskiplist,所以也被称之为Zset)
4.有序列表的底层是如何实现的?
答:当数据比较少时,有序集合是压缩列表 ziplist 实现的,反之则为跳跃表 skiplist 实现.使用压缩列表存储必满足以下两个条件:
- 有序集合保存的元素个数要小于 128个
- 有序集合保存的所有元素成员的长度都必须小于64 字节
如果不能满足以上两个条件中的任意一个,有序集合将会使用跳跃表 skiplist 结构进行存储.
5.什么是跳跃表?
答:跳跃表 Skip list,也称之为跳表,是一种数据结构,用于在有序元素的集合中进行高效的查找操作它通过添加多层链表的方式,提供了一种以空间换时间的方式来加速查找。跳跃表由一个带有多层节点的链表组成,每一层都是原始链表的一个子集。最底层是一个完整的有序链表,包含所有元素。每个更高层级都是下层级的子集,通过添加额外的指针来跳过一些元素。这些额外的指针称为“跳跃指针”,它们允许快速访问更远的节点,从而减少了查找所需的比较次数。跳跃表的平均查找时间复杂度为 0og n),其中 n 是元素的数量。这使得它比普通的有序链表具有更快的查找性能,并且与平衡二又搜索树 (如红黑树)相比,实现起来更为简单简单的跳跃表如下图所示:
6.Redis 如何实现分布式锁?
答:首先来说 Redis 作为一个独立的三方系统,其天生的优势就是可以作为一个分布式系统来使用,因此使用Redis实现的锁都是分布式锁,理解了这个概念才能看懂本文所说的内容分布式锁的示意图,如下所示:
使用 Redis 实现分布式锁可以通过 setnx (set if not exists)命令实现,当我们使用 setnx 创建键值成时,则表明加锁成功,否则既代码加锁失败,实现示例如下:
127.0.0.1:6379> setnx lock true (integer) 1 #创建锁成功 #逻辑业务的处理 127.0.0.1:6379> del lock (integer) 1 #释放锁
当我们重复加锁时执行结果:
127.8.9.1:6379> setnx lock true # 第一次加锁 (integer) 1 127.8.9.1:6379> setnx lock true # 第二次加锁 (integer) 0
因此我们可以通过看执行结果是否为 1 来判断加锁是否成功
7.Redis分布式锁存在什么问题
1.可能产生死锁
产生原因:
获取锁的线程下线或者是崩溃或者掉电了,线程不会继续执行了,那么就会发生死锁.
解决方法:
通过设置超时时间来解决,如果超过超时时间自动释放,就不会存在死锁问题了,也就是 setnx 和 expire配合使用,在 Redis 2.6.12 版本之后,新增了一个强大的功能,我们可以使用一个原子操作也就是一条命令来执行 setnx和expire 操作了,实现示例如下:
127.9.9.1:6379> set lock true ex 30 nx OK #创建锁成功 127.9.日.1:6379> set lock true ex 30 nx (nil) #在锁被占用的时候,企图获取锁失败
其中ex为设置超时时间,时间单位是 秒, nx 为元素非空判断,用来判断是否能正常使用锁的
2.锁误删
产生原因:
线程一的程序的执行时间大于超时时间,导致因为超时而删除掉锁了,这时候锁就被线程二获取, 等线程一业务执行完成,它并不知道自己的锁因为超时被删除掉了,还以为自己有锁呢,就会执行del lock从而释放掉线程二的锁,.线程二的锁莫名其妙就被删除了.
解决方法:
使用锁的唯一标识:每个锁都应该有一个唯一的标识,可以使用一个随机生成的字符串或者唯一的ID。在释放锁的时候,先比较锁的标识是否匹配,只有匹配才能删除锁。这样可以防止误删其他线程持有的锁。
8.Redis怎么保证数据不丢失
答:虽然Redis的读写都是在内存中,因为这样操作性能最高,但在内存中的数据会随着服务器的重启而丢失,为了保证数据不丢失,我们需要将内存中的数据存储到磁盘,以便 Redis 重启时能够从磁盘中恢复原有的数据,而整个过程就叫做 Redis 持久化,也就是说 Redis 使用了“持久化”技术来保证 Redis 中的数据不丢失。
Redis 持久化有以下 3 种实现方式:
1.快照方式(RDB,Redis DataBase):将某一个时刻的内存数据,以二进制的方式写入磁盘
2.文件追加方式(AOF,Append only File):记录所有的操作命令,并以文本的形到文件中
3.混合持久化方式:Redis 4.0之后新增的方式,混合持久化是结合了 RDB 和 AOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险
前两种配置可以在 Redis 的配置文件 (redis.conf) 中,通过“appendonly”来进行设置,如下图所示:
no:关闭RDB,开启AOF
yes:开启RDB 关闭AOF
9. RDB与AOF的区别
答:RDB和AOF 都是 Redis 持久化的两种方式,它们的区别如下:
(1).AOF 持久化
1.工作原理: AOF 持久化将 Redis 的写操作以日志的形式追加到一个文件中,类似于数据库的事务日志。当 Redis 需要重启时,可以通过回放 AOF 文件中的写操作来恢复数据.2.优点:
a.数据完整性较好:AOF 持久化是通过追加写操作来记录数据变化的,因此通常比 RDB更可靠,数据丢失的可能性较低.
b.可读性高:AOF 文件是一个文本文件,可以方便地查看和分析其中的内容,也更容易进行手动修复.
c.多种持久化策略: Redis 提供了不同的 AOF 持化策略,可以根据需要选择不同级别的数据安全性和性能.3.缺点:
a.文件较大:由于是追加写,AOF 文件会随着时间增长,可能会变得较大。较大的 AOF文件可能会影响重启和恢复的速度
b.对磁盘的写入频繁:AOF持久化需要频繁地写入日志文件,可能会对磁盘造成一定的写入压力。
(2)RDB持久化
1.工作原理:RDB 持久化是通过快照的方式,将当前 Redis 内存中的数据以二进制格式保存到硬盘上的一个文件中。快照文件是一个经过压缩的二进制文件,它代表了某个时间点上的数据库状态。
2.优点:
a.效率高:RDB持久化是将整个数据集以二进制格式保存到磁盘上,因此在数据恢复时,速度较快.
b.文件较小: 相比 AOF,RDB 生成的快照文件通常较小,占用较少的磁盘空间
3..缺点:
数据丢失:由于 RDB 是定期保存快照,如果 Redis 在快照之间发生故障,可能会导致部 分数据的丢失。
10.AOF持久化策略有哪些?
答:AOF 持久化策略有 3 种:
- always:每条 Redis 操作命令都会写入磁盘,最多丢失一条数据
- everysec:每秒钟写入一次磁盘,最多丢失一秒的数据 (默认持久化策略)。
- no:不设置写入磁盘的规则,根据当前操作系统来决定何时写入磁盘,Linux 默认 30s 写入一次数据至磁盘。
这3种配置可以在 Redis 的配置文件 (redis.conf) 中,通过“appendfsync everysec”来进行设置,如下图所示:
11.Redis键值过期之后会被直接删除吗?
答: Redis 中的键值过期之后,在物理层面来说,并不会立即去删除这个过期数据。因为 Redis 本身是单线程执行的,如果某个键值过期之后就立马删除它,那么删除操作可能就会影响主业务的执行,这样就得不偿失了,所以当某个键值过期之后,在 Redis 的物理层面并不会立即删除此过期数据,而是等待某个时机一起删除多个过期键。
12.Redis如何淘汰过期数据
答:Redis使用惰性删除和定期删除两种删除策略来淘汰过期数据。
1.惰性删除: 不主动删除过期键,每次从数据库获取键值时判断是否过期,如果过期则删除键值,并返回 null。
a.优点:每次访问时,才会判断过期键,所以此策略只会使用很少的系统资源
b.缺点:系统占用空间删除不及时,导致空间利用率降低,造成了一定的空间浪费2.定期删除:每隔一段时间检查一次数据库,随机删除一些过期键。
a.优点: 通过限制删除操作的时长和频率,来减少删除操作对 Redis 主业务的影响,同时也能删除一部分过期的数据减少了过期键对空间的无效占用。
b.缺点:内存清理方面会存在一定的空间浪费,同时没有惰性删除使用的系统资源少
定期删除的执行流程:
1.从过期字典中随机取出 20个键
2删除这 20个键中过期的键
3.如果过期 key 的比例超过 25%,重复步骤1。
同时为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过 25ms。
定期删除执行流程,如下图所示:
13.Redis内存用完会怎样
答:当Redis 运行内存被使用完时,也就是当 Redis 的运行内存,已经超过 Redis 设置的最大内存之后,Redis 将采用内存淘汰机制来删除符合条件的键值对,以此来保障 Redis 的正常运行.
14.Redis的内存淘汰策略有哪些
答:早期版本的 Redis 有以下 6种淘汰机制(也叫做内存淘汰策略)
1.noeviction: 不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略2.allkeys-lru:淘汰整个键值中最久未使用的键值;
3.allkeys-random: 随机淘汰任意键值
4.volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值
5.volatile-random: 随机淘汰设置了过期时间的任意键值:
6.volatile-ttl: 优先淘汰更早过期的键值。
在 Redis 4.0版本中又新增了2种淘汰机制:
1.volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值
2.allkeys-lfu:淘汰整个键值中最少使用的键值。其中allkeys-xxx表示从所有的键值中淘汰数据,而 volatile-xxx 表示从设置了过期键的键值中淘汰数据所以,现在 Redis 的版本中有 8种内存淘策略
LRU和LFU的区别?
答: LRU (Least Recently Used,最近最少使用)和 LFU (Least Frequently Used,最不常使用)都是常见的缓存淘汰策略,它们在选择淘汰缓存中的键时有不同的侧重点。
1LRU(最近最少使用):LRU 策略基于时间的概念,它认为最近被访问过的键是最有可能被再次访问的,因此在淘汰时会优先选择最久未被访问的键。LRU 策略会维护一个访问顺序列表,每当一个键被访问时,它会被移动到列表的末尾,最近没有被访问的键会位于列表的前面。当需要淘汰键时,LRU 策略会选择列表前面的键进行淘汰
2.LFU(最不常使用):LFU 策略基于访问频率的概念,它认为被访问次数最少的键是最不常用的,因此在淘汰时会优先选择访问次数最少的键。LFU 策略会为每个键维护一个访问计数器,每当一个键被访问时,其计数器会增加。当需要淘汰键时,LFU 策略会选择访问计数最低的键进行淘汰。主要区别:
1.LRU 是基于时间的策略,LFU 是基于访问频率的策略
2.LRU 策略假设最近被访问的键最有可能再次被访问,LFU 策略假设被访问次数最少的键是最不常用的3.LRU 维护一个访问顺序列表,LFU 维护一个访问计数器
4.LRU 只关注键的访问顺序,而 LFU 关注键的访问频率
15.过期删除策略和内存淘汰策略有什么区别?
答: Redis 内存淘汰机制和过期删除策略是完全不同的概念
1.内存淘汰机制:解决 Redis 运行内存过大的问题的,通过与最大运行内存(maxmemory)比较,决定要不要淘汰数据,根据 maxmemory-policy 参数,决定使用何种淘汰策略,在 Redis 4.0 之后已经有8 种淘汰策略了,默认的策略是 noeviction 当内存超出时不淘汰任何键值,只是新增操作会报错。
2.过期删除策略: Redis 为了删除过期键,而设定的规则,主要是为了删除过期数据的。
16.Redis 中有事务吗?
答: Redis 有事务,但 Redis 中的事务的主要作用是将多个命令一次性加入到事务队列中,然后通过EXEC命令一次性执行所有命令。这样可以减少网络往返次数,提高了批量操作的效率。如下图所示:
Redis 操作事务的命令主要有以下几个
1.MULTI(开启事务):用于标记一个事务的开始。在执行 MULTI 命令之后,客户端可以将多个命令添加到事务队列中。
2.EXEC(提交事务):用于执行事务队列中的所有命令。在执行 EXEC 命令之前,Redi 会依次记录所有入队的命令,然后按顺序一次性执行这些命令。如果在执行事务期间没有发生错误,所有的命令会原子性地执行。如果其中一个命令执行出错,整个事务会回滚,所有命令都不会执行。
3.DISCARD(回滚事务):用于取消一个事务,清空事务队列中的所有命令。事务回滚例如:
Redis和MySQL事务最大的区别
答:Redis 和 MySQL 事务最大的区别是 Redis 事务本质上是将一组命令加入到队列一次性执行的,但是并没有提供像 MySQL 那样的事务隔离性和事务隔离级别设置。在 Redis 事务执行期间,其他客户端的读写操作仍然可以进行,没有所谓的隔离性。
17.什么是缓存雪崩?如何解决
答:缓存雪崩是指在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。
我们先来看下正常情况下和缓存雪崩时程序的执行流程图,正常情况下系统的执行流程如下图所示:
缓存雪崩的执行流程,如下图所示:
以上对比图可以看出缓存雪崩对系统造成的影响,那如何解决缓存雪崩的问题?
缓存雪崩的常用解决方案有以下几个,但记住一个就好使:
随机化过期时间
为了避免缓存同时过期,可在设置缓存时添加随机时间l这样就可以极大的避免大量的缓存同时失效,示例代码如下:// 缓存原本的失效时间 int exTime = 19 * 69; // 随机数生成类 Random random = new Random(); // 缓存设置 jedis.setex(cacheKey,exTime+random.nextInt(100),value);
18.什么是缓存穿透?如何解决?
答:缓存穿透是指查询数据库和缓存都无数据,因为数据库查询无数据,出于容错考虑,不会将结果保存E/bgakcn到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透缓存穿透执行流程,如下图所示:
解决方法:我不管数据库有没有数据,我都把结果放到Redis里,没有数据也是种结果,下次访问的时候就不会穿透Redis.
19.什么是缓存击穿,如何解决?
答: 缓存击穿指的是某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求将会给数据库造成巨大的压力,这种情况就叫做缓存击穿,缓存击穿的执行流程如下图所示:
解决方案:设置永不过期
对于某些热点缓存,我们可以设置永不过期,这样就能保证缓存的稳定性.
20.缓存雪崩、缓存穿透和缓存击穿有什么区别?
答:首先是三者从定义上来说是不同的,它们的定义如下:
1.缓存穿透是指查询数据库和缓存都无数据,因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透;
2.缓存雪崩是指在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩
3.缓存击穿指的是某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求将会给数据库造成巨大的压力,这种情况就叫做缓存击穿。
小结:也就是说,缓存穿透是因为没有缓存数据导致的问题,而缓存击穿是因为某个热点缓存失效而导致的问题;而缓存雪崩是因为同时有大量缓存失效所带来的问题,这就是三者的区别。
21.Redis 多机部署有几种方式?
答: Redis 多机部署主要有以下3 种方式
1.主从同步:主从同步(主从复制)是 Redis 高可用服务的基石,也是多机运行中最基础的一个。我们把主要存储数据的节点叫做主节点(master),把其他通过复制主节点数据的副本节点叫做从节点(slave)。在 Redis 中一个主节点可以拥有多个从节点,一个从节点也可以是其他服务器的主节点
2.哨兵模式:哨兵模式 Redis Sentinel是 Redis 的一种运行模式,它专注于对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个 Redis 系统的可用性;
3.集群模式(最终形态):集群模式 Redis Cluster 是 Redis 3.0 版本推出的 Redis 集群方案,它将数据分布在不同的服务区上,以此来降低系统对单主节点的依赖,并且可以大大的提高 Redis 服务的读写性能。
主从、哨兵、集群模式有什么区别?
答:主从同步是多机部署最简单的方案,也是存在问题最多的一个方案,它的优点是实现成本低,而这种模式本身存在一个致命的问题,当主节点奔溃之后,需要人工干预才能恢复 Redis 的正常使用。例如,我们有3台服务器做了主从复制,一个主服务器 A 和两个从服务器 B、C,当A 发生故障之后,需要人工把 B服务器设置为主服务器,同时再去 C 服务器设置成从服务器并且从主服务器 B 同步数据,如果是发生在晚上或者从服务器节点很多的情况下,对于人工来说想要立即实现恢复的难度很多,所以我们需要个自动的工具--Redis Sentinel(哨兵模式)来把手动的过程变成自动的,让 Redis 拥有自动容灾恢复(failover)的能力。
哨兵模式如下所示:哨兵模式专注于对 Redis 实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障自动转移和恢复,确保整个 Redis 系统的可用性,所以哨兵模式相比于主从同步多个一个自动容灾恢复的优点
无论是主从还是哨兵模式,它的性能扩展都是有限的,因为主节点只能有一个,而集群模式可以实现多个集群、多主节点的平行扩展,如下图所示:
并且集群模式也具备自动容灾恢复、自主选主的功能,所以集群模式才是 Redis 多机部署的最终形态.