🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
文章目录
5. Redis 作为缓存,MySQL 的数据如何与 Redis 进行同步呢?(双写一致性)
13. 数据库有 1000 万数据,redis 只能缓存 20 万数据。如何保证 redis 中的数据都是热点数据?
18. Redisson 实现的分布式锁能解决主从一致性问题吗?
1. 什么是缓存穿透?是如何解决的?
缓存穿透是指查询一个一定不存在的数据,由于存储层查不到数据,因此不写入到缓存中,这将导致这个不存在的数据每次请求都要到 DB 去查询,可能导致 DB 挂掉。这种情况大概率是遭到了攻击。解决方案的话,一般来说有两种:第一种是缓存空对象;第二种是使用布隆过滤器来解决。
2. 可以介绍一下布隆过滤器吗?
布隆过滤器主要是用来检索一个元素是否在一个集合中。当时使用的是 Redisson 实现的布隆过滤器。它的底层原理是,先是初始化一个比较大的数组,里面存放的是二进制 0 或 1。一开始数组中存放的都是 0,当一个 key 来了之后,经过 3 次 hash 计算,模数组长度找到数组的下标,然后把数组中原来的 0 改为 1。这样,三个数组的位置就能标明一个 key 的存在。查找的过程中也是一样的。当然,布隆过滤器有可能会产生误判,可以设置这个误判率,一般是不超过 5% 。其实这个误判率是必然存在的,要不就增加数组的长度。5% 以内的误判率一般的项目也能接受,不至于高并发下压倒数据库。
3. 什么是缓存击穿?怎么解决?
缓存击穿的意思是,对于设置了过期时间的 Key,缓存在某个时间点过期的时候,恰好这个时间点有大量的请求过来。这些请求发现缓存过期,一般都会从后端 DB 加载数据并会重新将数据写回到缓存中,这也就是缓存重建。对于 DB 来说,大量的请求来进行缓存重建,可能会导致瞬间将 DB 压垮。
解决的方案有两种方式:
第一种方式,使用互斥锁:当缓存失效时,当大量请求来进行缓存重建之前,先获取锁,只有获取锁成功的请求,才能进行缓存重建。而没有获取锁成功的请求,则需要等待一段时间,再去查询缓存,直到缓存重建完成之后,最终才能查询到数据。这种解决的方式符合数据的强一致性,不过会消耗过多的 cpu 资源,导致性能低。
第二种方式,使用 key 逻辑过期:1)先设置 key 的时候,设置一个过期时间字段一块存入缓存中,不给当前 key 设置过期时间;2)当查询的时候,从 redis 取出的数据后判断时间是否过期;3)如果过期了,则开通另外一个线程进行数据同步,当前线程正常返回数据,当然这个数据不是最新的。
两种方案各有利弊:如果选择数据的强一致性,建议使用分布式锁的方案,但性能上可能没有那么高,且有可能会产生死锁的问题。如果选择 key 的逻辑删除,则优先考虑高可用性,性能比较高,但是数据同步做不到强一致性。
4. 什么是缓存雪崩?怎么解决?
缓存雪崩的意思是,设置缓存时采用了相同的过期时间,导致缓存在某一时间同时失效,请求全部转发到 DB,DB 瞬间压力过重而雪崩。与缓存击穿的区别是:雪崩是很多 key,而击穿是某一个 key 缓存。解决方案主要是将缓存失效时间分散开。比如,可以在原有的失效时间基础上增加一个随机值,比如 1 - 5 分钟随机。这样,每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
5. Redis 作为缓存,MySQL 的数据如何与 Redis 进行同步呢?(双写一致性)
需要让数据库与 redis 高度保持一致性,因为要求时效性比较高。当时采用的读写锁保证的强一致性。使用的是 Redisson 实现的读写锁。在读的时候添加共享锁,可以保证读读不互斥、读写互斥。当更新数据的时候,添加排他锁,它是读写、读读都是互斥的,这样就能保证在写数据的同时,是不会让其他线程读取数据的,避免了脏数据。这里面需要注意的是,读方法和写方法上需要使用同一把锁才行。
除了使用 redisson 提供的读写锁来保证数据的同步,还可以使用异步的方案同步数据。允许延时一致的业务,采用异步通知:使用 MQ 中间件,在更新数据之后,通知缓存删除。
6. 排他锁是如何保证读写、读读互斥的呢?
排他锁底层使用的也是 setnx,它保证了同时只能有一个线程操作锁住的方法。
7. 什么是延时双删?为什么不使用它呢?
延时双删,如果是写操作,先把缓存中的数据删除,然后更新数据库,最后再延时删除缓存中的数据。其中,找个延时多久不太好确定。在延时的过程中,可能会出现脏数据,并不能保证强一致性,所以没有采用。
8. redis 作为缓存,数据的持久化是怎么做的?
在 redis 中提供了两种数据持久化的方式:1)RDB;2)AOF
9. 这两种持久化方式有什么区别?
RDB 是一个快照文件。它是把 redis 内存存储的数据写到磁盘上。当 redis 实例宕机恢复数据的时候,可以从 RDB 快照文件中恢复数据。AOF 的含义是追加文件。当 redis 执行写命令的时候,都会存储到找个文件中。当 redis 实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据。
10. 这两个方式,哪种恢复的比较快呢?
RDB 因为是二进制文件,保存时体积比较小,所以恢复得比较快。但是有可能会丢失数据。通常在项目中也会使用 AOF 来恢复数据,虽然 AOF 恢复的速度慢一些,但是丢失数据的风险要小得多。在 AOF 文件中可以设置刷盘策略,可以设置为每秒批量写入一次命令。
11. Redis 的数据过期策略有哪些?
在 redis 中提供了两种数据过期删除策略。第一种是惰性删除。在设置该 key 过期时间后,不去管它。当需要该 key 的时候,首先会判断是否过期,如果过期了,则会删除 key;反之,返回该 key。第二种是定期删除。也就是说,每隔一段时间,就会对 key 进行检查,并删除里面过期的 key。定期清理的两种模式是:1)SLOW 模式,是定时任务,执行频率默认为 10 hz,每次事件循环会尝试执行,每次不超过 25 ms,可以通过修改配置文件 redis.conf 的 hz 选项来调整这个次数;2)FAST 模式,执行频率不固定,每次事件循环会尝试执行,但两次间隔不低于 2ms,每次耗时不超过 1ms。redis 的过期删除策略是:惰性删除 + 定期删除两种策略配合使用。
12. redis 的数据淘汰策略有哪些?
在这个 redis 中提供了很多种,默认是 noeviction,不删除任何数据,内部不足时直接报错。这个可以在 redis 的配置文件中进行设置。里面有两个非常重要概念:一个是 LRU,另一个是 LFU。LRU 的意思是最少最近使用。它会用当前时间减去最后一次访问时间。这个值越大,则优先淘汰优先级越高。LFU 的意思是最少频率使用。它会统计每个 key 的访问频率。值越小,淘汰优先级越高。常用在项目中设置的是 allkeys-lru,它会挑选最近最少使用的数据进行淘汰,把一些经常访问的 key 留在 redis 中。
13. 数据库有 1000 万数据,redis 只能缓存 20 万数据。如何保证 redis 中的数据都是热点数据?
可以使用 allkeys-lru 最近最少使用的淘汰策略。留下来的都是经常访问的热点数据。
14. redis 的内存用完了会发生什么?
这个需要看 redis 的数据淘汰策略是什么。如果是默认的配置,redis 内存用完之后,则直接报错。当时设置的是 allkeys-lru 策略,把最近最常访问的数据留在缓存中。
15. redis 分布式锁如何实现?
在 redis 中提供了一个命令 setnx。由于 redis 是单线程的,用了这个命令之后。只能有一个客户端对某一个 key 设置值。在没有过期或删除 key 的时候,其他客户端时不能设置这个 key 的。
16. 如何控制 redis 实现分布式锁的有效时长?
redis 的 setnx 指令不好控制这个问题。可以采用 redis 的一个框架 redisson 实现的。在 redisson 中需要手动加锁,并且可以控制锁的失效时间和等待时间。当锁住的一个业务还没有执行完成的时候,Redisson 会引入一个看门狗机制,就是说,每隔一段时间就检查当前业务是否还持有锁。如果持有,就增加加锁的持有时间。当业务执行完成之后,需要使用释放锁就可以了。还有一个好处就是,在高并发下,一个业务可能会执行很快。客户 1 持有锁的时候,客户 2 来了以后并不会马上被拒绝,它会自旋不断尝试获取锁。如果客户 1 释放之后,客户 2 就可以马上持有锁,性能也得到了提升,这就是可重试机制。
17. Redisson 实现的分布式锁是可重入的吗?
是可以重入的,这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计数上减 1。在存储数据的时候采用的 hash 结构,大 key 可以按照自己的业务进行定制,其中小 key 是当前线程唯一标识, value 是当前线程重入的次数。
18. Redisson 实现的分布式锁能解决主从一致性问题吗?
不能解决主从一致性问题。比如,当线程 1 加锁成功后,master 节点数据会异步复制到 slave 节点,此时如果当前持有 redis 锁的 master 节点宕机,slave 节点被提升为新的 master 节点,假如现在来了一个线程 2,再次加锁,会在新的 master 节点上加锁成功,这个时候就会出现两个节点同时持有一把锁的问题了。
可以利用 redisson 提供的红锁来解决这个问题,它的主要作用是,不能只在一个 redis 实例上创建锁,而是在多个 redis 实例上创建锁,并且要求在大多数 redis 节点上都成功创建锁,红锁中要求是 redis 的节点数量要过半。这样就能避免线程 1 加锁成功后 master 节点宕机导致线程 2 成功加锁到新的 master 节点上的问题了。
但是,如果使用了红锁,因为需要同时在多个节点上都添加锁,性能就变得非常低,并且运维维护成本也非常高,所以,一般在项目中也不会直接使用红锁,并且官方也暂时废弃这个红锁。
19. Redis 集群有哪些方案,知道吗?
在 redis 中提供的集群方案总共有三种:主从复制、哨兵模式、Redis 分片集群。
20. 可以介绍一下主从同步吗?
单节点的 redis 的并发能力是有上限的,要进一步提高 redis 的并大能力,可以搭建主从集群,实现读写分离。一般都是一主多从,主节点负责写数据,从节点负责读数据,主节点写入数据之后,需要把数据同步到从节点中。
21. 可以说一下主从同步数据的流程吗?
主从同步分为两个阶段,一个是全量同步,一个是增量同步。
全量同步时指从节点第一次与主节点建立连接的时候使用全量同步,流程是这样的:
1)从节点请求主节点同步数据,其中从节点会携带自己的 replication id 和 offset 偏移量。
2)主节点判断是否是第一次请求,主要判断的依据就是,主节点与从节点是否是同一个 replication id,如果不是,就说明是第一次同步,那主节点就会把自己的 replication id 和 offset 发送给从节点,让从节点与主节点的信息保持一致。
3)同时主节点会执行 bgsave,生成 RDB 文件后,发送给从节点去执行,从节点先把自己的数据清空,然后执行主节点发送过来的 RDB 文件,这样就保持了一致。
4)当然,如果在生成 RDB 文件期间,依然有请求到主节点,而主节点会以命令的方式记录到缓冲区,缓冲区是一个日志文件,最后把这个日志文件发送给从节点,这样就能保证主节点与从节点的数据完全一致了,后期再同步数据的时候,都是依赖这个日志文件,这就是全量同步。
增量同步指的是,当从节点服务重启之后,数据就不一致了,所以这个时候,从节点会请求主节点同步数据,主节点根据 replication id 进行判断,判断的结果不是第一个请求,就直接获取 offset 值,然后主节点从命令日志文件中获取 offset 值之后的数据,发送给从节点进行数据同步。
22. 怎么保证 Redis 的高并发高可用?
首先可以搭建主从集群,再加上使用 redis 中的哨兵模式,哨兵模式可以实现主从集群的自动故障恢复,里面就包含了:监控、自动故障恢复、通知;如果 master 故障,sentinel 会将一个 slave 提升为 master。当故障恢复后也是以新的 master 为主;同时 sentinel 也充当 redis 客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给 redis 客户端,所以一般项目都会采用哨兵模式来保证 redis 的高并发高可用。
23. Redis 集群脑裂,该怎么解决呢?
现在使用的是 Redis 的哨兵模式集群的。有的时候由于网络等原因可能会出现脑裂的情况,就是说,由于 Redis master 节点和 Redis slave 节点和 Sentinel 处于不同的网络分区,使得 sentinel 没有能够心跳感知到 master,所以通过选举的方式提升一个 slave 为 master,这样就存在两个 master,就像大脑分裂了一样,这样会导致客户端还在 old master 那里写入数据,新节点无法同步数据,当网络恢复后,sentinel 会将 old master 降为 salve,这是再从新 master 同步数据,这会导致 old master 的大量数据丢失。
关于解决的话,可以再 redis 的配置中设置:第一可以设置最小的 slave 节点个数,比如设置至少要有一个从节点才能同步数据;第二可以设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量数据丢失了。
24. Redis 的分片集群有什么作用?
分片集群主要解决的是海量数据存储问题,集群中有多个 master,每个 master 保存不同的数据,并且还可以给每个 master 设置多个 slave 节点,就可以继续增大集群的高并发能力。同时每个 master 之间通过 ping 监测彼此健康状态,就类似于哨兵模式了。当客户端请求可以访问集群任意节点,最终都会被转发到正确的节点。
25. Redis 分片集群中数据是怎么存储和读取的?
在 redis 集群引入了哈希槽的概念,有 16384 个哈希槽,集群中每个主节点绑定了一定范围的哈希槽范围,key 通过 CRC16 校验后,再进行对 16384 取模来决定放置哪个槽,通过槽找到对应的节点进行存储。取值的逻辑也是一样的。
26. Redis 是单线程的,但是为什么还那么快?
主要是因为:1)完全基于内存的,c 语言编写的;2)采用单线程,避免了不必要的上下文切换和竞争条件。;3)使用多路 I/O 复用模型。
比如:bgsave 和 bgrewriteaof 都是在后台执行操作,不影响主线程的正常使用,不会产生阻塞。
27. 能解释一下 I/O 多路复用模型?
I/O 多路复用是指利用单个线程来同时监听多个 socket,并且在某个 socket 可读、可写时得到通知,从而避免无效的等待,充分利用 cpu 资源。目前的 I/O 多路复用都是采用 epoll 模式实现,它会在通知用户进程 socket 就绪的同时,把已就绪的 socket 写入到用户空间,不需要挨个遍历 socket 来判断是否就绪,提升了性能。
其中 Redis 的网络模型就是使用 I/O 多路复用结合事件的处理器来应对多个 socket 请求,比如,提供了连接应答处理器、命令回复处理器、命令请求处理器;
在 Redis6.0 之后,为了提升更好的性能吗,在命令回复处理器使用了多线程来处理回复事件,在命令请求处理器中,将命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程。
希望文章可以帮助到您,如果还需要想详细了解更多 Redis 内容,可以关注一下:Redis 篇_小扳的博客-CSDN博客
转载自CSDN-专业IT技术社区
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Tingfeng__/article/details/145438558