JAVA面试专题-Redis

你在最近的项目中哪些场景使用了Redis

缓存

缓存穿透

缓存穿透:查询一个不存在的数据,mysql查询不到数据也不好直接写入缓存,导致每次请求都查数据库。

解决方案一:缓存空数据,即使查询返回的数据为空,也把这个空结果进行缓存(如key:1,value:null)。

优点:简单

缺点:消耗内存(总有空数据),不一致问题(来真的数据了)

解决方案二:布隆过滤器(可以使用redisson实现):查询之前先访问布隆过滤器,如果不存在就直接返回

优点:内存占用少,没有多余key

缺点:实现复杂,存在误判(bitmap位图越小误判越大,数组越大误判越小,但有内存消耗,一半百分之五以内的误判都可以接收)

布隆过滤器作用:可以用于检索一个元素是否在一个集合中(bitmap位图,数组每个单元只能存储0/1)。

缓存击穿

缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这个时间点对这个key有大量的并发请求,只能访问数据库,会把db压垮。

解决方案一:互斥锁

未命中后获取互斥锁(如setnx),处理完后释放锁,其他线程拿到锁才能运行

解决方案二:逻辑过期

在设置key时设置过期时间字段存入缓存,查询时如果过期,获取互斥锁后直接返回过期数据,同时开启新线程进行缓存重建释放锁,其他线程如果获取互斥锁失败后,仍然返回过期数据。

缓存雪崩

缓存雪崩:同一时间大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库

解决方案:

  1. 给不同的Key的TTL添加随机值,避免同时过期

  2. 利用Redis集群提高服务的可用性(哨兵模式,集群模式)

  3. 给缓存业务添加降级限流策略(nginx或Spring cloud gateway)

  4. 给业务添加多级缓存

《缓存三兄弟》

穿透无中生有key,布隆过滤null隔离。

缓存击穿过期key,锁与非期解难题。

雪崩大量过期key,过期时间要随机。

面试必考三兄弟,可用限流来保底。

先删除缓存还是先修改数据库-双写一致性

读操作:缓存命中,直接返回;缓存未命中查询数据库,写入缓存,设定超时时间

写操作:延迟双删:先删除缓存,修改数据库,延时一段时间再删除缓存

分布式锁(强一致性,性能低)

共享锁:读锁readLock,加锁之后,其他线程可以共享读操作

排他锁:独占锁write,加锁之后,阻塞其他线程读写操作

异步通知(允许延时一致的业务):

修改数据后写入数据库同时发送消息到MQ,受到MQ消息后更新缓存,主要依赖于MQ的可靠性。

阿里的组件canal组件也可以实现数据同步,不需要更改业务代码,部署服务后,它会把自己伪装成mysql的一个从节点,mysql数据更新后,canal会读取binlog数据,然后通过canal客户端获取到数据,更新缓存即可。

Redis持久化

RDB

RDB(Redis Database Backup file)Redis数据备份文件,也叫Redis数据快照,可以把内存中的所有数据记录到磁盘中,当Redis故障时,从磁盘读取快照文件,恢复数据。

主动备份:save bgsave(不影响主进程,一般用这个)

被动备份:redis.conf修改文件

RDB执行原理:物理内存通过进程中的页表交换数据,bgsave开始时会fork主进程得到子进程(复制页表),子进程完成fork后读取内存数据并写入RDB文件,fork采用copy-on-write技术(主进程读取内存时,访问共享内存;主进程执行写操作时,从内存中拷贝一份数据副本执行写操作,以后主进程读也从副本读)

AOF

AOF(Append Only File)追加文件,redis处理的每一个写命令都会记录在AOF文件中,可以看做是命令日志文件。

因为是记录命令,所以AOF文件会很大,通过bgrewriteaof命令,让AOF执行重写功能,只有最后一次有意义。也可以在配置文件里设置触发阈值自动重写。

Redis过期策略

惰性删除

设置key过期时间后,不去管它,下次使用该key时,检查是否过期,过期删除。

优点:CPU友好,不用浪费时间检查

缺点:内存不友好,不用就永远不会释放

定期删除

每隔一段时间对key检查,从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key。氛围SLOW(执行频率10hz,每次不超过25ms)和FAST(每次时间循环尝试执行,间隔不低于2ms,耗时不超过1ms)两种模式。

优点:可以通过限制删除操作执行的时长和频率来减少删除操作对cpu的影响,也能释放过期key占用的内存

缺点:难以确定删除操作的时长和频率

所以:Redis的过期删除策略为:惰性删除+定期删除配合使用

数据淘汰策略

使用建议:

  1. 优先使用allkeys-lru策略,把最近最常访问的数据留在缓存中,如果业务有明显的冷热数据区分建议使用。

  2. 访问数据频率差别不大,没有明显冷热区分,建议使用allkeys-random随机淘汰

  3. 如果有置顶要求,使用volatile-lru策略,同时置顶数据不设置过期时间

  4. 如果业务有短时高频访问的数据,建议使用lfu策略

分布式锁

可以使用setnx命令,或者直接用redisson(底层是setnx和lua脚本)

看门狗机制

每隔一段时间自动做一次续期(默认每隔十秒续期一次)

可重入性

基于redssion实现的分布式锁可重入,利用hash结构记录线程id和重入次数

主从一致性

RedLock(红锁):布置在一个redis实例上创建锁,应该实在多个redis实例上创建锁(n/2+1)。

但是红锁这个思想,实现复杂,性能差,运维繁琐,因为redis是基于AP思想啊(可用性),如果要实现主从一致,可以利用CP思想(一致性)的zookeeper来实现。

Redis集群方案

主从复制

单节点redis的并发能力是有上限的,要提高并发能力就要搭建主从集群,实现读写分离,往主节点里写,从节点里读。

主从全量同步

slave节点执行replicaof命令建议连接,请求master数据同步(发送replid和offset),master判断是否是第一次同步(replid是否一致),如果是第一次,就向slave返回master的数据版本信息(replid,offset),slave保存版本信息,master执行bgsave生成RDB文件并向slave发送,slave清空本地数据,加载RDB文件。在这个发送期间,主节点记录RDB期间的所有命令存入到repl_baklog中发送到slave,slave执行接收到的命令。

主从增量同步

适用于slave重启或后期数据变化,从节点发送replid和offset,主节点判断是否一致,如果不是第一次,就回复continue,之后去repl_back中获取offset后的数据,发送命令。

哨兵机制

Sentinel哨兵机制实现主从集群的自动故障恢复。

服务状态监控

Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令

主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。

客观下线:如果超过sentinel数量一半的哨兵都认为该实例主观下线,该实例客观下线。

哨兵选主规则

判断主与从断开时间长短,超过指定值就排该从节点

然后判断从节点的slave-priority值,越小优先级越高

如果优先级一样,判断offset值,越大优先级越高

判断slave节点的运行id大小,越小优先级越高

集群脑裂

主节点和从节点和哨兵处于不同网络分区,使得哨兵灭有能够心跳感应到主节点,所以选举出了一个新的主节点,这样就存在了两个master,像大脑分裂一样,导致客户端孩子啊老主节点写入数据,新节点无法同步数据,网络恢复后,哨兵会将老的主节点降为从节点,再从新master中同步数据,导致数据丢失。

解决:修改redis配置,设置最小的从节点数量以及缩短主从数据同步的延迟时间。

分片集群结构

主从和哨兵可以解决高可用,高并发读的问题,但是没有解决海量数据存储和高并发写问题,这时可以使用分片集群。

分片集群特征:

集群中有多个master,每个master保存不同数据

每个master有多个slave节点

master之间通过ping监测彼此健康状态

客户端请求可以访问集群任意节点,最终都会转发到正确节点

分片集群结构:数据读写

引入哈希槽概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。

Redis是单线程的为什么速度这么快?

Redis是存内存操作,执行速度非常快

采用单线程,避免不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题

使用I/O多路复用模型,非阻塞IO

I/O多路复用模型

redis是纯内存操作,执行速度非常快,性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型实现了高效的网络请求

常见的IO模型

阻塞IO:等待数据和从内核拷贝数据到用户空间都需要阻塞等待

非阻塞IO:recvform阶段立即返回结果而不阻塞第二阶段依旧阻塞

IO多路复用:利用单个线程来同时监听多个Socket,并在某个Socket可读,可写时得到通知,避免无效等待,充分利用cpu资源。

Redis网络模型

提供了连接应答处理器,命令回复处理器(多线程回复事件),命令请求处理器(命令转换使用多线程,增加速度,命令执行依旧是单线程)。

相关推荐
代码小鑫4 分钟前
A031-基于SpringBoot的健身房管理系统设计与实现
java·开发语言·数据库·spring boot·后端
五味香12 分钟前
Linux学习,ip 命令
linux·服务器·c语言·开发语言·git·学习·tcp/ip
欧阳枫落18 分钟前
python 2小时学会八股文-数据结构
开发语言·数据结构·python
何曾参静谧25 分钟前
「QT」文件类 之 QTextStream 文本流类
开发语言·qt
monkey_meng29 分钟前
【Rust类型驱动开发 Type Driven Development】
开发语言·后端·rust
落落落sss37 分钟前
MQ集群
java·服务器·开发语言·后端·elasticsearch·adb·ruby
我救我自己37 分钟前
UE5运行时创建slate窗口
java·服务器·ue5
2401_853275731 小时前
ArrayList 源码分析
java·开发语言
zyx没烦恼1 小时前
【STL】set,multiset,map,multimap的介绍以及使用
开发语言·c++
lb36363636361 小时前
整数储存形式(c基础)
c语言·开发语言