思维导图知识点整理:
一、认识 Redis
1、Redis是什么?
Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景。有多种数据类型,并且对数据类型的操作都是原子性的,因为执行命令由单线程负责的,不存在并发竞争的问题。(除此之外,Redis 还支持事务 、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片机群模式)、发布/订阅模式,内存淘汰机制、过期删除机制等等)
2、Redis和Memcached有什么区别?
很多人都说用 Redis 作为缓存,但是 Memcached 也是基于内存的数据库,为什么不选择它作为缓存呢?要解答这个问题,我们就要弄清楚 Redis 和 Memcached 的区别。 Redis 与 Memcached 共同点:
- 都是基于内存的数据库,一般都用来当做缓存使用。
- 都有过期策略。---康
- 两者的性能都非常高。
Redis 与 Memcached 区别:
- Redis 支持的数据类型更丰富(String、Hash、List、Set、ZSet),而 Memcached 只支持最简单的 key-value 数据类型;
- Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 没有持久化功能,数据全部存在内存之中,Memcached 重启或者挂掉后,数据就没了;
- Redis 原生支持集群模式,Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;
- Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持;
3、为什么用 Redis 作为 MySQL 的缓存?
mysql是将数据存储在磁盘中,当大量的高并发访问到来时,mysql会因为频繁的IO导致数据响应慢或无法响应的问题,而redis是基于内存的数据库,天然支持高并发的访问,高性能的支持,QPS是mysql的数十倍倍,完全可以将高频数据放入redis来提高程序的响应速度,减轻mysql的压力。
二、Redis数据结构
问题的答案看链接。
补充的知识点:
SDS:简单动态字符串,是 Redis 中的一种字符串类型,它是 Redis 用来做字符串的底层实现。
listpack:在Redis中,listpack是一种用来取代ziplist的数据结构,从5.0版本开始引入。
压缩列表:压缩列表是一种数据结构,旨在节省内存。Redis使用压缩列表作为列表和哈希表键值对的底层实现。ziplist,有连锁更新的性能问题,所以之后的redis版本使用listpack紧凑的列表实现,但这消耗了更多的内存。
quicklist:quicklist是Redis中一种新型的数据结构,它是由双向链表和压缩列表或listpack共同构成的。在Redis 3.2之后,List对象的底层是由quicklist实现的。
跳表:跳表是一种基于链表的数据结构,可以用来快速地查找元素。跳表的实现使用了空间换时间的思想,通过增加索引层来加速查找的速度。
三、Redis线程模型
1、Redis是单线程吗?
Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。但redis程序并不是单线程的,启动redis后,会启动后台线程来处理一些耗时任务,而不去打扰主线程,防止主线程阻塞(更详细的解答见链接)
2、为什么redis6.0之前使用单线程?
我们都知道单线程的程序无法使用服务器的多核cpu,那么为什么还要使用单线程呢?
原因:官方文档告诉我们cpu并不是限制redis性能的瓶颈,限制redis瓶颈的是内存大小和网络IO,单线程同样可以使用多核cpu,利用分片集群。使用了单线程后,可维护性高,多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。
3、为什么redis6.0之后又引入了多线程?
在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上。
但是对于命令的执行,Redis 仍然使用单线程来处理,所以大家不要误解 Redis 有多线程同时执行命令。综合2,3回答:redis执行命令还是使用的单线程,引入多线程是为了性能的提升。
四、Redis持久化
1、AOF 日志是如何实现的?
执行完写命令后,将写命令写入AOF文件,重启redis时加载这个文件,重载数据
2、为什么先执行命令,再把数据写入日志呢?
如果我们先将命令写入日志,那么执行命令可能会有延迟;或者万一命令有错,写入的就是错误的命令,先执行命令还可以将命令顺便检查一遍
3、AOF 写回策略有几种?
Always:执行完命令同步将命令写入AOF文件。
Everysec:先将命令写入内核缓冲区,然后每秒将缓冲区的内容写进硬盘。
NO:由操作系统控制何时将缓冲区的内容写进硬盘。
4、AOF 日志过大,会触发什么机制?
AOF重写机制,当AOF文件的大小超过设定的阈值就会触发。
5.什么是RDB快照?
将某一时刻的内存数据,以二进制的方式写入磁盘。
6.RDB快照是如何实现的?
由于AOF日志增多,会造成redis恢复数据缓慢,所以RDB就来了,记录一瞬间的内存真实数据,直接读入内存恢复数据。
7.RDB做快照时会阻塞线程吗?
看你用哪个命令生成RDB文件,save命令通过主线程生成RDB文件,会阻塞。bgsave命令通过创建子进程来生成RDB文件,不会阻塞。此外我们还可以通过配置来控制快照的频率。
8.RDB 在执行快照的时候,数据能修改吗?
可以的。具体怎么修改可以在用到时做具体了解。
9.什么是混合持久化?
Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点。
10.为什么会有混合持久化?
解决AOF恢复慢,RDB数据丢失风险高的缺点。并结合它们的优点,让redis启动更快,丢失数据的风险降低
五、Redis集群
1.redis如何实现服务高可用?
要想设计一个高可用的 Redis 服务,一定要从 Redis 的多服务节点来考虑,比如 Redis 的主从复制、哨兵模式、切片集群。
(1)主从复制
""" 从一台主redis服务器同步数据到多台从服务器,即一主多从,主从服务器采用读写分离的模式,主一般写入,从一般读取。 主服务器写入数据(一个线程),同步数据到从服务器(另一个线程),这个过程是异步的,但是有时间数据还没同步,所以强一致性是不可能,只能是解决最终一致性问题,降低数据不一致的概率 """(2)哨兵模式
""" 当 Redis 的主从服务器出现故障宕机时,需要手动进行恢复。为了解决这个问题,Redis 增加了哨兵模式,因为哨兵模式做到了可以监控主从服务器,并且提供主从节点故障转移的功能。 """(3)切片集群模式
""" 当 Redis 缓存数据量大到一台服务器无法缓存时,就需要使用 Redis 切片集群(Redis Cluster )方案,它将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。 """
2.集群脑裂导致数据丢失怎么办?
什么是脑裂?
""" 出现两个主服务器,一个旧的一个新的 """为什么会导致数据丢失?
""" redis内部网络出现问题,主服务器与从服务器失联,实际上主服务器还可以使用,并且依旧与客户端交互,客户端的写入依旧在主服务器;这时哨兵察觉不对,认为是不能使用了,就将一个从作为主,这时就出现了两个主,等网络好了,那么新的主就会替换掉旧的主,旧的主变为从,从在第一次同步数据时会清空本地数据,所以造成了之间写入这个从的数据丢失。 """
六、Redis过期删除与内存淘汰
1.redis使用的过期策略是什么?
redis有一个过期字典(保存了数据库所有key的过期时间),若key设置了过期时间,redis会将key并带有其过期时间存在过期字典中。当我们查找key,redis首先判断该key是否有过期时间,没有则返回值;有,就会到过期字典里查找,与系统时间对比判断是否过期,过期则返回null,并删除key。
Redis 使用的过期删除策略是「惰性删除+定期删除」这两种策略配和使用。
(1)什么是惰性删除?
""" 不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key,返回null。
对cpu友好,对内存不友好。 """(2)什么是定期删除?
""" 每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。 Redis 的定期删除的流程: 1.从过期字典中随机抽取 20 个 key; 2.检查这 20 个 key 是否过期,并删除已过期的 key; 3.如果本轮检查的已过期 key 的数量,超过 5 个(20/4),也就是「已过期 key 的数量」占比「随机抽取 key 的数量」大于 25%,则继续重复步骤 1;如果已过期的 key 比例小于 25%,则停止继续删除过期 key,然后等待下一轮再检查。 可以看到,定期删除是一个循环的流程。那 Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过 25ms,超过就直接结束 """
2.Redis 持久化时,对过期键会如何处理的?
RDB快照:
(1)在生成RDB时会对键进行检查,若过期则不写入RDB
(2)在加载RDB时,要看是主服务器还是从服务器。主服务器:加载RDB,并检查键是否过期,过期不载入。从服务器:过不过期都会载入,因为主服务器同步数据时,从服务器数据也会清空,不会造成什么影响
AOF:
(1)在写入时,若数据库有过期键还是会写入,当删除时,redis会向AOF追加一条DEL命令来删除
(2)在重写时,redis会检查键,过期不会重写进AOF
3.Redis 主从模式中,对过期键会如何处理?
从库对过期键的处理依赖主库,主库检查过期键,删除后同步到从库。也就是说,若从库的键过期,我们依然能访问到。从库不会对键进行过期扫描
4.Redis 内存满了,会发生什么?
会触发内存淘汰机制。就是说当内存运行达到某个阈值触发,redis的配置文件中选项maxmemory进行配置
5.Redis 内存淘汰策略有哪些?
Redis 内存淘汰策略共有八种,这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略。
不进行数据淘汰:运行内存超过设置的最大阈值,不提供服务,直接返回错误信息进行数据淘汰:可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略,比较好的就是淘汰设置了过期时间并且使用率最少的键
七、Redis缓存设计
1.如何避免缓存雪崩?
为了数据的一致性,通常我们会对缓存的数据设置过期时间,过期了就去数据库请求,重新更新缓存
缓存雪崩:当缓存大面积失效(同一时间许多键过期),大量并发请求直接到了数据库,压力增大,甚至会出现宕机,造成整个系统问题,这就是雪崩问题
解决:
(1)将过期时间打散,减少同一时间,大面积的键过期
(2)不设置过期时间,通过后台服务更新缓存
2.如何避免缓存击穿?
当热频数据过期,此时瞬时大量请求此热点数据,请求直接到了数据库,为缓存击穿。与缓存雪崩类似,可以看成雪崩的一个子集。
解决:
(1)热点数据不设置过期时间,由后台异步更新,或者要过期了,提前通知后台线程进行过期时间更新
(2)使用互斥锁方案,保证同一时间只有一个业务线程请求缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值
3.如何避免缓存穿透?
缓存雪崩和击穿,数据库都保留了相关数据,只要缓存恢复,就能减轻数据库压力,而穿透不一样。
缓存穿透:访问不存在的数据,请求会到达数据库,当瞬时大量访问,数据库压力增大
一般分两种情况:
- 业务误操作,缓存中的数据和数据库中的数据都被误删除了,所以导致缓存和数据库中都没有数据;
- 黑客恶意攻击,故意大量访问某些读取不存在数据的业务;
解决:
(1)非法请求的限制。api接口处判断参数是否合理,不合理直接拦截。
(2)在缓存中设置空值或默认值。
(3)使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在。
4.如何设计一个缓存策略,可以动态缓存热点数据呢?
通过数据最新访问时间来做排名(或者记录次数),并过滤掉不常访问的数据,只留下经常访问的数据。
5.说说常见的缓存更新策略?
常用的缓存有三种:
- Cache Aside(旁路缓存)策略;
- Read/Write Through(读穿 / 写穿)策略;
- Write Back(写回)策略;
实际开发中,Redis 和 MySQL 的更新策略用的是 Cache Aside,另外两种策略应用不了。
为了数据一致性一定要先更新数据库,在更新缓存,因为缓存的写入通常要远远快于数据库的写入。
Cache Aside 策略适合读多写少的场景,不适合写多的场景,因为写多频繁清理缓存,命中率下降,所以要考虑一些对命中率比较严格的业务,思考如何解决。
redis分布式锁作用及其使用场景?
Redis加分布式锁的主要目的是为了解决在分布式系统中多个进程或线程访问共享资源时可能出现的并发问题,避免出现数据不一致的情况。
使用分布式锁可以保证在同一时刻只有一个进程或线程能够访问共享资源,从而避免并发访问造成的问题。Redis提供的分布式锁通常基于Redis的原子操作SETNX(set if not exist)实现,通过给定一个唯一标识作为value值,在key不存在时设置key的值,从而获取锁。
分布式锁通常用于以下场景:
防止重复执行:如果在分布式系统中有多个进程需要执行某个任务,但是这个任务需要保证只能被执行一次,那么可以使用分布式锁来保证只有一个进程可以执行该任务。
保证数据一致性:在对共享资源进行修改的时候,需要保证所有对该资源的修改都能够同步到其他节点,否则可能会导致数据不一致。此时可以使用分布式锁来保证同一时刻只有一个进程可以修改共享资源,从而保证数据一致性。
控制流量:当一个服务涉及到很多请求时,可能会造成服务雪崩。此时可以使用分布式锁来控制请求的流量,避免瞬间大量请求导致服务无法响应。
总之,分布式锁是一种重要的分布式协作机制,在分布式系统中广泛应用于并发控制、数据一致性和流量控制等场景。
参考链接:https://xiaolincoding.com/redis/base/redis_interview.html#%E8%AE%A4%E8%AF%86-redis