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