当前位置: 首页 > news >正文

【Redis】常见面试题

什么是Redis?

Redis 和 Memcached 有什么区别? 

为什么用 Redis 作为 MySQL 的缓存? 

主要是因为Redis具备高性能和高并发两种特性。

高性能:MySQL中数据是从磁盘读取的,而Redis是直接操作内存,速度相当快。

高并发:单台设备的Redis的QPS(每秒钟处理完请求的次数)是MySQL的十倍,Redis单机的QPS能轻松破10w,而MySQL单机的QPS很难破1w。所以,直接访问Redis能够承受的请求是远远大于直接访问MySQL的。

Redis数据类型

介绍Redis数据结构之前,先来介绍以下Redis对象系统。

什么是Redis对象系统?

Redis中基于双向链表、简单动态字符串、字典、跳跃表、整数集合、压缩列表、快速列表等数据结构实现了一个对象系统,并且实现了5种不同的对象,每种对象都使用了至少一种前面的数据结构,优化对象在不同场合下的使用效率。

对象结构 RedisObject 包含:type、encoding、lru、refcount、*ptr,下面逐一介绍:

type:表示对象的数据类型,包括字符串类型、列表类型、集合类型、有序集合类型、哈希类型,占4位。

encoding:对象的编码类型,占4位,标识了该对象使用了哪种底层的数据结构。

lru:该字段是一个时间戳,表示对象最近一次被访问的时间,用于实现内存管理。当 Redis 设置了最大内存限制并需要进行逐出策略时,LRU 被用来决定哪些数据应该被淘汰。

refcount:表示对该 RedisObject 的引用计数,当某个操作引用这个对象时,refcount 会加1;当引用被释放时,refcount减1。引用计数为0时,对象会被销毁,释放内存。

*ptr:指向底层数据实现的指针。

对象结构的功能?

  • 为5种不同的对象类型提供同一的表示形式。
  • 针对不同的场景,Redis 支持同一种对象类型使用多种不同的数据结构。
  • 支持引用计数,实现对象共享机制。
  • 记录对象的访问时间,便于删除对象。

字符串对象底层实现:

列表对象底层实现:

集合对象底层实现:

哈希对象底层实现:

有序集合对象底层实现:

Redis常见数据类型和应用场景

String

介绍
内部实现
String 类型的底层的数据结构实现主要是 int SDS (简单动态字符串)。

具体如下:

常用命令

普通字符串的基本操作:

批量设置:

计数器(字符串的内容为整数的时候可以使用): 

过期(默认为永不过期):

不存在就插入:

应用场景

1.缓存对象

2.常规计数

因为 Redis 处理命令是单线程,所以执行命令的过程是原子的。因此 String 数据类型适合计数场
景,比如计算访问次数、点赞、转发、库存数量等等。

比如计算文章的阅读量:

3.分布式锁

4.共享 Session 信息

List

介绍

内部实现

常用命令

应用场景

消息队列

消息队列在存取消息时,必须要满足三个需求,分别是 消息保序、处理重复的消息和保证消息可
靠性。
1.如何满足消息保序需求?

2.如何处理重复的消息?

3.如何保证消息可靠性?

总结:

list 作为消息队列有什么缺陷?

list 不支持多个消费者消费同一条消息。Redis 从 5.0 版本开始提供 Stream 数据类型,同样满足消息队列三大需求,而且它还支持多个消费者消费同一条消息。

Hash

介绍

内部实现 

常用命令

应用场景

1.缓存对象

2.购物车

以用户 id 为 key,商品 id 为 field,商品数量为 value,恰好构成了购物车的3个要素。

Set

介绍

内部实现

常用命令

Set 常用操作

Set 运算操作

交集运算:

并集运算:

差集运算:

 

应用场景

1.点赞

Set 类型可以保证一个用户只能点一个赞。

2.共同关注

Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等。

3.抽奖活动

存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次。

Zset

介绍

内部实现

常用命令

Zset 常用操作

Zset 运算操作(相比于 Set 类型,ZSet 类型没有支持差集运算

应用场景

1.排行榜

有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名等。

2.电话、姓名排序

获取所有号码: 

 ZRANGEBYLEX phone - +

 获取 132 号段的号码:

ZRANGEBYLEX phone [132 (133

 获取132、133号段的号码:

ZRANGEBYLEX phone [132 (134

BitMap

介绍

内部实现

常用命令

bitmap 基本操作

bitmap 运算操作

# BitMap间的运算
# operations 位移操作符,枚举值AND 与运算 &OR  或运算 |XOR 异或 ^NOT 取反 ~
# result 计算的结果,会存储在该key中
# key1 … keyn 参与运算的key,可以有多个,空格分割,not运算只能一个key
# 当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。返回值是保存到 destkey 的字符串的长度(以字节byte为单位),和输入 key 中最长的字符串长度相等。BITOP [operations] [result] [key1] [keyn…]
# 返回指定key中第一次出现指定value(0/1)的位置
BITPOS [key] [value]
应用场景

1.签到统计

2.判断用户登录状态

3.连续签到的用户总数

如何统计出连续 7 天打卡的用户总数呢?

我们把每天的日期作为 Bitmap 的 key,userId 作为 offset,若是打卡则将 offset 位置的 bit 设置成 1。key 对应的集合的每个 bit 位的数据则是一个用户在该日期的打卡记录。

一共有 7 个这样的 Bitmap,对这 7 个 Bitmap 对应的 bit 位做与运算。如果最终 userId 对应的 bit 位为 1,就说明该用户 7 天连续打卡。将结果保存到一个新 Bitmap 中,我们再通过 BITCOUNT 统计 bit = 1 的个数便得到了连续打卡 7 天的用户总数了。

HyperLogLog

介绍

内部实现
常用命令

127.0.0.1:6379>pfadd uv1 a b c d e#uv1中5个元素:[a,b,c,d,e]
(integer) 1
127.0.0.1:6379>pfcount uv1 #uv1中数量为5
(integer)5
127.0.0.1:6379>pfadd uv2 b c d e f #uv2中5个元素:[b,c,d,e,f]
(integer) 1
127.0.0.1:6379>pfcount uv2 #uv2中数量为5
(integer)5
127.0.0.1:6379>pfcount uv1 uv2#获取uv1和uv2去重之后数量合集:[a,b,c,d,e,f], 数量为6
(integer)6
应用场景

百万级网页 UV(独立访客)计数

Redis HyperLogLog 优势在于只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数,和元
素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。

在统计 UV 时,可以用 PFADD 命令(用于向 HyperLogLog 中添加新元素)把访问页面的每个用户都添加到 HyperLogLog 中。

PFADD page1:uv user1 user2 user3 user4 user5

接下来,就可以用 PFCOUNT 命令直接获得 page1 的 UV 值了,这个命令的作用就是返回
HyperLogLog 的统计结果。

PFCOUNT page1:uv

GEO

介绍

内部实现

常用命令
# 存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。
GEOADD key longitude latitude member [longitude latitude member ...]# 从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
GEOPOS key member [member ...]# 返回两个给定位置之间的距离。
GEODIST key member1 member2 [m|km|ft|mi]# 根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。单位:[m|km|ft|mi]->[米|干米|英里|英尺]
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
应用场景

滴滴叫车

Stream

介绍

内部实现
常用命令

应用场景

消息队列

注:前面介绍的这些操作 List 也支持。

Stream 特有功能

Stream 可以使用 XGROUP 创建消费组,创建消费组之后,Stream 可以使用 XREADGROUP
令让消费组内的消费者读取消息。

消息队列中的消息一旦被消费组里的一个消费者读取了,就不能再被该消费组内的其他消费者读
取了,即同一个消费组里的消费者不能消费同一条消息
但是,不同消费组的消费者可以消费同一条消息但是有前提条件,创建消息组的时候,不同消
费组指定了相同位置开始读取消息)。
基于 Stream 实现的消息队列,如何保证消费者在发生故障或宕机再次重启后,仍然可以读
取未处理完的消息?
Streams 会自动使用内部队列(也称为 PENDING List)留存消费组里每个消费者读取的消息,直
到消费者使用 XACK 命令通知 Streams“消息已经处理完成”。
消费确认增加了消息的可靠性,一般在业务处理完成之后,需要执行 XACK 命令确认消息已经被
消费完成。如果消费者没有成功处理消息,它就不会给 Streams 发送 XACK 命令,消息仍然会留存。此时,消费者可以在重启后,用 XPENDING 命令查看已读取、但尚未确认处理完成的消息。
一旦消息被消费者处理了,消费者就可以使用 XACK 命令通知 Streams,然后这条消息就会被删除。
总结:
Redis 基于 Stream 消息队列与专业的消息队列有哪些差距?
一个专业的消息队列,必须做到两大块:
  • 消息不丢
  • 消息可堆积

Redis 发布/订阅机制为什么不可以作为消息队列?

总结

Redis数据结构

SDS

C语言 char* 字符数组的缺陷

SDS 做了哪些优化? 

总结:Redis 的 SDS 结构是在原本字符数组基础上,增加了三个元数据:len、alloc、flags,用来解决 C 语言字符串的缺陷。

具体来说 SDS 的优势:

SDS 扩容规则: 

比如 sdshdr16 和 sdshdr32 这两个类型,它们的定义分别如下:

这样设计有什么好处?

举例:使用编译优化,由原来的 8 字节变为 5 字节。

链表

Redis 的 List 对象的底层实现之一就是链表。下面我们来看一下链表的结构设计:

链表的优点

1.listNode 链表节点的结构里带有 prev 和 next 指针,获取某个节点的前置节点或后置节点的时
间复杂度只需O(1)
,而且这两个指针都可以指向 NULL,所以链表是无环链表;

2.list 结构因为提供了表头指针 head 和表尾节点 tail,所以获取链表的表头节点和表尾节点的时
间复杂度只需O(1)

3.list 结构因为提供了链表节点数量 len,所以获取链表中的节点数量的时间复杂度只需O(1)

4.listNode 链表节点使用 void* 指针保存节点值,并且可以通过 list 结构的 dup、free、match 函数
指针为节点设置该节点类型特定的函数,因此链表节点可以保存各种不同类型的值

链表的缺陷

1.链表每个节点之间的内存都是不连续的,意味着无法很好利用 CPU 缓存。能很好利用 CPU 缓
存的数据结构是数组,因为数组的内存是连续的,这样就可以充分利用 CPU 缓存来加速访问。
2.保存一个链表节点的值都需要一个链表节点结构头的分配, 内存开销较大

压缩列表

zlbytes:占4个字节,记录整个压缩列表占用的内存字节数(总字节数)。

zltail:占4个字节,记录压缩列表尾节点 entryN 距离压缩列表的起始地址的字节数。

zllen:占2个字节,记录了压缩列表的节点数量。

entry[1-N]:长度不定,保存数据。

zlend:占1个字节,标记压缩列表的末端,固定值 0xFF(十进制255)。

举例:假如有三个节点: 

注:如果 prevlen 属性的长度为 5 字节,那么第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度。 

举例说明: 

连锁更新

总结压缩列表的缺陷

quicklist

typedef struct quicklist {//quicklist的链表头quicklistNode* head;//quicklist的链表尾quicklistNode* tail;//所有压缩列表中的总元素个数unsigned long count;//quicklistNodes的个数unsigned long len;...
} quicklist;
typedef struct quicklistNode {//前一个quicklistNodestruct quicklistNode* prev;//后一个quicklistNodestruct quicklistNode* next;//quicklistNode指向的压缩列表unsigned char* zl;//压缩列表ziplist的总长度unsigned int sz;//压缩列表ziplist中的元素个数unsigned int count : 16;....
} quicklistNode;

用一张图更好的理解一下: 

listpack

listpack 结构设计

哈希表

Redis哈希表结构如下:

哈希表节点结构如下:

dictEntry 结构里不仅包含指向键和值的指针,还包含了指向下一个哈希表节点的指针,这个指针
可以将多个哈希值相同的键值对连接起来,以此来解决哈希冲突的问题,这就是链式哈希。

除此之外,dictEntry 结构里键值对中的值是一个「联合体 v」定义的,因此,键值对中的值可以是一个指向实际值的指针,或者是一个无符号的 64 位整数或有符号的 64 位整数或 double 类型的值。这么做的好处是可以节省内存空间,因为当「值」是整数或浮点数时,就可以将值的数据内嵌在 dictEntry 结构里,无需再用一个指针指向实际的值,从而节省了内存空间。

哈希冲突

什么是哈希冲突?

哈希表实际上是一个数组,数组里每一个元素就是一个哈希桶。当一个键值对的键经过 Hash 函数计算后得到哈希值,再经过(哈希值 % 哈希表大小),得到的结果值就是该 key-value 对应的数组元素位置,也就是第几个哈希桶。当有两个以上数量的 key 被分到哈希表中同一个哈希桶上时,此时称这些 key 发生了冲突

链式哈希

Redis 采用了「链式哈希」的方法来解决哈希冲突。实现的方式就是每个哈希表节点都有一个 next 指针,用于指向下一个哈希表节点,因此多个哈希表节点可以用 next 指针构成一个单向链表,被分配到同一个哈希桶上的多个节点可以用这个单向链表连接起来,这样就解决了哈希冲突。

链式哈希的缺陷:

随着链表长度的增加,在查询该位置上的数据的耗时就会增加,最差情况下时间复杂度为O(n)。

要想解决这一问题,就需要进行 rehash,也就是对哈希表的大小进行扩展。

rehash

在实际使用哈希表时,Redis 定义了一个 dict 结构体,这个结构体里定义了两个哈希表(ht[2]),原因就是 rehash 的时候需要两个哈希表。

渐进式 rehash

rehash 触发条件

整数集合

整数集合是 Set 对象的底层实现之一。当一个 Set 对象只包含整数值元素,并且元素数量不大时,
就会使用整数集合这个数据结构作为底层实现。

整数集合结构设计

整数集合本质上是一块连续内存空间,它的结构定义如下:

整数集合的升级操作

整数集合升级有什么好处?

整数集合支持降级操作吗?

跳表

跳表结构体

跳表节点结构体

跨度是用来干什么的?

首先明确的是,跨度与遍历操作无关,遍历操作只需要用前向指针(struct zskiplistNode *forward)就可以完成,跨度实际是为了计算这个节点在跳表中的排位。具体怎么做的呢?因为跳表中的节点都是按序排列的,那么计算某个节点排位的时候,从头节点到该节点的查询路径上,将沿途访问过的所有层的跨度累加起来,得到的结果就是目标节点在跳表中的排位

跳表节点查询过程

跳表节点层数设置

如何维持相邻两层的节点数量的比例为 2 : 1 呢?

对于跳表最高的层数,Redis 7.0 定义为 32Redis 5.0 定义为 64, Redis 3.0 定义为 32

面试题:为什么 Zset 的实现用跳表而不用平衡树(如 AVL树、红黑树等)?

Redis线程模式

Redis是单线程吗?

Redis采用单线程为什么还这么快?

Redis6.0之前为什么使用单线程?

1.官方回答:CPU并不是制约Redis性能表现的瓶颈(即使单线程的程序无法利用多核CPU),更多情况下是受内存大小和网络I/O的限制,所以Redis核心网络模型采用单线程没有问题,如果想使用服务器的多核CPU,可以在一台服务器上启动多个节点或者采用分片集群的方式。

2.使用单线程后,可维护性高,多线程可能会导致并发读写问题,增加系统复杂性,同时存在线程切换、锁带来的性能损耗。

Redis 6.0 之后为什么引入了多线程?

随着网络硬件性能提升,Redis性能瓶颈有时会出现在网络I/O处理上。为了提高网络I/O并行度,Redis6.0对于网络I/O采用多线程处理(对于命令的执行,仍然采用单线程处理)。引入多线程I/O特性对性能提升至少一倍以上。

Redis 持久化

Redis 如何实现数据不丢失?

AOF 持久化是如何实现的? 

三种写回策略优缺点:

AOF日志过大,会触发什么机制?

AOF采用文件追加方式,文件会越来越大,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF重写机制,对AOF文件的内容压缩,只保留可以恢复数据的最小指令集。

重写 AOF 日志的过程是怎样的?

触发重写机制后,主进程会创建重写AOF的子进程,重写AOF子进程会读取数据库中所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令写入新的AOF文件。

问题:在重写过程中,主进程依然可以正常处理命令,如果在子进程重写AOF日志过程中,主进程修改了已经存在的 key-value,那么会发生写时复制,此时会导致主进程与子进程数据不一致,如何解决?

RDB 持久化是如何实现的呢? 

RDB 持久化是把当前进程数据生成时间点快照保存到硬盘的过程(默认保存到dump.rdb中),避免数据意外丢失(Redis默认的持久化方式)。相比于AOF日志,RDB快照记录的是实际数据,因此在Redis恢复数据时,RDB恢复数据效率更高,只需将RDB文件读入内存即可,不需要像AOF那样需要额外执行操作命令才能恢复。

RDB做快照时会阻塞线程吗?

注:Redis的快照是全量快照,也就是每次执行快照,都是把内存中所有数据都记录到磁盘中。如果太频繁,会对Redis性能造成影响;如果频率太低,服务器故障时,丢失的数据会更多。

为什么会有混合持久化?

混合持久化工作流程:

Redis的大Key对持久化有什么影响? 

如何避免大Key? 

Redis 集群

Redis 如何实现服务高可用?

主从复制

在Redis中,用户可以通过执行 SLAVEOF 命令或者设置 slaveof 选项,让一个服务器去复制另一个服务器,我们称被复制的服务器为主服务器(master),而对主服务器进行复制的服务器则被称为从服务器(slave)。进行复制中的主从服务器双方的数据库将保存相同的数据,且主从服务器之间采用读写分离的方式,主服务器只写,从服务器只读。

为什么进行主从复制?

1.读写分离,性能扩展,降低主服务器的压力

2.容灾,快速恢复,主机挂掉时,从机变为主机

如何确定谁是主服务器,谁是从服务器?

  

命令传播:

主从服务器在完成第一次同步后,双方之间就会维护一个TCP长连接,目的是避免频繁的TCP连接和断开带来的性能开销。后续主服务器可以通过这个长连接继续将写操作命令传输给从服务器,然后从服务器执行该命令,使主从一致。

分摊主服务器的压力:

从服务器也可以拥有自己的从服务器,此时该从服务器不仅可以接受主服务器的同步数据,自己也可以作为主服务器将数据同步给自己所拥有的从服务器。通过这种方式,主服务器生成RDB和传输RDB的压力可以分摊到该从服务器上。

增量复制:

如果在命令传输中,TCP长连接断开,后续又恢复正常,怎么保证主从数据一致性?

在Redis2.8之前做法是主从服务器重新进行一次全量复制,从Redis2.8开始,主从服务器采用增量复制的方式继续进行同步,即只把网络断开期间的主服务器的写操作同步给从服务器。

面试题

Redis主从节点是长连接还是短连接?

长连接

怎么判断 Redis 某个节点是否正常工作? 

主从复制架构中,过期key如何处理?

Redis 是同步复制还是异步复制?

Redis 主节点每次收到写命令之后,先写到内部的缓冲区,然后异步发送给从节点。

主从复制中两个 Buffer(replication buffer 、repl backlog buffer)有什么区别? 

为什么会出现主从数据不一致?

如何应对主从数据不一致? 

主从切换如何减少数据丢失? 

主从切换中,产生数据丢失的情况有两种:

1.异步复制导致的数据丢失

解决:对于主节点,一旦所有从节点数据复制和同步的延迟超过 min-slaves-max-lag 定义的值,此时主节点拒绝接受任何请求。对于客户端,当发现 master 不可写后,采用降级措施,将数据暂时写入本地缓存和磁盘中。待 master 恢复正常后再重新写入。

2.集群产生脑裂导致数据丢失

解决:设置配置文件中参数,如果与主节点连接的从节点数量小于指定值或者主从复制延迟超过指定值,则主节点禁止写数据。

主从如何做到故障自动切换?

由哨兵自动完成故障发现和故障转移。

哨兵模式 

为什么有哨兵机制?

哨兵机制是如何工作的? 

1.监控:哨兵每隔1秒给所有主从节点发送PING命令,如果主从节点没有在规定时间响应哨兵的PING命令,哨兵会将它们标记为主观下线。为了减少误判,会部署多个哨兵节点组成哨兵集群(最少需要三台机器组成哨兵集群)。当一个哨兵判断主节点为主观下线后,就会向其他哨兵发起命令,其他哨兵收到命令后,根据自身和主节点的网络状况,做出赞成投票或拒绝投票的响应。当这个哨兵的赞同票数达到哨兵配置文件中的 quorum 设定的值后,此时主节点被哨兵标记为客观下线。(客观下线只适用于主节点)

由哪个哨兵做故障转移?

先选举候选者:哪个哨兵判断主节点为客观下线哪个就是候选者。

候选者选举成为leader: 2.选主+通知:

主从故障转移包含以下步骤:

步骤一:选出新主节点

步骤二:将从节点指向新主节点

步骤三:通知客户端主节点已更换

步骤四:将旧主节点变为从节点

哨兵继续监视旧主节点,当旧主节点重新上线时,哨兵集群就会向它发送 SLAVEOF 命令,让它成为新主节点的从节点。至此,故障转移完成。

哨兵集群是如何组成的?

切片集群模式

哈希槽如何映射到具体的Redis节点上?

注意:如果采用手动分配哈希槽,需要把 16384 个槽都分配完,否则Redis集群无法正常工作。 

集群脑裂导致数据丢失怎么办?

什么是集群脑裂?

如何解决? 

Redis 过期删除与内存淘汰

过期删除策略

Redis是可以对key设置过期时间的,过期删除策略用于将已过期的键值对删除。

如何设置过期时间?

如果想查看某个 key 剩余的存活时间,可以使用 TTL <key> 命令。 

如果想取消 key 的过期时间,则可以使用 PERSIST <key> 命令。

如何判定 key 已过期了?

过期删除策略有哪些?

定时删除、惰性删除、定期删除

 

Redis 过期删除策略是什么? 

Redis采用惰性删除+定期删除这两种策略配合使用。

惰性删除:Redis在访问或者修改key之前,会检查key是否过期,如果过期,则删除该key,返回null给客户端;如果没过期,直接返回正常结果给客户端。

定期删除:默认每100ms进行一次过期检查(该值可在Redis配置文件中修改,默认hz 10,即每秒进行十次过期检查),每次从过期字典中随机抽取20个key(固定值),检查是否过期,删除过期的key;如果本轮检查已过期的key的超过5个(即占比超过25%),则继续抽取;如果已过期的key比例小于25%,则停止删除,等待下一轮再检查。

可见,定期删除是一个循环的过程。为了保证定期删除不会出现循环过度,导致线程卡死的现象,为此增加了定期删除循环流程的时间上限,默认不会超过25ms。

内存淘汰策略

当Redis运行内存超过最大运行内存时,就会触发内存淘汰策略删除符合条件的key,以此保障Redis的高效运行。

如何设置 Redis 最大运行内存?

Redis 内存淘汰策略有哪些?

LRU 算法和 LFU 算法有什么区别?

什么是LRU算法?

Redis是如何实现LRU算法的?

什么是LFU算法?

Redis是如何实现LFU算法的?

 

Redis 缓存设计 

什么是缓存雪崩?

大量缓存数据在同一时间过期或者Redis故障宕机时,如果此时有大量的用户请求,都无法在Redis中处理,于是全部请求都直接访问数据库,从而导致数据库压力骤增,甚至崩溃。

如何解决?

针对大量数据同时过期:

1.均匀设置过期时间:避免将大量数据设置成同一过期时间,在对缓存数据设置过期时间时,给这些数据的过期时间加上一个随机数,保证数据不会在同一时间过期。

2.互斥锁:如果发现数据不在Redis中,就加一个互斥锁,保证同一时间只有一个请求来构建缓存(从数据库读取数据,再将数据更新到Redis中),当缓存构建完成后,再释放锁。

3.监控缓存过期,提前更新

针对Redis故障宕机:

1.服务熔断或请求限流机制:服务熔断即暂停对缓存的访问,直接返回错误,不再访问数据库。

请求限流机制只将少部分请求发送到数据库进行处理。

2.构建Redis集群:当Redis主节点故障宕机,从节点切换成为主节点,继续提供缓存服务。

什么是缓存击穿?

如果缓存中某个热点数据过期了,此时大量的请求访问该热点数据,将无法从缓存中获取,直接访问数据库,导致数据库容易被高并发的请求击垮。

如何解决?

1.互斥锁

2.不给热点数据设置过期时间

什么是缓存穿透?

 如何解决?

布隆过滤器如何工作?

如何设计一个缓存策略,可以动态缓存热点数据呢?

说说常见的缓存更新策略? 

 Cache Aside策略:

Read/Write Through策略: 

 

Write Back策略:

数据库和缓存如何保证一致性?

先更新数据库,再更新缓存:

解决:

先更新缓存,再更新数据库: 

Cache Aside 策略:

该策略可以细分为读策略与写策略:

先删除缓存,再更新数据库:

A请求先删除缓存,在A更新数据库之前,B请求到来,B先访问缓存,发现缓存未命中,于是访问数据库,读取数据库的值并更新到缓存,此时A更新数据库为新值,但缓存中还是旧值,造成二者不一致。所以,此策略在读写并发时,存在缓存与数据库不一致问题。

先更新数据库,再删除缓存:

存在问题: 如果删除缓存失败,导致缓存中的数据还是旧值,而数据库中的数据是新值。

解决:利用消息队列进行删除的补偿。在删除缓存时,将数据加入到消息队列,由消费者从消息队列中读数据,执行删除操作,删除成功后,将数据从消息队列中移除。

Redis 实战

Redis 如何实现延迟队列?

Redis 的大 key 如何处理?

什么是 Redis 大 key?

如何查找大 key? 

1.redis-cli --bigkeys 查找大key

 2.使用 SCAN 命令查找大 key

3.使用 RdbTools 工具查找大 key

如何删除大 key?

删除操作的的本质是释放键值对所占用的内存空间,在释放内存时,操作系统会将释放的内存块插入到空闲内存块的链表中,以便后续进行管理和再分配。如果一下释放大量内存,操作空闲内存块链表时间就会增加,进而造成Redis主线程阻塞,导致其他请求无法响应。

1.分批次删除:先获取一定数量的大key,再将这些大key分批次删除。

2.异步删除:

Redis 管道有什么用?

管道技术是客户端提供的一种批处理技术,用于一次处理多个Redis命令,从而提高交互的性能。

Redis 事务支持回滚吗?

不支持。对于MySQL事务中,只要有一条命令执行失败,事务就会回滚到之前的状态,但Redis事务中,一条命令失败不会影响其他正确的命令,其他正确的命令仍然可以执行成功。

如何用 Redis 实现分布式锁的?

如何使用分布式锁?

如何解锁?

基于 Redis 实现分布式锁有什么优缺点?

优点:

1.性能高效:redis读写速度快,适合高并发的锁操作场景。

2.实现方便:可直接使用setnx方法实现。

3.避免单点故障:Redis是跨集群部署的,自然就避免了单点故障。

缺点:

1.超时时间不好设置:时间过长,影响性能;时间过短,起不到保护共享资源的目的。

2.Redis主从复制中数据是异步复制的,导致分布式锁的不可靠性:

Redis 如何解决集群情况下分布式锁的可靠性?

Redlock 算法加锁的过程?

相关文章:

【Redis】常见面试题

什么是Redis&#xff1f; Redis 和 Memcached 有什么区别&#xff1f; 为什么用 Redis 作为 MySQL 的缓存&#xff1f; 主要是因为Redis具备高性能和高并发两种特性。 高性能&#xff1a;MySQL中数据是从磁盘读取的&#xff0c;而Redis是直接操作内存&#xff0c;速度相当快…...

每日 Java 面试题分享【第 17 天】

欢迎来到每日 Java 面试题分享栏目&#xff01; 订阅专栏&#xff0c;不错过每一天的练习 今日分享 3 道面试题目&#xff01; 评论区复述一遍印象更深刻噢~ 目录 问题一&#xff1a;Java 中的访问修饰符有哪些&#xff1f;问题二&#xff1a;Java 中静态方法和实例方法的区…...

「全网最细 + 实战源码案例」设计模式——桥接模式

核心思想 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;将抽象部分与其实现部分分离&#xff0c;使它们可以独立变化。降低代码耦合度&#xff0c;避免类爆炸&#xff0c;提高代码的可扩展性。 结构 1. Implementation&#xff08;实现类…...

JavaScript 进阶(上)

作用域 局部作用域 局部作用域分为函数作用域和块作用域。 函数作用域&#xff1a; 在函数内部声明的变量只能在函数内部被访问&#xff0c;外部无法直接访问。 总结&#xff1a; 函数内部声明的变量&#xff0c;在函数外部无法被访问 函数的参数也是函数内部的局部变量 …...

【编译原理实验二】——自动机实验:NFA转DFA并最小化

本篇适用于ZZU的编译原理课程实验二——自动机实验&#xff1a;NFA转DFA并最小化&#xff0c;包含了实验代码和实验报告的内容&#xff0c;读者可根据需要参考完成自己的程序设计。 如果是ZZU的学弟学妹看到这篇&#xff0c;那么恭喜你&#xff0c;你来对地方啦&#xff01; 如…...

深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据

深入探讨&#xff1a;服务器如何响应前端请求及后端如何查看前端提交的数据 一、服务器如何响应前端请求 前端与后端的交互主要通过 HTTP 协议实现。以下是详细步骤&#xff1a; 1. 前端发起 HTTP 请求 GET 请求&#xff1a;用于从服务器获取数据。POST 请求&#xff1a;用…...

如何利用Docker和.NET Core实现环境一致性、简化依赖管理、快速部署与扩展,同时提高资源利用率、确保安全性和生态系统支持

目录 1. 环境一致性 2. 简化依赖管理 3. 快速部署与扩展 4. 提高资源利用率 5. 确保安全性 6. 生态系统支持 总结 使用 Docker 和 .NET Core 结合&#xff0c;可以有效地实现环境一致性、简化依赖管理、快速部署与扩展&#xff0c;同时提高资源利用率、确保安全性和生态…...

@Inject @Qualifier @Named

Inject Qualifier Named 在依赖注入&#xff08;DI&#xff09;中&#xff0c;Inject、Qualifier 和 Named 是用于管理对象创建和绑定的关键注解。以下是它们的用途、依赖配置和代码示例的详细说明&#xff1a; 1. 注解的作用 Inject&#xff1a;标记需要注入的构造函数、字段…...

创建 priority_queue - 进阶(内置类型)c++

内置类型就是 C 提供的数据类型&#xff0c;⽐如 int 、 double 、 long long 等。以 int 类型为例&#xff0c;分 别创建⼤根堆和⼩根堆。 这种写法意思是&#xff0c;我要告诉这个优先级队列要建一个什么样的堆&#xff0c;第一个int是要存什么数据类型&#xff0c;vecto…...

2. Java-MarkDown文件解析-工具类

2. Java-MarkDown文件解析-工具类 1. 思路 读取markdown文件的内容&#xff0c;根据markdown的语法进行各个类型语法的解析。引入工具类 commonmark 和 commonmark-ext-gfm-tables进行markdown语法解析。 2. 工具类 pom.xml <!-- commonmark 解析markdown --> <d…...

动态规划DP 最长上升子序列模型 登山(题目分析+C++完整代码)

概览检索 动态规划DP 最长上升子序列模型 登山 原题链接 AcWing 1014. 登山 题目描述 五一到了&#xff0c;ACM队组织大家去登山观光&#xff0c;队员们发现山上一共有N个景点&#xff0c;并且决定按照顺序来浏览这些景点&#xff0c;即每次所浏览景点的编号都要大于前一个…...

css-设置元素的溢出行为为可见overflow: visible;

1.前言 overflow 属性用于设置当元素的内容溢出其框时如何处理。 2. overflow overflow 属性的一些常见值&#xff1a; 1 visible&#xff1a;默认值。内容不会被剪裁&#xff0c;会溢出元素的框。 2 hidden&#xff1a;内容会被剪裁&#xff0c;不会显示溢出的部分。 3 sc…...

家居EDI:Hom Furniture EDI需求分析

HOM Furniture 是一家成立于1977年的美国家具零售商&#xff0c;总部位于明尼苏达州。公司致力于提供高品质、时尚的家具和家居用品&#xff0c;满足各种家庭和办公需求。HOM Furniture 以广泛的产品线和优质的客户服务在市场上赢得了良好的口碑。公司经营的产品包括卧室、客厅…...

1、开始简单使用rag

文章目录 前言数据存放申请api开始代码安装依赖从文件夹中读取文档文档切块将分割嵌入并存储在向量库中检索部分代码构造用户接口演示提示 整体代码 前言 本章只是简单使用rag的一个示例&#xff0c;为了引出以后的学习&#xff0c;将整个rag的流程串起来 数据存放 一个示例…...

Linux Samba 低版本漏洞(远程控制)复现与剖析

目录 前言 漏洞介绍 漏洞原理 产生条件 漏洞影响 防御措施 复现过程 结语 前言 在网络安全的复杂生态中&#xff0c;系统漏洞的探索与防范始终是保障数字世界安全稳定运行的关键所在。Linux Samba 作为一款在网络共享服务领域应用极为广泛的软件&#xff0c;其低版本中…...

安卓(android)实现注册界面【Android移动开发基础案例教程(第2版)黑马程序员】

一、实验目的&#xff08;如果代码有错漏&#xff0c;可查看源码&#xff09; 1.掌握LinearLayout、RelativeLayout、FrameLayout等布局的综合使用。 2.掌握ImageView、TextView、EditText、CheckBox、Button、RadioGroup、RadioButton、ListView、RecyclerView等控件在项目中的…...

【 AI agents】letta:2024年代理堆栈演进(中英文翻译)

The AI agents stack AI 代理堆栈 November 14, 2024 11月 14, 2024原文: The AI agents stack官方教程教程学习笔记: 【memgpt】letta 课程1/2:从头实现一个自我编辑、记忆和多步骤推理的代理Understanding the AI agents landscape 了解 AI 代理环境 Although we see a …...

Java中 instanceof 的用法(详解)

目录 引言 基本语法 基本作用 1. 检查对象是否是指定类的实例 2. 检查对象是否是子类的实例 3. 检查对象是否实现某个接口 4.null 处理 错误分析&#xff1a; 5.综合对比示例 最后总结 注意事项 引言 instanceof 概念在多态中引出&#xff0c;因为在多态发生时&…...

联想拯救者R720笔记本外接显示屏方法,显示屏是2K屏27英寸

晚上23点10分前下单&#xff0c;第二天上午显示屏送到&#xff0c;检查外包装没拆封过。这个屏幕左下方有几个按键&#xff0c;按一按就开屏幕、按一按就关闭屏幕&#xff0c;按一按方便节省时间&#xff0c;也支持阅读等模式。 显示屏是 &#xff1a;AOC 27英寸 2K高清 100Hz…...

【RocketMQ 存储】- 一文总结 RocketMQ 的存储结构-基础

文章目录 1. 前言 本文章基于 RocketMQ 4.9.3 1. 前言 RocketMQ 存储部分系列文章&#xff1a; 【RocketMQ 存储】- RocketMQ存储类 MappedFile 【RocketMQ 存储】- 一文总结 RocketMQ 的存储结构-基础 【RocketMQ 存储】- 一文总结 RocketMQ 的存储结构-基础...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架&#xff0c;它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用&#xff0c;和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

React核心概念:State是什么?如何用useState管理组件自己的数据?

系列回顾&#xff1a; 在上一篇《React入门第一步》中&#xff0c;我们已经成功创建并运行了第一个React项目。我们学会了用Vite初始化项目&#xff0c;并修改了App.jsx组件&#xff0c;让页面显示出我们想要的文字。但是&#xff0c;那个页面是“死”的&#xff0c;它只是静态…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析

MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录&#xff0c;这个目录下存放着许多可执行文件。与其他系统的可执行文件类似&#xff0c;这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中&#xff0c;用…...

深入解析 ReentrantLock:原理、公平锁与非公平锁的较量

ReentrantLock 是 Java 中 java.util.concurrent.locks 包下的一个重要类,用于实现线程同步,支持可重入性,并且可以选择公平锁或非公平锁的实现方式。下面将详细介绍 ReentrantLock 的实现原理以及公平锁和非公平锁的区别。 ReentrantLock 实现原理 基本架构 ReentrantLo…...