Redis篇

Redis篇

redis使用场景-缓存-缓存穿透

image-20241022213844469

解决方案一:缓存空数据

image-20241022214024205

解决方案二:布隆过滤器

image-20241022220210991

布隆过滤器的介绍

image-20241022220345897

image-20241022220419176

image-20241022220605043

image-20241022220636120

image-20241022220700075

面试总结

什么是缓存穿透?怎么解决?
缓存一个不存在的数据,导致每次都需要去访问数据库,造成数据库压力较大。
1)缓存空数据
2)布隆过滤器(位数组)
布隆过滤器存在的问题:误判问题

redis使用场景-缓存-缓存击穿

image-20241022221247768

两种解决方案:

1)互斥锁

2)逻辑过期

两种解决方案:互斥锁/逻辑过期

image-20241022221813254

上述两者对比:

image-20241022221935945

对于容错率低的场景,比如银行等采用互斥锁,其满足强一致性,缺点是性能差。

面试总结

什么是缓存击穿?怎么解决?
缓存中的key过期,恰好同一时间点有大量的请求到来,大量请求发现缓存过期进而都去访问数据库,把数据库压垮。
解决:
1)使用互斥锁(分布式锁),缓存失效后先获取锁,再更新缓存,再释放锁。
2)不设置过期时间。
	1、设置一个逻辑过期时间字段存入缓存,不给当前key设置过期时间
	2、查询时将数据从缓存中取出来再判断是否过期
	3、如果过期则开通一个新线程(还是要加锁)去重置缓存,当前缓存会继续返回数据,只不过数据不是最新的。
	
方法一:保证强一致性,性能低
方法二:高可用性,性能高

redis使用场景-缓存-缓存雪崩

解决方案

image-20241022222757852

面试总结

什么是缓存雪崩?怎么解决?
同一时段,大量缓存同时失效或者Redis宕机,导致大量请求到达数据库,带来巨大压力。
解决:
1)给不同key的过期时间TTL添加随机值
2)利用Redis集群提高服务的可用性
	哨兵模式、集群模式
3)给缓存添加降级限流策略
	nginx 或 Spring cloud gateway

redis使用场景-缓存-双写一致性

image-20241022223420253

image-20241022224038017

image-20241022224209128

image-20241022223906712

先删缓存

image-20241031092212108

先删数据库

image-20241031092506002

强一致性-采用Redisson提供的读写锁

image-20241022224827858

允许延时一致的业务-异步通知保证数据的最终一致性

image-20241022225138717

允许延时一致的业务-基于Canal的异步通知

image-20241022225311033

面试总结

image-20241023093843700

1)延迟双删:如果是写操作,先删缓存,再更新数据库,最后再延时删缓存,只不过延时多久不太好确定,并且在延时的过程中可能会出现脏数据,不能保证强一致性。
2)先更新数据库,再删除缓存,这种也不能保证强一致性。
3)强一致性的情况:可以采用Redisson实现的读写锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥。当我们更新数据的时候,添加排他锁,它是读写、读读都互斥,这样能保证数据在写入的时候,不会让其他线程进行读写操作,避免了脏数据。
4)允许延时一致:
	1、使用MQ中间件,更新数据之后,通知缓存删除
	2、利用canal中间件,不需要修改业务代码,伪装成mysql的一个从节点,canal通过读取Binlog日志来更新缓存。
	两者区别(我查的,供参考):工作原理:MQ 中间件是应用程序更新数据后主动发消息通知缓存删除;Canal 是伪装成 MySQL 从节点,被动读取 Binlog 日志来更新缓存。
代码侵入性:MQ 中间件需要修改业务代码添加消息发送逻辑;Canal 不需要修改业务代码。

这个地方可以看->小林后端面经178

redis使用场景-缓存-持久化

image-20241023101334646

RDB

image-20241023101639698

image-20241023102314723

AOF

image-20241023104213311

image-20241023104731933

疑问:redis中AOF方式下,everysec为什么会丢失一秒的数据

​ 如果在某一秒内,Redis已经将数据写入缓冲区但还未执行fsync操作时系统发生故障(如断电、崩溃、Redis进程被意外终止等),那么这一秒的数据由于只存在于内存缓存区中,未持久化到磁盘而导致数据丢失。

触发重写规则

image-20241023105853512

RDB和AOF对比

image-20241023110044751

面试总结

Redis数据持久化:
1)RDB
	RDB是一个快照文件,把Redis内存存储的数据写到磁盘上,当Redis实例宕机恢复数据的时候,方便从RDB的快照文件中恢复数据。
	执行原理:bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据,完成fork后读取内存数据并写入RDB文件。fork采用的是copy-on-write技术。
		1、当主进程执行读操作时,访问共享内存
		2、当主进程执行写操作时,则会拷贝一份数据,执行写操作
2)AOF
	AOF默认不开启,开启的话在redis.conf文件中有一个appendonly no改为yes.
	AOF的含义是追加文件,当Redis操作写命令的时候,都会存储到这个文件中,当Redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据。
	AOF的命令记录频率:

image-20241217142657966

疑问:redis中AOF方式下,everysec为什么会丢失一秒的数据
	如果在某一秒内,Redis已经将数据写入缓冲区但还未执行fsync操作时系统发生故障(如断电、崩溃、Redis进程被意外终止等),那么这一秒的数据由于只存在于内存缓存区中,未持久化到磁盘而导致数据丢失。

image-20241217143129325

RDB因为是二进制文件,在保持的时候体积比较小,故它恢复的比较快,但是可能会丢失数据;我们在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一些,但是它丢失数据的风险要小很多,在AOF文件中可以设置刷盘策略,比如每秒备份一下命令。

image-20241217144648326

redis使用场景-缓存-数据过期策略

image-20241023112030461

惰性删除

image-20241023112336509

定期删除

image-20241023112552042

Redis使用的策略

image-20241023112634926

面试总结

提供的两种过期删除策略:
1)惰性删除:在设置该key过期时间后,不去管它;需要该key时,再检查其是否过期;如果过期,就删掉它。	
	对CPU友好,不需要去检查过期key。对内存不友好,过期的key一直存在在那里,直到检查它时。
2)定期删除:每隔一段时间,对一些key进行检查,删除里面过期的key
	两种模式:
		SLOW模式是定时任务:执行频率默认10hz,每次不超过25ms
		FAST模式执行频率不固定,每次事件循环会尝试执行
Redis使用的是:惰性删除+定期删除配合使用

redis使用场景-缓存-数据淘汰策略

image-20241023113451545

数据淘汰策略-使用建议

image-20241023113737809

面试总结

Redis的淘汰策略:
默认是noeviction,不删除任何数据,内部不足直接报错
也可以在Redis配置文件中进行修改,里面有两个重要的概念:LRU和LFU
LRU:最少最近使用,即当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。
LFU:最少频率使用,统计每个key的访问频率,值越小淘汰优先级越高。

其他问题:
image-20241023113933714

redis分布式锁-使用场景

image-20241023114322629

以抢劵场景为例进行分析

image-20241023114858880

对于单体项目分析

这种情况下加个synchronized是没有问题的,它是属于本地的锁

image-20241023115010423

服务集群部署分析

image-20241023115213183

使用synchronized就解决不了,需要使用分布式锁

image-20241023115326123

image-20241023115459879

redis分布式锁-实现原理

内容

image-20241026140000388

执行流程

image-20241026140759291

redisson实现的分布式锁-可重入

image-20241026141530360

img

流程图

image-20241026165433550

redisson实现的分布式锁-主从一致性

image-20241026141727518

image-20241026141935989

面试总结

synchronized是属于本地的锁,对于单体项目没有问题,但当代码部署在多个服务器上时,就需要使用分布式锁进行解决。

Q1:Redis分布式锁如何实现?
	在Redis中提供了一个命令setnx(SET if not exists),由于Redis单线程,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或者删除key的情况下,其他客户端是不能设置这个key的。
	
Q2:如何控制Redis实现分布式锁的有效时长?
	Redis中setnx指令不好控制这个问题,可以使用Redis的Redisson框架实现。在Redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成时,在Redisson中引入了一个看门狗机制,即每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当任务完成之后释放锁即可。
	
Q3:Redisson的这个锁,可以重入吗?
	可以重入,这样可以避免死锁的产生。(在一个 Java 类中有方法methodA和methodB,methodA获取了一个 Redisson 可重入锁,然后在methodA中调用了methodB,methodB也尝试获取相同的锁,因为是可重入锁,线程不会被阻塞,从而避免了死锁。)
	多个锁重入需要判断是否是当前线程,如果是当前线程持有的锁就会计数,释放锁进行减一。存储的时候采用的是Hash结构来存储线程信息和重入的次数。
大key 	小key(当前线程唯一标识)	  value(当前线程重入的次数)
	
Q4:Redisson锁能解决主从数据一致性问题吗?
	不能。(例如,如果在主节点完成库存更新后,还没来得及将更新后的库存数据复制到从节点时,主节点发生故障,进行主从切换。此时新的主节点(原来的从节点)可能还没有最新的库存数据,就会出现数据不一致的情况。在这种情况下,即使有 Redisson 锁控制对主节点的并发访问,也无法避免主从数据不一致的问题。)
	可以使用Redisson提供的红锁解决,但性能会很低,若非要保证数据的强一致性,可以使用zookeeper实现的分布式锁。
	RedLock(红锁):避免只在一个Redis实例上创建锁,需要在多个Redis实例中创建锁。

redis集群-相关面试题

image-20241026165931373

主从复制

image-20241026170134110

主从全量同步

image-20241026170806553

主从增量同步

image-20241026171022403

面试总结

Q1:Redis主从同步
	单节点Redis并发能力有上限,要进一步提高Redis并发能力,就需要搭建主从集群,实现读写分离。主节点负责写数据,从节点负责读数据。
	
Q2:主从同步数据的流程
	主从同步分为两个阶段:全量同步和增量同步。
	全量同步是指从节点第一次与主节点建立连接的时候使用全量同步。
	1、从节点请求主节点同步数据,其中从节点会携带自己的replication id 和 offset偏移量。
	2、主节点判断是否是第一次请求。判断依据是两者是否是同一个replication id。如果不是,说明第一次同步,那么主节点会把自己的replication id 和 offset发送给从节点,让从节点与主节点的信息保持一致。
	3、同时主节点会执行bgsave,生成rdb文件后,发送给从节点去执行。从节点先是把自己的数据清空,然后执行主节点发送过来的rdb文件。
	当然,如果在rdb生成期间,仍然有请求到达主节点,则主节点会以命令的方式记录到缓冲区,缓冲区是一个日志文件,最后把这个日志文件发送到从节点,从而保证主节点与从节点完全一致。
	
	增量同步指的是,当从节点服务重启之后,数据就不一致了,所以这个时候,从节点会请求主节点同步数据,主节点判断两者不是第一次建立连接,就取从节点的offset值,然后主节点就从命令日志中获取offset值之后的数据,发送给从节点进行数据同步。

哨兵模式

哨兵的作用

image-20241026171521657

服务状态监测

image-20241026171742988

哨兵模式的脑裂问题

image-20241026172125794

image-20241026172205668

面试总结

Q1:如何保证Redis的高并发高可用?
	首先搭建主从集群,再加上使用Redis中的哨兵模式,哨兵模式可以实现主从集群的自动故障恢复,里面包含对主从服务的监控、自动故障恢复、通知;如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后仍以新的master为主;同时Sentinel也充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送到Redis的客户端。
	
Q2:你们使用的Redis是单节点还是集群?
	使用的是主从(11从)加哨兵。尽量不做分片集群,因为集群维护起来比较麻烦,并且集群之间的心跳检测和数据通信会消耗大量的网络带宽。
	
Q3:Redis集群脑裂,该怎么解决?
	有时候由于网络等原因可能会出现脑裂的情况,即Redis master节点和Redis slave节点和Sentinel 处于不同的网络分区,使得Sentinel没能心跳感知到master,进而通过选举的方式将一个slave提升为master,这样就存在两个master,就像大脑分裂。导致客户端在old master那里写入数据,新节点无法同步数据,当网络恢复后,Sentinel会将old master降为salve,这时再从新master同步数据,导致old master中大量数据丢失。
	解决方法:1)设置最少的salve节点个数,比如至少要有一个从节点才能同步数据(这样可以防止在数据同步不充分的情况下进行故障转移,避免出现两个 “主节点” 同时接受写入请求,导致数据丢失或不一致的情况。因为只有在有足够的从节点同步了数据后,才会进行主从切换,保证了新主节点的数据相对完整性。)
			2)设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量数据丢失。(如果主从数据同步延迟在 5 秒以内,说明从节点的数据相对较新,能够及时跟上主节点的数据更新。主节点就会处理这个写入请求,并且数据能够比较及时地同步到从节点。但是,如果主从数据同步延迟超过了 5 秒,主节点会拒绝这个写入请求。这样做是为了避免在脑裂发生时,主节点的数据无法及时同步到从节点,防止在网络恢复后,原主节点(在脑裂期间被孤立)的数据无法同步到新主节点(由 Sentinel 选举产生)而导致数据丢失。)

分片集群结构

image-20241026173057000

数据读写

image-20241026173326326

面试总结

Q1:Redis的分片集群有什么作用?
	分片集群主要解决:海量数据存储的问题,集群中有多个master,每个master保存不同数据,并且还可以给每个master设置多个slave节点,就可以继续增大集群的高并发能力。同时每个master之间通过ping监测彼此健康状态,就类似于哨兵模式了。当客户端请求访问集群任意节点,最终都会被转发到正确节点。
	
Q2:Redis分片集群中数据是如何存储和读取的?
	Redis集群引入哈希槽的概念,有16384个哈希槽,集群中每个主节点绑定了一定范围的哈希槽,key通过CRC16校验后对16384取模来决定放置哪个槽,通过槽找到对应的节点进行存储。
取值的逻辑一样。

redis是单线程,为什么这么快?

image-20241026173858125

用户空间和内核空间

image-20241026174547892

阻塞IO

image-20241026174917703

非阻塞IO

image-20241026175617898

IO多路复用

image-20241026180045310

IO多路复用的实现方式

image-20241026180321120

Redis网络模型

image-20241026180633296

面试问答

Q1:Redis单线程,为什么还这么快?
	1、纯内存操作,执行速度非常快
	2、采用单线程,避免不必要的上下文切换;而且多线程还要考虑线程安全问题
	3、使用I/O多路复用模型,非阻塞IO
	例如,bgsave 和 bgrewriteaof 都是在后台执行操作,不影响主线程的正常使用,不会产生阻塞。
	(其他补充:Redis采用了I/O多路复用机制处理大量的客户端socket请求,I/O多路复用机制是指一个线程处理多个IO流,就是我们经常听到的select/epoll机制。简单来说,在Redis只运行单线程的情况下,该机制允许内核同时监听socket和已连接的socket。内核会一直监听这些socket上的连接请求和数据请求。一旦有请求到达,就会交给Redis线程处理,这就实现了一个Redis线程处理多个IO流的效果。)
	
Q2:IO多路复用模型?
	I/O多路复用是指利用单个线程来同时监听多个socket,并在某个socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。目前的I/O多路复用都是采用epoll模式实现,它会通知用户进程socket就绪的同时,把已就绪的socket写入用户空间,不需要挨个遍历socket来判断是否就绪,提升了性能。
	其中 Redis 的网络模型就是使用 I/O 多路复用结合事件的处理器来应对多个 Socket 请求,比如,提供了连接应答处理器、命令回复处理器、命令请求处理器;
	在 Redis 6.0 之后,为了更好的提升性能,在命令回复处理器中使用了多线程来处理回复事件,在命令请求处理器中,命令的转换使用了多线程,增加命令转换速度,在命令执行的时候,依然是单线程。

Redis数据类型

image-20241219104149576

posted @   墨羽寻觅  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示