redis八股文
2024-06-17 16:45:45 2 举报
AI智能生成
Redis是一种高性能的键值存储系统,其核心内容是处理key-value数据。它是一个开源的项目,基于C语言开发,支持丰富的数据结构,如字符串、哈希表、链表、集合和有序集合。Redis支持主从同步机制和持久化,保证了数据的可靠性和持久性。在构建现代应用程序中,Redis常被用作缓存系统,以加快数据访问速度。同时,Redis的强大功能也使其成为构建实时应用和分布式系统的理想选择。
作者其他创作
大纲/内容
认识redis
什么是redis?
- 内存数据库;
- 提供数据结构(字符串、散列、列表、集合、位图、超日志、地理空间的索引、流)
- 支持:事务 、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片机群模式、
发布/订阅模式,内存淘汰机制、过期删除机制)等等。 - 常用于:缓存,消息队列、分布式锁等场景。
Redis 和 Memcached 有什么区别?
共同点:
不同点:
- 都是内存数据库,一般作为缓存;
- 都有过期策略;
不同点:
- 支持的数据类型不同,redis支持(String、Hash、List、Set、ZSet),而
Memcached 只支持最简单的 key-value 数据类型; - redis支持持久化到磁盘,memcached不支持持久化
- Redis 原生支持集群模式,Memcached 没有原生的集群模式,需要依靠客
户端来实现往集群中分片写入数据; - Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持;
redis为何作为msyql缓存?
redis支持高性能高并发
- 高性能:redis直接操作内存()
- 高并发:Redis 单机的 QPS 能轻松破 10w,
而 MySQL 单机的 QPS 很难破 1w。
redis数据结构
Redis 数据类型以及使用场景分别是什么?
- string字符串:缓存对象、常规计数、分布式锁、共享 session 信息等
- List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行
实现全局唯一 ID;2. 不能以消费组形式消费数据)等 - Hash 类型:缓存对象、购物车等
- Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、
抽奖活动等。 - Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
- BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登
陆状态、连续签到用户总数等; - HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级
网页 UV 计数等; - GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
- Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,
有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。
五种常见的 Redis 数据类型是怎么实现?
String 类型内部实现
否结束,并且 SDS 的所有 API 都会以处理二进制的方式来处理
SDS 存放在 buf[] 数组里的数据。)
- 数据结构:SDS(动态字符串)
- 功能:SDS 不仅可以保存文本数据,还可以保存二进制数据
否结束,并且 SDS 的所有 API 都会以处理二进制的方式来处理
SDS 存放在 buf[] 数组里的数据。)
List 类型
- 数据结构:
双向链表或压缩列表 压缩列表:列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),
列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),
Redis 会使用压缩列表作为 List 类型的底层数据结构;双向列表:不满足压缩列表条件用双向列表- Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了
Hash 类型
- 数据结构:
压缩列表或哈希表,listpack(redis7) - 压缩列表:列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),
- 列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),
- Redis 会使用压缩列表作为 hash类型的底层数据结构;
- 哈希表:不满足压缩列表条件用哈希表
Set 类型
数据结构:哈希表或整数集合
整数集合:如果集合中的元素都是整数且元素个数小于 512 (默认值,set-maxintset-entries配置)个
,Redis 会使用整数集合作为 Set 类型的底层数据结构;
哈希表:如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。
整数集合:如果集合中的元素都是整数且元素个数小于 512 (默认值,set-maxintset-entries配置)个
,Redis 会使用整数集合作为 Set 类型的底层数据结构;
哈希表:如果集合中的元素不满足上面条件,则 Redis 使用哈希表作为 Set 类型的底层数据结构。
Zset类型
- 数据结构:
压缩列表,跳表,listpack (redis7) - 压缩列表:如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,
Redis 会使用压缩列表作为 Zset 类型的底层数据结构; - 跳表:如果有序集合的元素不满足上面的条件,Redis 会使用跳表作为 Zset 类型的底
层数据结构;
redis线程模型
redis是单线程吗?
不是,它是BIO,Redis 在主线程启动的时,是会启动3个后台线程(BIO)+3个i/o线程
3个后台线程:
关闭文件、AOF 刷盘、异步释放 Redis 内存( lazyfree 线程)
后台线程相当于一个消费者,生产者把耗时任务丢到任务队列中,
消费者(BIO)不停轮询这个队列,拿出任务就去执行对应的方法即可
关闭文件、AOF 刷盘、异步释放 Redis 内存( lazyfree 线程)
后台线程相当于一个消费者,生产者把耗时任务丢到任务队列中,
消费者(BIO)不停轮询这个队列,拿出任务就去执行对应的方法即可
BIO_CLOSE_FILE,关闭文件任务队列:当队列有任务后,后台线程会调用 close(fd) ,将文件关闭;
BIO_AOF_FSYNC,AOF刷盘任务队列:当 AOF 日志配置成 everysec 选项后,主线程会把 AOF
写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用 fsync(fd),将 AOF 文件刷盘,
写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用 fsync(fd),将 AOF 文件刷盘,
BIO_LAZY_FREE,lazy free 任务队列:当队列有任务后,后台线程会 free(obj) 释放对象 / free(dict) 删除数据库所
有对象 / free(skiplist) 释放跳表对象;
有对象 / free(skiplist) 释放跳表对象;
Redis 6.0 之后为什么引入了多线程?
虽然 Redis 的主要工作(网络 I/O 和执行命令)一直是单线程模型,但是在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上。
所以为了提高网络 I/O 的并行度,Redis 6.0 对于网络 I/O 采用多线程来处理。但是对于命令的执行,Redis 仍然使用单线程来处理,所以大家不要误解 Redis 有多线程同时执行命令。
Redis 6.0 版本支持的 I/O 多线程特性,默认情况下 I/O 多线程只针对发送响应数据(write client socket),并不会以多线程的方式处理读请求(read client socket)。要想开启多线程处理客户端读请求,就需要把 Redis.conf 配置文件中的 io-threads-do-reads 配置项设为 yes。
所以为了提高网络 I/O 的并行度,Redis 6.0 对于网络 I/O 采用多线程来处理。但是对于命令的执行,Redis 仍然使用单线程来处理,所以大家不要误解 Redis 有多线程同时执行命令。
Redis 6.0 版本支持的 I/O 多线程特性,默认情况下 I/O 多线程只针对发送响应数据(write client socket),并不会以多线程的方式处理读请求(read client socket)。要想开启多线程处理客户端读请求,就需要把 Redis.conf 配置文件中的 io-threads-do-reads 配置项设为 yes。
redis持久化
Redis 如何实现数据不丢失?
Redis 的读写操作都是在内存中,所以 Redis 性能才会高,但是当 Redis 重启后,内存中的数据就会丢失,
那为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到磁盘,
这样在 Redis 重启就能够从磁盘中恢复原有的数据。
Redis 共有三种数据持久化的方式:
AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
混合持久化方式:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;
那为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到磁盘,
这样在 Redis 重启就能够从磁盘中恢复原有的数据。
Redis 共有三种数据持久化的方式:
AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
混合持久化方式:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;
AOF 日志是如何实现的?
AOF 日志:每执行一条写操作命令,先执行命令操作,再把该命令以追加的方式写入到一个文件里;
优化性能:AOF重写机制
优化性能:AOF重写机制
RDB 快照是如何实现的呢?
概念
因为AOF日志记录的是操作命令,不是实际数据,AOF恢复数据时候需要扫描全部日志,性能差。
所以需要RDB,RDB快照是记录某一瞬间的内存数据,将RDB 文件读入内存就可以
所以需要RDB,RDB快照是记录某一瞬间的内存数据,将RDB 文件读入内存就可以
RDB 做快照时会阻塞线程吗?
redis通过两个命令来生成RDB文件
save:主线程生产RDB文件
bgsave:创建子线程然后生产一个RDB文件
save:主线程生产RDB文件
bgsave:创建子线程然后生产一个RDB文件
RDB 在执行快照的时候,数据能修改吗?
可以修改,执行 bgsave 过程中,Redis 依然可以处理操作命令,
因为使用了写时复制技术(Copy-On-Write, COW)
因为使用了写时复制技术(Copy-On-Write, COW)
为什么会有混合持久化?
RDB 优点是数据恢复速度快,但是快照的频率不好把握。频率太低,丢失的数据就会比较多,频率太高,就会影响性能。
AOF 优点是丢失数据少,但是数据恢复不快。
混合持久化的有点:
保证了 Redis 重启速度,又降低数据丢失风险
原理:
在AOF日志重写过程中,Redis首先会fork()一个重写子进程。这个子进程负责将内存中的数据以RDB的格式写入新的AOF文件中,这部分数据形成了AOF文件的前半部分,相当于一个全量的快照。
在重写子进程处理RDB格式数据的同时,主线程继续处理客户端的操作命令。但是,不是直接将这些操作命令写入磁盘,而是先记录在一个重写缓冲区中。这个重写缓冲区的内容以AOF格式保存。
主线程不断处理操作命令,将其写入重写缓冲区,直到AOF文件重写完成。重写缓冲区里的命令以AOF格式写入磁盘。
当子进程将RDB格式的全量数据和主线程记录在重写缓冲区中的增量数据都写入了新的AOF文件后,它会通知主进程。主进程接收到通知后,将新的AOF文件替换旧的AOF文件,这样就完成了AOF日志的重写过程。
这样,AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据,实现了AOF日志的混合持久化。
AOF 优点是丢失数据少,但是数据恢复不快。
混合持久化的有点:
保证了 Redis 重启速度,又降低数据丢失风险
原理:
在AOF日志重写过程中,Redis首先会fork()一个重写子进程。这个子进程负责将内存中的数据以RDB的格式写入新的AOF文件中,这部分数据形成了AOF文件的前半部分,相当于一个全量的快照。
在重写子进程处理RDB格式数据的同时,主线程继续处理客户端的操作命令。但是,不是直接将这些操作命令写入磁盘,而是先记录在一个重写缓冲区中。这个重写缓冲区的内容以AOF格式保存。
主线程不断处理操作命令,将其写入重写缓冲区,直到AOF文件重写完成。重写缓冲区里的命令以AOF格式写入磁盘。
当子进程将RDB格式的全量数据和主线程记录在重写缓冲区中的增量数据都写入了新的AOF文件后,它会通知主进程。主进程接收到通知后,将新的AOF文件替换旧的AOF文件,这样就完成了AOF日志的重写过程。
这样,AOF文件的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据,实现了AOF日志的混合持久化。
Redis 集群
Redis 如何实现服务高可用?
主从复制
概念:
将一台主redis服务器上的数据同步到多台从redis服务器上,保证一主多从,读写分离。
只在主redis服务器上做修改
将一台主redis服务器上的数据同步到多台从redis服务器上,保证一主多从,读写分离。
只在主redis服务器上做修改
哨兵模式
概念:
为了解决主从服务器宕机需要手动恢复的问题,哨兵模式监控主从服务器,提供主从节点的故障迁移功能.
为了解决主从服务器宕机需要手动恢复的问题,哨兵模式监控主从服务器,提供主从节点的故障迁移功能.
哨兵系统架构
哨兵模式两个组件组成:
哨兵节点(Sentinel Nodes):哨兵节点是独立运行的进程,
负责监控Redis主从集群的状态,并执行故障转移和配置更新。
通常会部署多个哨兵节点形成哨兵集群,以实现高可用和故障容错。
Redis主从集群:被监控的Redis集群,包含一个主节点和一个或多
个从节点。主节点负责处理写请求和数据同步,从节点提供读服务并
保持与主节点的数据同步
哨兵节点(Sentinel Nodes):哨兵节点是独立运行的进程,
负责监控Redis主从集群的状态,并执行故障转移和配置更新。
通常会部署多个哨兵节点形成哨兵集群,以实现高可用和故障容错。
Redis主从集群:被监控的Redis集群,包含一个主节点和一个或多
个从节点。主节点负责处理写请求和数据同步,从节点提供读服务并
保持与主节点的数据同步
哨兵模式的工作机制
哨兵模式通过以下几个步骤来实现自动故障检测和故障恢复:
监控与心跳检测:哨兵周期性地向Redis节点发送INFO命令,
获取节点的状态信息。同时,哨兵通过PING命令检测节点的
连通性,如果节点在指定时间内未响应,则标记为下线。
主观下线与客观下线:当一个哨兵节点认为某个节点不可达
时,它会将该节点标记为主观下线(Subjectively Down)。
当足够数量的哨兵节点同意某个节点为主观下线时,该节点被
标记为客观下线(Objectively Down),触发故障转移流程。
故障转移(Failover):
领导者选举:当主节点被标记为客观下线时,哨兵集群进行领
导者选举,选出一个哨兵节点作为故障转移的协调者。
从节点晋升:协调者哨兵节点选择一个从节点晋升为主节点,
执行SLAVEOF NO ONE命令使其脱离旧主节点。
配置更新:协调者哨兵节点向其他哨兵节点和从节点广播新
的主节点信息,更新它们的配置。
数据同步:新主节点开始接受写请求,其他从节点重新连接
新主节点,进行数据同步。
监控与心跳检测:哨兵周期性地向Redis节点发送INFO命令,
获取节点的状态信息。同时,哨兵通过PING命令检测节点的
连通性,如果节点在指定时间内未响应,则标记为下线。
主观下线与客观下线:当一个哨兵节点认为某个节点不可达
时,它会将该节点标记为主观下线(Subjectively Down)。
当足够数量的哨兵节点同意某个节点为主观下线时,该节点被
标记为客观下线(Objectively Down),触发故障转移流程。
故障转移(Failover):
领导者选举:当主节点被标记为客观下线时,哨兵集群进行领
导者选举,选出一个哨兵节点作为故障转移的协调者。
从节点晋升:协调者哨兵节点选择一个从节点晋升为主节点,
执行SLAVEOF NO ONE命令使其脱离旧主节点。
配置更新:协调者哨兵节点向其他哨兵节点和从节点广播新
的主节点信息,更新它们的配置。
数据同步:新主节点开始接受写请求,其他从节点重新连接
新主节点,进行数据同步。
哨兵心跳检测
哨兵故障转移:收到节点下线通知
切片集群模式
概念:
当 Redis 缓存数据量大到一台服务器无法缓存时,就需要使用 Redis
切片集群(Redis Cluster )方案,它将数据分布在不同的服务器上,
以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。
当 Redis 缓存数据量大到一台服务器无法缓存时,就需要使用 Redis
切片集群(Redis Cluster )方案,它将数据分布在不同的服务器上,
以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。
原理:
采用哈希槽,处理数据和节点之间的映射关系,一个切片集群共有 16
384 个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的
key,被映射到一个哈希槽中,具体执行过程分为两大步:
根据键值对的 key,按照 CRC16 算法计算一个 16 bit 的值。
再用 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数
代表一个相应编号的哈希槽。
采用哈希槽,处理数据和节点之间的映射关系,一个切片集群共有 16
384 个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的
key,被映射到一个哈希槽中,具体执行过程分为两大步:
根据键值对的 key,按照 CRC16 算法计算一个 16 bit 的值。
再用 16bit 值对 16384 取模,得到 0~16383 范围内的模数,每个模数
代表一个相应编号的哈希槽。
这些哈希槽怎么被映射到
具体的 Redis 节点上的呢?
具体的 Redis 节点上的呢?
平均分配: Redis 会自动把所有哈希槽
平均分布到集群节点上。比如集群中有 9 个
节点,则每个节点上槽的个数为 16384/9 个。
手动分配: 可以使用 cluster meet 命令手动
建立节点间的连接,组成集群,再使用 cluster
addslots 命令,指定每个节点上的哈希槽个数。
平均分布到集群节点上。比如集群中有 9 个
节点,则每个节点上槽的个数为 16384/9 个。
手动分配: 可以使用 cluster meet 命令手动
建立节点间的连接,组成集群,再使用 cluster
addslots 命令,指定每个节点上的哈希槽个数。
集群脑裂导致数据丢失怎么办?
什么是脑裂?
redis一主多从节点,网络原因导致主从节点之间出现通
信问题,哨兵重新从从节点选出一个作为主节点,网络
恢复后,旧主节点会降级为从节点,再与新主节点进行
同步复制的时候,由于会从节点会清空自己的缓冲区,
所以导致之前客户端写入的数据丢失了。
redis一主多从节点,网络原因导致主从节点之间出现通
信问题,哨兵重新从从节点选出一个作为主节点,网络
恢复后,旧主节点会降级为从节点,再与新主节点进行
同步复制的时候,由于会从节点会清空自己的缓冲区,
所以导致之前客户端写入的数据丢失了。
解决方案
在 Redis 的配置文件中有两个参数我们可以设置:
min-slaves-to-write x,主节点必须要有至少 x 个从节
点连接,如果小于这个数,主节点会禁止写数据。
min-slaves-max-lag x,主从数据复制和同步的延迟不
能超过 x 秒,如果超过,主节点会禁止写数据。
在 Redis 的配置文件中有两个参数我们可以设置:
min-slaves-to-write x,主节点必须要有至少 x 个从节
点连接,如果小于这个数,主节点会禁止写数据。
min-slaves-max-lag x,主从数据复制和同步的延迟不
能超过 x 秒,如果超过,主节点会禁止写数据。
Redis 过期删除与内存淘汰
Redis 使用的过期删除策略是什么?
「惰性删除」
redis对key设置过期时间,key和过期时间存到过期字典里,
查询一个 key 时,如果过期字典不存在这个key正常读取,
反之判断是否过期,过期则删除这个key.
redis对key设置过期时间,key和过期时间存到过期字典里,
查询一个 key 时,如果过期字典不存在这个key正常读取,
反之判断是否过期,过期则删除这个key.
什么是惰性删除策略?
不主动删除过期键,每次从数据库访问 key 时,
都检测 key 是否过期,如果过期则删除该 key。
都检测 key 是否过期,如果过期则删除该 key。
定期删除
redis对key设置过期时间,key和过期时间存到过期字典里
每隔一段时间「随机」从数据库中取出一定数量的 key 进
行检查,并删除其中的过期key。
redis对key设置过期时间,key和过期时间存到过期字典里
每隔一段时间「随机」从数据库中取出一定数量的 key 进
行检查,并删除其中的过期key。
Redis 持久化时,对过期键会如何处理的?
RDB
RDB 文件生成阶段:
从内存状态持久化成 RDB(文件)的时候,会对 key 进行过期检查,
过期的键「不会」被保存到新的 RDB 文件中。
从内存状态持久化成 RDB(文件)的时候,会对 key 进行过期检查,
过期的键「不会」被保存到新的 RDB 文件中。
RDB 加载读取阶段
主服务器载入时候对过期key进行检查,如果过期就不载入
从服务器载入是不对key检查,因为主从服务器做数据同步时
候从服务器数据会被清空
候从服务器数据会被清空
AOF
AOF 文件写入阶段:
当 Redis 以 AOF 模式持久化时,如果数据库某
个过期键还没被删除,那么 AOF 文件会保留此
过期键,当此过期键被删除后,Redis 会向 AOF
文件追加一条 DEL 命令来显式地删除该键值。
当 Redis 以 AOF 模式持久化时,如果数据库某
个过期键还没被删除,那么 AOF 文件会保留此
过期键,当此过期键被删除后,Redis 会向 AOF
文件追加一条 DEL 命令来显式地删除该键值。
AOF 重写阶段:
执行 AOF 重写时,会对 Redis
中的键值对进行检查,已过期的键不会被保存到
重写后的 AOF 文件中,因此不会对 AOF 重写造
成任何影响。
执行 AOF 重写时,会对 Redis
中的键值对进行检查,已过期的键不会被保存到
重写后的 AOF 文件中,因此不会对 AOF 重写造
成任何影响。
Redis 主从模式中,对过期键会如何处理?
从库不会进行过期扫描,主库在 key 到期时,
会在 AOF 文件里增加一条 del 指令,同步到
所有的从库,从库就会删除key
会在 AOF 文件里增加一条 del 指令,同步到
所有的从库,从库就会删除key
Redis 内存满了,会发生什么?
内存达到了某个阀值(maxmemory),触发内存淘汰机制
Redis 内存淘汰策略有哪些?
不进行数据淘汰的策略
内存超过最大设置内存时,返回错误。
进行数据淘汰的策略
在设置了过期时间的数据中进行淘汰
volatile-random:随机淘汰设置了过期时间的任意键值;
volatile-ttl:优先淘汰更早过期的键值。
volatile-lru(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;
volatile-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值;
volatile-ttl:优先淘汰更早过期的键值。
volatile-lru(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;
volatile-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值;
在所有数据范围内进行淘汰
allkeys-random:随机淘汰任意键值;
allkeys-lru:淘汰整个键值中最久未使用的键值;
allkeys-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰整个键值中最少使用的键值。
allkeys-lru:淘汰整个键值中最久未使用的键值;
allkeys-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰整个键值中最少使用的键值。
什么是 LFU 算法?
最近最不常用的,LFU 算法是根据数据访问次数来淘汰数据的,它的核心思想是
“如果数据过去被访问多次,那么将来被访问的频率也更高”。
“如果数据过去被访问多次,那么将来被访问的频率也更高”。
Redis 缓存设计
如何避免缓存雪崩、缓存击穿、缓存穿透?
如何避免缓存雪崩?
缓存雪崩
大量缓存数据在同一时间过期(失效),全部访问数据库,造成数据库宕机,导致崩溃
大量缓存数据在同一时间过期(失效),全部访问数据库,造成数据库宕机,导致崩溃
解决方案
将缓存失效时间随机打散
降低过期时间设置的重复率
降低过期时间设置的重复率
设置缓存不过期
后台服务来更新缓存数据
后台服务来更新缓存数据
如何避免缓存击穿?
缓存击穿
比如秒杀,大量请求一个缓存数据,如果这个数据过期了,
那么大量并发请求会去请求数据库,那么数据库肯能会宕机
比如秒杀,大量请求一个缓存数据,如果这个数据过期了,
那么大量并发请求会去请求数据库,那么数据库肯能会宕机
解决方案
互斥锁方案(Redis 中使用 setNX 方法设置一个状态位,表示这是一种锁定状态)
同一时间只有一个业务线程请求缓存,若是没有锁,要么等待锁,要么返回空。
同一时间只有一个业务线程请求缓存,若是没有锁,要么等待锁,要么返回空。
不给热点数据设置过期时间,由后台异步更新缓存,或者在热点数据准备要过期前,
提前通知后台线程更新缓存以及重新设置过期时间;
提前通知后台线程更新缓存以及重新设置过期时间;
如何避免缓存穿透?
概念:
既不在缓存中,也不再数据库中,大量的请求过来造成后端压力
既不在缓存中,也不再数据库中,大量的请求过来造成后端压力
解决方案
非法请求限制
设置空值或默认值
使用布隆过滤器快速判断数据是否存在,
避免通过查询数据库来判断数据是否存在
设置空值或默认值
使用布隆过滤器快速判断数据是否存在,
避免通过查询数据库来判断数据是否存在
如何设计一个缓存策略,可以动态缓存热点数据呢?
系统不能将所有的数据缓存起来,将其中一部分数据缓存起来,通过数据最新的访问时间做排名,
过滤掉不经常访问的数据,留下经常访问的数据。
举例
要求缓存TOP 1000条商品,先通过缓存做一个排序队列,根据商品访问的时间做排名,时间越近放
在越前面,末尾200商品过滤掉,然后从数据库随机抽取200商品放到队列中。
过滤掉不经常访问的数据,留下经常访问的数据。
举例
要求缓存TOP 1000条商品,先通过缓存做一个排序队列,根据商品访问的时间做排名,时间越近放
在越前面,末尾200商品过滤掉,然后从数据库随机抽取200商品放到队列中。
常见的缓存更新策略?
Cache Aside(旁路缓存)策略
写:先更新数据库然后修改缓存
读:先读缓存,缓存没有再读数据库,然后更新缓存
读:先读缓存,缓存没有再读数据库,然后更新缓存
Read/Write Through(读穿 / 写穿)策略
写:更新缓存,然后通过换成更新数据库
读:数据库数据加载到缓存,然后传给用户
读:数据库数据加载到缓存,然后传给用户
Write Back(写回)策略
写:更新缓存,然后异步更新数据库
Redis 实战
Redis 如何实现延迟队列?
场景
- 在淘宝、京东等购物平台上下单,超过一定时间未付款,订单会自动取消;
- 打车的时候,在规定时间没有车主接单,平台会取消你的单并提醒你暂时没有车主接单;
- 点外卖的时候,如果商家在10分钟还没接单,就会自动取消订单;
概念
有序集合(ZSet)的方式来实现延迟消息队列,ZSet有一个Score属性存储延迟执行的时间
zadd score1 value1 命令就可以一直往内存中生产消息。zrangebysocre 查询符合条件的所有待处理的任务【轮询】
zadd score1 value1 命令就可以一直往内存中生产消息。zrangebysocre 查询符合条件的所有待处理的任务【轮询】
Redis 的大 key 如何处理?
什么是 Redis 大 key?
概念
key 对应的 value 很大
key 对应的 value 很大
举例
- String 类型的值大于 10 KB;
- Hash、List、Set、ZSet 类型的元素的个数超过 5000个;
大 key 会造成什么问题?
- 客户端超时阻塞。由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,
那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。 - 引发网络阻塞。每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB。
- 阻塞工作线程。如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续
的命令。 - 内存分布不均。集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有
大 key 的 Redis 节点占用内存多,QPS 也会比较大。
如何找到大Key
redis-cli -h 127.0.0.1 -p6379 -a "password" -- bigkeys
使用 SCAN 命令查找大 key
rdb dump.rdb -c memory --bytes 10240 -f redis.csv
如何删除大 key?
分批次删除
删除大 Hash
hscan 命令,每次获取 100 个字段,再用 hdel 命令,每次删除 1 个字段。
hscan 命令,每次获取 100 个字段,再用 hdel 命令,每次删除 1 个字段。
删除大 List
ltrim 命令,每次删除少量元素。
ltrim 命令,每次删除少量元素。
删除大 Set
使用 sscan 命令,每次扫描集合中 100 个元素,再用 srem 命令每次删除一个键。
使用 sscan 命令,每次扫描集合中 100 个元素,再用 srem 命令每次删除一个键。
删除大 ZSet
使用 zremrangebyrank 命令,每次删除 top 100个元素。
使用 zremrangebyrank 命令,每次删除 top 100个元素。
异步删除
概念
用 unlink 命令代替 del 来删除
用 unlink 命令代替 del 来删除
四种方法
- azyfree-lazy-eviction:表示当 Redis 运行内存超过 maxmeory 时,是否开启 lazy free 机制删除;
- lazyfree-lazy-expire:表示设置了过期时间的键值,当过期之后是否开启 lazy free 机制删除;
- lazyfree-lazy-server-del:有些指令在处理已存在的键时,会带有一个隐式的 del 键的操作,
比如 rename 命令,当目标键已存在,Redis 会先删除目标键,如果这些目标键是一个 big key,
就会造成阻塞删除的问题,此配置表示在这种场景中是否开启 lazy free 机制删除; - slave-lazy-flush:针对 slave (从节点) 进行全量数据同步,slave 在加载 master 的 RDB 文件前,
会运行 flushall 来清理自己的数据,它表示此时是否开启 lazy free 机制删除。
Redis 管道有什么用?
概念
把多个命令整合到一起发送给服务器端处理之后统一返回给客户端
把多个命令整合到一起发送给服务器端处理之后统一返回给客户端
Redis 事务支持回滚吗?
不支持,Redis 提供了 DISCARD 命令,但是这个命令只能用来主动放弃
事务执行,把暂存的命令队列清空,起不到回滚的效果。。
事务执行,把暂存的命令队列清空,起不到回滚的效果。。
为什么Redis 不支持事务回滚?
错误通常都是编程错误造成的,这种错误通常只会出现在开发环境中
这种复杂的功能和 Redis 追求的简单高效的设计主旨不符合。
不支持事务运行时错误的事务回滚。
如何用 Redis 实现分布式锁的?
概念
并发控制的一种机制,用于控制某个资源在同一时刻只能被一个应用所使用
并发控制的一种机制,用于控制某个资源在同一时刻只能被一个应用所使用
用法
获取锁
使用set命令,并带上 NX(仅在键不存在时设置)和 PX(设置键的过期时间)选项。
使用set命令,并带上 NX(仅在键不存在时设置)和 PX(设置键的过期时间)选项。
释放锁
释放锁时需要确保只有持有锁的客户端才能释放锁,这需要使用 Lua 脚本来保证操作的原子性。
释放锁时需要确保只有持有锁的客户端才能释放锁,这需要使用 Lua 脚本来保证操作的原子性。
Redis 如何解决集群情况下分布式锁的可靠性?
使用Redlock 算法,分别在多个redis实例获取锁,获取到的数量大于等于N/2+1,代表获取锁成功(总时间小于锁的过期时间)
基于 Redis 实现分布式锁有什么优缺点?
0 条评论
下一页