Java面试八股—Redis篇

news2025/3/15 14:53:17

一、Redis的使用场景

(一)缓存

1.Redis使用场景缓存

场景:缓存热点数据(如用户信息、商品详情),减少数据库访问压力,提升响应速度。

2.缓存穿透

正常的访问是:根据ID查询文章,先查Redis,如果Redis中命中,返回结果;Redis查不到,查DB,DB查询到结构,返回(返回之前数据存储到Redis)。

缓存穿透是什么?
缓存穿透是大量请求访问缓存和数据库中都不存在的数据(如非法ID或随机攻击),导致请求穿透缓存直接打到数据库,mysql查询不到数据也不会直接写入缓存,每次请求都查数据库,引发数据库压力

简单回答:查询不存在的数据,mysql查询不到数据也不会直接写入缓存,导致请求穿透缓存直接打到数据库。

3.缓存穿透解决方案

(1)方案一:缓存空值。

①缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存,设置较短的过期时间。
②优点:简单。
③缺点:消耗内存,可能会发生不一致的问题。

(2)方案二:布隆过滤器

在这里插入图片描述
①缓存预热时,需要将布隆过滤器给初始化。例如有一批热点数据,先将这些热点数据批量添加到缓存中,与此同时,要将这些热点数据添加到布隆过滤器中。
②布隆过滤器依靠位图(bitmap):相当于是一个以(bit)位为单位的数组,数组中每个单元只能存储二进制数0或1。
③布隆过滤器作用:可以用于检索一个元素是否在一个集合中。
④布隆过滤器存储数据和查询数据,初始的bitmap中数组中都是0。
存储数据时:假设存储id=1的数据,通过多个hash函数获取hash值,根据hash计算数组的对应位置,将对应位置0改为1。
查询数据时:使用相同hash函数获取hash值,判断对应位置是否都为1。
⑤布隆过滤器存在误判。查询数据时,根据hash函数获取hash值时,可能出现重叠,也就是误判,明明不存在的数据,被误判存在。
误判率:数组越小误判率越大;数组越大误判率越小,但是同时带来了更多的内存消耗。误判不可能不存在,一般设置误判率为0.05(5%)。
在这里插入图片描述
⑥优点:内存占用较少,没有多余的key
⑦缺点:实现复杂,存在误判。
⑧布隆过滤器实现方案:Redisson或Guava。

在这里插入图片描述

4.缓存击穿

缓存击穿定义:当某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些请求发现缓存过期,一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 DB 压垮。

5.缓存击穿解决方案

(1)方案一:互斥锁

假设有两个线程。
线程1查询缓存,未命中,缓存中没有查询需要的数据。接着获取互斥锁成功,线程1查询数据库,查询之后返回数据给缓存,重建缓存数据。将返回的数据写入缓存,最后释放锁。
线程2在线程1查询过程中也发起查询缓存,发现未命中,线程2尝试获取互斥锁但是失败。(线程1目前正获取互斥锁)线程2休眠一会儿后,再返回到发起查询缓存,进行不断的重试,直到线程1释放互斥锁,那么线程2就可以在查询缓存中,缓存命中,返回数据。

第一,可以使用互斥锁:当缓存失效时,不立即去load db,先使用如 Redis 的 SETNX 去设置一个互斥锁。当操作成功返回时,再进行 load db的操作并回设缓存,否则重试get缓存的方法。

在这里插入图片描述
特点:确保数据的强一致性,但性能低,且有可能产生死锁的问题。

(2)方案二:逻辑过期

在这里插入图片描述

在这里插入图片描述

第二种方案是设置当前key逻辑过期,大概思路如下:1) 在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间;2) 当查询的时候,从redis取出数据后判断时间是否过期;3) 如果过期,则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据可能不是最新的。

特点:具有高可用性,性能比较高,但数据同步无法做到强一致,不能保证数据绝对一致。

在这里插入图片描述

6.缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Reids服务宕机,导致大量请求到达数据库,带来巨大压力。与缓存击穿的区别是:雪崩是很多key,而击穿是某一个key缓存。

7.缓存雪崩的解决方案

(1)方案一:给不同的key的TTL(失效时间)添加随机值

让不同的key的过期时间是不一样的就可以,例如可以在原有的失效时间上添加随机值。这样,每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

(2)方案二:搭建Redis集群提高服务的可用性,例如哨兵模式、集群模式。
(3)方案三:给缓存业务添加降级限流策略,例如ngxin或spring cloud gateway。
(4)方案四:给业务添加多级缓存,例如Guava或Caffeine

在这里插入图片描述

8.双写一致性

一定要设置前提,比如项目中要求一致性要求高,还是允许延迟一致,两者是不同的。

(1)定义

双写一致性:当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。由于数据库和缓存的读写操作可能并发执行,导致缓存与数据库不一致,Redis采取不同策略来解决该问题。
在这里插入图片描述

读操作:缓存命中,直接返回数据;缓存未命中查询数据库,返回之前将数据写入缓存,设定该数据的过期时间。

(2)双写一致性问题原因

①先删除缓存,再操作数据库

并发读写冲突:
当更新操作(如先删缓存再更新数据库)和查询操作并发执行时,查询可能读取到旧数据并重新写入缓存,导致缓存与数据库不一致。
时序问题:更新操作删除缓存后,数据库尚未完成更新,此时查询将旧数据重新加载到缓存。
​主从同步延迟:数据库主从同步需要时间,从库可能短暂存在旧数据,导致查询结果不一致。

正常情况下:
假设初始缓存=10,数据库=10;
线程1任务:对数据库进行更新操作,将数据库改为20。
线程2任务:查询数据。
线程1先删除缓存中存放的数据,现在缓存=空。然后线程1再去更新数据库,将其改为20;目前数据库=20,缓存=空。
在线程1更新完数据库后。线程2现在要进行查询操作。线程2查询缓存,未命中,再查询数据库,查到20,将20写入缓存。目前数据库=20,缓存=20,两者一致。
在这里插入图片描述

冲突情况下:
假设初始缓存=10,数据库=10;
线程1任务:对数据库进行更新操作,将数据库改为20。
线程2任务:查询数据。
线程1先去删除缓存中的数据。数据库还没有进行更改。目前数据库=10,缓存=空。等线程1要去进行下一步更新数据库时。线程2来了。
线程2查询缓存未命中,缓存=空,接着线程2去查询数据库,发现数据库=10,线程2就将该数据写入缓存。目前数据库=10,缓存=10。
线程2执行完查询操作后,线程1接着来进行更新数据库操作,将数据库改为20。目前数据库=20,缓存=10。数据不一致了,无法满足双写一致性,出现了脏读。
在这里插入图片描述

②先操作数据库,再删除缓存。
正常情况下:
假设初始缓存=10,数据库=10;
线程1任务:对数据库进行更新操作,将数据库改为20。
线程2任务:查询数据。
此时线程1先进行数据库操作,将数据库改为20,再删除缓存。目前数据库=20,缓存=空。
接着线程2在线程1删除缓存之后开始查询数据,查询缓存,未命中,去查询数据库得到20,接着写入缓存。目前数据库=20,缓存=20。两者一致。
在这里插入图片描述

冲突情况下:
正常情况下:
假设初始缓存=10,数据库=10;
线程1任务:对数据库进行更新操作,将数据库改为20。
线程2任务:查询数据。
线程2先查询数据,查询缓存,但是并发情况下,可能该数据过期了,线程2缓存未命中,将去查询数据库,获得数据库=10;接着线程1开始更新数据库,数据库=20,再删除缓存,目前缓存=空;线程1执行完毕,线程2将查询到的数据库10写入缓存,那么此时缓存=10,数据库=20,双写不一致。

先删缓存 vs 先更新数据库:无论哪种顺序,在高并发场景下都可能因线程切换导致脏数据残留。

(3)双写一致性问题解决方案1—延迟双删

在写操作采用延迟双删
在这里插入图片描述

①延迟双删原理

先删除缓存,然后进行更新操作,修改数据库,更新操作完成后,延迟一定时间再次删除缓存。

②为什么要删除两次缓存?

先删除缓存,之后再删除一次缓存,就是为了降低脏数据出现。

③为什么要延时呢?

因为数据库主从同步需要时间,一般情况下数据库是主从模式的,是读写分离的,所以需要延时,让主节点把数据同步到从节点。

④延迟双删的实现方式

定时任务:通过 ScheduledExecutorService 的 schedule() 实现,指定时间延迟删除Redis中的缓存。
消息队列:在更新操作之后,设置一条删除Redis中指定缓存的延迟消息,通过发送该延迟消息触发二次删除。

⑤延迟双删的特点

优点:
1)性能高,由于读和写是并发的,性能很高。
2)实现简单:定时任务和消息队列实现都比较简单。
3)保证了数据最终一致性,最终数据是一致的。
缺点:
1)无法保证数据的强一致性,且延迟时间需根据业务调整:由于延迟删除缓存的时刻可能与数据更新完毕(主从同步之后)的时刻间隔了不少时间,在这期间数据的一致性无法保障。

⑥延迟双删使用的场景

允许短暂不一致但对性能要求较高的场景(如文章浏览量统计)。

(4)双写一致性问题解决方案2—读写锁机制
①读写锁原理

通过Redission提供的共享锁(读锁)和排他锁(写锁)控制并发。

②实现过程。

共享锁:对于删除缓存操作即读操作,加共享锁,允许多线程读,但阻塞写操作。直到删除缓存执行完之后,释放锁。
排他锁:对于更新操作即写操作,加排他锁,阻塞其他读写操作,直到更新缓存之后,解锁。
在这里插入图片描述

③特点

优点:
1)强一致性保障。
缺点:
1)性能较低。

④使用场景

适用于对一致性要求极高但并发量适中的场景。

(5)异步消息队列(MQ/Canal)
①MQ异步通知原理

数据库更新完数据后,发送消息到MQ,而消费者也就是缓存服务器监听到消息后,更新缓存。
MQ异步通知特点:
优点:解耦数据库和缓存操作,支持最终一致性。
缺点:存在短暂延迟,高并发下会出现数据不一致的情况。
在这里插入图片描述

②基于Canal的异步通知原理

Canal是基于MySQL的主从同步来实现的。
MySQL进行更新操作后,数据库发生变化,将这种变化记录于BinLog文件中,Canal监听mysql的Binlog,然后解析binlog,发送消息给缓存,将数据变更情况告知给缓存服务器,接着再更新缓存。
Canal特点:
无代码入侵,适用于复杂系统。

二进制日志(BinLog)记录了所有的DDL(数据定义语言)语句和DML(数据操纵语言)语句,但不包括数据查询(Select、show)语句。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

9.数据持久化策略

(1)RDB
①定义

RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
save和bgsave是人工手动备份
在这里插入图片描述
Redis内部有触发RDB的机制,可以在redis.conf文件中找到,格式如下:
在这里插入图片描述

②RDB执行原理

简单理解:
bgsave开始时,主进程通过调用fork()函数创建子进程,子进程共享主进程的内存数据,子进程是异步的,对主进程几乎没有阻塞。完成fork后,子进程读取内存数据并写入RDB文件,此时内存为只读。fork采用copy-on-write技术,当主进程执行读操作时,访问共享内存;当主进程执行写操作时,拷贝一份内存数据,得到数据副本,在数据副本上执行写操作。

详细解析:
bgsave开始时,主进程通过调用fork()函数创建子进程,子进程共享主进程的内存数据,子进程是异步的,对主进程几乎没有阻塞。
有一个进程,Redis的主进程,要去实现对redis的读写操作,要在内存中去操作。但是在linux系统中,所有的进程都没有办法直接操作物理内存,操作系统给每一个进程都分配了一个虚拟内存,主进程只能操作虚拟内存。操作系统会维护虚拟内存和物理内存之间的映射关系表,这个表被称为页表。主进程操作虚拟内存,虚拟内存基于页表的映射,关联到物理内存真正的存储数据的位置,这样就可以对物理内存进行读和写操作。
子进程会拷贝主进程中的页表,也就是映射关系,子进程在操作自己的虚拟内存的时候,也会关联到物理内存。那这就实现了子进程和主进程的内存空间共享。
子进程读取到物理内存中的数据,就可以将数据写入磁盘中,生成新的RDB文件,替换旧的RDB文件。
子进程在写新的RDB文件时,主进程可能接收到写请求,会产生冲突。为避免这个冲突,fork采用了copy-on-write技术,写时复制。也就是将物理内存中的数据变为只读,不可以写。如果主进程接收到写请求,主进程将物理内存中的数据拷贝一份,得到数据副本,在副本上进行写操作。
页表:记录虚拟地址和物理地址的映射关系。

(2)AOF
①定义

AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令(修改数据的命令,如set)都会存储在AOF文件,可以看作是命令日志文件。
在这里插入图片描述

②详细解析AOF

AOF默认是关闭的,需要修改redis.conf配置文件来开启AOF
在这里插入图片描述
AOF的命令记录的频率也可以通过redis.conf文件来配
在这里插入图片描述
一般在项目中,我们都会采用everysec。

因为是记录命令,AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作,但只有最后一次写操作才有意义。通过执行bgrewriteaof命令,可以让AOF文件执行重写功能,用最少的命令达到相同的效果。
在这里插入图片描述

(3)RDB和AOF的对比

RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。
在这里插入图片描述
在这里插入图片描述

10.数据过期策略

(1)数据过期定义

Redis对数据设置数据的有效时间,数据过期以后,就需要将数据从内存中删除掉。可以按照不同的规则进行删除,这种删除规则就被称之为数据的删除策略(数据过期策略)。有两种策略,惰性删除和定期删除。
Redis的过期删除策略是惰性删除+定期删除两种策略配合使用

(2)惰性删除
①定义

惰性删除:设置该key过期时间后,我们不去管它,当需要该key时,我们再检查其是否过期,如果过期,我们就删掉它,反之就返回该key。
在这里插入图片描述

②优缺点

优点:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。
缺点:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放。

(3)定期删除
①定义

定期删除:每隔一段时间,我们就会对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)。不会存在已经过期的key还没有被删除的可能。

②清理模式

1)SLOW模式:是定时任务,执行频率默认为10hz(每秒执行10次,每个执行周期是100毫秒),每次不超过25毫秒【时间这么短是因为在清理过程中,要尽可能少的去影响主进程操作】,可以通过修改配置文件redis.conf的hz选项来调整这个次数。
2)FAST模式:执行频率不固定,但两次间隔不低于2毫秒,每次耗时不超过1毫秒。【尽可能少的去影响主进程操作】

③优缺点

优点:可以通过限制删除操作执行的时长和频率来减少删除操作对CPU的影响。另外定期删除,也能有效释放过期key占用的内存。
缺点:难以确定删除操作执行的时长和频率。
在这里插入图片描述

11.数据淘汰策略

加入缓存过多,内存是有限的,内存被占满了怎么办?
问的就是数据淘汰策略。

(1)数据淘汰策略定义

当Redis中的内存不够用时,此时在向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除掉,这种数据的删除规则被称之为内存的淘汰策略。

(2)八种淘汰策略
  1. noeviction: 不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略。
    在这里插入图片描述

  2. volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰

  3. allkeys-random:对全体key ,随机进行淘汰。

  4. volatile-random:对设置了TTL的key ,随机进行淘汰。

  5. allkeys-lru: 对全体key,基于LRU算法进行淘汰
    LRU:
    在这里插入图片描述
    LFU:
    在这里插入图片描述

在这里插入图片描述

  1. volatile-lru: 对设置了TTL的key,基于LRU算法进行淘汰
  2. allkeys-lfu: 对全体key,基于LFU算法进行淘汰
  3. volatile-lfu: 对设置了TTL的key,基于LFU算法进行淘汰

(3)淘汰策略使用建议

1)优先使用allkeys-lru策略,充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用。
2)如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用allkeys-random,随机选择淘汰。
3)如果业务中有置顶的需求,可以使用volatile-lru策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。
4)如果业务中有短时高频访问的数据,可以使用allkeys-lfu或volatile-flu策略。

在这里插入图片描述
在这里插入图片描述

(二)分布式锁

Redis分布式锁,是如何实现的。
需要结合项目中的业务进行回答,通常情况下,分布式锁使用的场景:集群情况下的定时任务、抢单、幂等性场景。

1.抢券场景

在这里插入图片描述
在这里插入图片描述

正常情况下:
线程1执行完之后,线程2开始执行,查询优惠券,不会出现问题。
在这里插入图片描述
冲突情况:
假设此时优惠券库存=1,线程1查询到优惠券库存=1,而此时线程2也查询到优惠券库存=1,接着线程1扣减库存,库存=0,线程1执行结束。线程2也扣减库存,那么此时,库存=-1,出现了超卖现象。
在这里插入图片描述
解决方案:
加锁。但是这种解决方案只适合单体项目,并且只开启一台服务。
在这里插入图片描述
在这里插入图片描述

但是我们的项目为了支撑更多的并发请求,往往将服务做成集群部署,同一份代码,部署在多个tomcat中.
当用户请求的时候,使用nginx做反向代理,负载均衡到各个请求去访问各个服务。那么此时就不适合加锁了。
在这里插入图片描述
在这里插入图片描述
此时使用的是本地的锁,只能解决同一个jvm下线程的互斥,解决不了多个jvm下线程的互斥。所以在集群的情况下,就不能使用本地的锁来解决,需要使用外部的锁来解决,也就是分布式锁。
在这里插入图片描述
使用了分布式锁以后,针对集群部署的项目,在服务器8080下,线程1加锁之后,分布式锁中就会记录服务器8080的线程1持有锁。那么服务器8081的线程1试图获取锁会失败,别的线程都会获取锁失败,只有等8080的线程1释放锁以后才可以获取到锁。

2.分布式锁的原理

(1)定义

Redis实现分布式锁主要利用Redis的setnx命令。setnx是 SET if not exists(如果不存在,则SET)的简写。
在这里插入图片描述
其中EX表示key的过期时间,在一条命令中设置可以保证原子性,不设置EX可能会导致死锁的问题。
在这里插入图片描述

(2)redis分布式锁控制锁的有效时长

Redis实现分布式锁如何合理地控制锁的有效时长?

方案一:根据业务执行时间预估。但是可能出现网络不稳定服务宕机的情况,也会自动释放锁,导致业务的原子性无法得到满足,业务不能很好地被执行。【该方案有欠缺】
方案二:给锁续期。单独开一个线程监控业务完成情况,如果业务不够时间完成,则延长加锁时间。【也有欠缺,很浪费】

最好的解决方案:
redisson实现的分布式锁

(3)redisson实现的分布式锁执行流程

线程1获取锁,加锁成功之后可以直接操作Redis执行业务,同时加锁成功之后会增加一个watch dog看门狗进行监听【每隔(releaseTime/3)的时间做一次续期】,看门狗会不断地去监听持有锁的线程,releaseTime就是锁的过期时间(默认30秒),也就是每隔10s,看门狗将锁的过期时间重新设置为30s。当业务执行完成以后,手动地释放锁,并且告诉看门狗不需要再进行监听了。
线程2也尝试获取锁,看是否加锁成功,但是线程1正在持有锁。在线程2中设置了一个while循环,不断尝试获取锁。如果线程1在很短的时间内释放了锁,那线程2可以获得加锁。在这个while循环设置了一个阈值,如果线程2尝试获取锁达到这个阈值,那么线程2会获取锁失败。
一般情况下,业务执行非常快,所以线程2不需要等待线程1很久。加入了这个while循环等待机制,在高并发的情况下,可以很大程度上增加分布式锁的使用性能。【这就是redisson的重试机制】
加锁、设置过期时间等操作都是基于lua脚本完成。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(4)Redisson实现的分布式锁—可重入

在这里插入图片描述
add1方法调用add2方法,其中add2方法是可以获取锁成功的,redisson实现的分布式锁是可以重入的。add1和add2方法都是同一个线程,每个线程在执行的时候,都有一个唯一的线程ID作为标识,加锁的时候根据这个线程ID来判断,如果是同一个线程,那么可以获取锁成功。如果不是一个线程,则获取不成功。

可重入的好处:当业务比较复杂的时候,锁的粒度比较细的时候,就可以用到重入。可以避免多个锁之间产生死锁的问题。

重入的实现:在存储锁数据的时候,采用hash结构,利用hash结构记录线程id和重入次数。这个key根据自己的业务进行命名,也就是锁的命名,field存储的是持有锁线程的唯一标识(线程ID),value存储的是当前线程重入的次数。

上面的代码中,加锁之后,存入到field中,将线程标识,然后add1中加锁,value=1,之后调用add2,先去field查看线程ID是否一致,一致的话,add2加锁成功,value=2。add2执行完业务之后,释放锁value-1=1。之后,add1执行完业务之后,释放锁value-1=0;
在这里插入图片描述
在这里插入图片描述

(5)Redisson实现的分布式锁—主从一致性

Redis的主从集群架构,有主节点和从节点,主节点主要负责写操作(更新操作),从节点负责对外的读操作,当主节点发生了写操作之后,就要将数据同步到从节点,因为要保证主从数据的同步。目前有java应用创建了一个分布式锁,因为是写操作,先找到主节点,将数据写入到主节点中,正常的话就是主节点同步到从节点,但是还没来得及同步数据,主节点宕机了。Redis提供的哨兵模式会在两个从节点中,选择一个从节点充当主节点,那么新的线程来了以后,会直接请求新的主节点,尝试获取锁,会加锁成功。那么这两个线程会同时持有同一把锁,那么此时就丧失了锁的互斥性,可能会出现脏数据的问题。
在这里插入图片描述

解决方案:
redisson提供了RedLock红锁:不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁(n/2+1),避免在一个redis实例上加锁。(n代表的是redis节点的数量)
下面有3个节点,那么3/2+1=2.5,至少要创建大于等于2个锁。
红锁也是有缺陷的,在实际项目中很少使用,主要是加了红锁之后,实现起来很复杂,尤其是在高并发的情况下,性能变得很差,因为需要提供多个独立的redis节点,运维繁琐。不是很支持用这个。
Redis集群的思想是AP思想,优先保证高可用性,可以做到最终一致。如果要做到强一致性,则考虑使用zookeeper的CP思想

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(三)计数器

(四)保存token

(五)消息队列

(六)延迟队列

二、其他面试题

(一)集群

Redis集群有哪些方案?知道嘛
三种:主从复制、哨兵模式、分片集群。D
在这里插入图片描述

1.主从复制

(1)定义

主从复制:单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。
主节点复制写操作,从节点复制读操作,因为Redis一般是读多写少,多个从节点,增加了并发能力。主节点需要将数据同步给从节点。
在这里插入图片描述

(2)主从数据同步原理
①主从全量同步

执行流程:

  1. 从节点执行replicaof命令,建立连接。
  2. 接着从节点向主节点请求数据同步。
  3. 主节点接收到之后,会判断该从节点是否是第一次同步。
  4. 如果该从节点是第一次和主节点建立连接,那么将返回主节点的数据版本信息给从节点。
  5. 从节点保持版本信息,主从版本保持一致。
  6. 接着主节点会执行bgsave,生成RDB文件。
  7. RDB文件生成之后,主节点向从节点发送RDB文件。
  8. 从节点接收到RDB文件后,会清空本地数据,加载RDB文件。
  9. 那么主节点在生成RDB文件时,可能接收到其他更改请求,那么就会导致主从数据不一致的问题。为了解决这个问题,主节点会记录RDB期间的所有命令,生成一个日志文件repl_baklog。
  10. 主节点 再将这个日志文件发送给从节点,发送repl_baklog中的命令给从节点。
  11. 从节点接收到该日志文件之后,会执行接收到的命令,那么就可以实现主从数据的完全同步。

问题解决:
1)主节点是怎么判断从节点是否是第一次同步呢?
从节点发起连接请求时,将自己的replid发送给主节点,主节点通过判断该从节点的replid和自己的replid是否一致,如果不一样,说明这个从节点是第一次进行连接。
如果一致,表明该从节点之前已经建立过连接。那么主节点就不会再生成RDB文件,直接执行repl-baklog的命令。
2)不是第一次建立连接(第二次、第三次同步),那么该执行多少这个日志文件中的命令呢?
根据offset,从节点发送自己的offset给主节点,主节点判断从节点的offset和自己是否一致,不一致的话,将不一致的部分发送给同节点进行同步。

在这里插入图片描述
在这里插入图片描述

②主从增量同步(salve重启或后期数据变化)

从节点重启之后,向主节点发起同步请求,附带有replid和offset两个值,主节点master判断请求replid是否一致,如果是第一次,则返回主节点replid和offset给从节点。如果不一致,不是第一次连接,回复continue,主节点去repl_baklog中获取offset后的数据,发送offset后的命令给从节点,从节点执行命令。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

主从复制保证不了redis的高可用,因为一旦主节点宕机之后,就无法执行写操作。在这里插入图片描述

2.哨兵模式

(1)哨兵的作用

Redis提供了哨兵机制来实现主从集群的自动故障恢复。哨兵也是redis节点,也由多个节点组成了集群,一般情况下至少要部署三台哨兵。

  1. 监控:哨兵会不断检查主节点和从节点是否按预期工作,监控集群状态。
  2. 自动故障恢复:如果主节点故障,哨兵会将一个从节点提升为主节点。当故障实例恢复后,也以新的主节点为主。
  3. 通知:哨兵充当Redis客户端的服务发现来源,当集群发生故障转移时,哨兵会将最新信息推送给Redis的客户端。例如,主节点宕机,更换新的主节点之后,哨兵会将新的主节点告诉给Redis的客户端,Redis的客户端会自动连接上新的主节点进行工作。

极大可能保持了Redis的高可用性。
在这里插入图片描述

(2)服务状态监控

哨兵(Sentinel)基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
在这里插入图片描述
在这里插入图片描述

其中指定数量quorum也是可以设置的,最好超过哨兵实例数量的一半。
哨兵选主规则:
在这里插入图片描述

(3)Redis集群(哨兵模式)脑裂

如果主节点和哨兵处于不同的网络分区,那哨兵只能去监测从节点,无法监测到主节点,那么哨兵就会在从节点当中根据选主规则,选择新的主节点。但是原本的主节点还存在,只是网络出现问题,客户端还可以正常连接。那这样子,就会出现两个master主节点,就像大脑分裂了一样。这个就是脑裂。
在这里插入图片描述

脑裂带来的问题:
由于原本的主节点还存在,客户端仍然在向原来的主节点写入数据,但是其他节点无法同步数据,因为网络异常。
如果网络恢复之后,哨兵会将原来的主节点强制降为从节点,依附新的主节点,那么这个从节点就会从新的主节点同步数据,就会将自身原本的数据清空,那脑裂之前,客户端写入的数据就丢失了。脑裂带来数据丢失。
解决方案:
主节点必须要有最少一个从节点,才可以接收客户端的数据,否则直接拒绝请求。
redis中有两个配置参数:
min-replicas-to-write 1 表示最少的salve节点为1个
min-replicas-max-lag 5 表示数据复制和同步的延迟不能超过5秒
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.分片集群

主从和哨兵可以解决高可用、高并发读的问题【主从复制解决高并发读的问题,哨兵模式解决高可用问题】。但是依然有两个问题没有解决:
海量数据存储问题
高并发写的问题。(采用分片集群解决)

(1)分片集群特征

使用分片集群可以解决上述问题,分片集群特征:
1.集群中有多个master,每个master保存不同数据
2.每个master都可以有多个slave节点
3.master之间通过ping监测彼此健康状态。每个master互相之间起到哨兵作用。
4.客户端请求可以访问集群任意节点,最终都会被转发到正确节点
在这里插入图片描述

(2)数据读写

Redis 分片集群引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。
可以设置key的有效部分,按照一定的规则,key选择存储到哪一个节点中。有相同的业务数据,都想进入到redis的同一个节点下,就可以设置相同的有效部分来存储。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(二)事务

(三)Redis为什么这么快

在这里插入图片描述
在这里插入图片描述

1.用户空间和内核空间

Linux系统中一个进程使用的内存情况划分两部分:内核空间、用户空间。
用户空间只能执行受限的命令(Ring3),而且不能直接调用系统资源,必须通过内核提供的接口来访问。
内核空间可以执行特权命令(Ring0),调用一切系统资源。
在这里插入图片描述
在这里插入图片描述
需要想办法减少等待时间,以及减少用户空间和内核空间之间数据的拷贝。

2.阻塞IO

阻塞IO就是两个阶段都必须阻塞等待。比较耗时,性能不高。
在这里插入图片描述

3.非阻塞IO

在这里插入图片描述

4.IO多路复用(Redis底层使用的就是这个)

IO多路复用:是利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
这个Socket指的是客户端的连接。
用户进程先调用select函数,可以监听一个Socket集合,里面包含了多个Sockets

在这里插入图片描述
IO多路复用是利用单个线程来同时监听多个Socket ,并在某个Socket可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。不过监听Socket的方式、通知的方式又有多种实现,常见的有:
select
poll
epoll。
在这里插入图片描述

5.Redis网络模型

单线程的
在这里插入图片描述
IO多路复用监听每个客户端的连接,每个连接可能处理不同的事件,IO多路复用只是针对已经就绪的连接,将这些事件进行派发。Redis中提供了多个事件处理器,这些事件处理器分别用于实现不同的网络通信请求。比如连接应答处理器,可以处理客户端请求的应答;命令回复处理器,处理客户端响应的;命令请求处理器,接收客户端的参数,接收请求数据,将数据转为Redis命令,选择并执行命令,把结果写入缓冲队列,放入缓冲区。这个是单线程的。
影响性能的永远是IO。也就是网路的读写,为了解决这个问题,引入了多线程。命令回复处理器也就是往外写,也加入了多线程。加入了多线程以后,大大提高了Redis对客户端的速度,主要是减少了网络IO导致的性能变慢的影响。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.Redis的数据持久化策略有哪些?
2.什么是缓存穿透,怎么解决?
3.什么是布隆过滤器?
4.什么是缓存击穿,怎么解决?
5.什么是缓存雪崩,怎么解决?
6.Redis双写问题是什么?
7.Redis分布式锁如何实现?
8.Redis实现分布式锁如何合理的控制锁的有效时长?
9.Reids的数据过期策略有哪些?
10.Redis的数据淘汰策略有哪些?
11.Redis集群有哪些方案,知道吗?
12.什么是Redis主从同步?
13.你们使用Redis是单点还是集群?哪种集群?
14.Redis分片集群中数据是怎么存储和读取的?
15.Redis集群脑裂是什么?
16.怎么保证Redis的高并发高可用?
17.你们用过Redis的事务吗?事务的命令有哪些?
18.Redis是单线程,但是为什么还那么快?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2315505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web后端开发之Maven

Maven Mven是apache旗下的一个开源项目,用来管理和构建java项目的工具。 通过一小段描述信息来管理项目。 Maven的作用 1.依赖管理:方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题 以前用某个jar包需要下载…

there are no enabled repos

我做了两个操作 第一个操作: 1.先在本地电脑,也就是在我们电脑的桌面上下载 https://repo.huaweicloud.com/repository/conf/CentOS-7-reg.repo 2.在CentOS 创建etc文件夹 3在etc文件夹内创建yum.repos.d文件夹 4.将下载好的repo 黏贴到yum.repos.d…

OpenEuler-22.03-LTS上利用Ansible轻松部署MySQL 5.7

一、需求 使用ansible自动化部署mysql二进制部署mysql部署mysql并创建JDBC用户 二、环境信息 本文涉及的代码,配置文件地址: 链接:百度网盘 请输入提取码 提取码:1g6y 软件名称版本备注Ansible2.9.27All modules — Ansible Doc…

前端无限滚动内容自动回收技术详解:原理、实现与优化

文章目录 一、核心需求与技术挑战1.1 无限滚动的问题症结1.2 自动回收的三大目标 二、技术实现原理2.1 虚拟滚动核心机制2.2 关键技术指标 三、完整实现方案3.1 基础HTML结构3.2 CSS关键样式3.3 JavaScript核心逻辑3.3.1 滚动控制器3.3.2 动态尺寸处理 四、性能优化策略4.1 内存…

如何在Ubuntu上构建编译LLVM和ISPC,以及Ubuntu上ISPC的使用方法

之前一直在 Mac 上使用 ISPC,奈何核心/线程太少了。最近想在 Ubuntu 上搞搞,但是 snap 安装的 ISPC不知道为什么只能单核,很奇怪,就想着编译一下,需要 Clang 和 LLVM。但是 Ubuntu 很搞,他的很多软件版本是…

【MySQL】表的约束(上)

文章目录 表的约束什么是表的约束空属性默认值列描述(comment)零填充(zerofill)主键 总结 表的约束 什么是表的约束 表的约束(Constraints)是数据库表中的规则,用于限制存储的数据&#xff0c…

静态分析技术:Jadx-GUI高级用法与模式识别

1. 深度反编译策略 1.1 多层级反混淆方案 代码恢复流程: graph TD A[混淆代码] --> B{符号恢复} B -->|字典匹配| C[变量重命名] B -->|类型推导| D[参数重构] C --> E[控制流优化] D --> E E --> F[语义化输出] 反混淆脚本示例&…

30天学习Java第六天——Object类

Object类 java.lang.Object时所有类的超类。Java中所有类都实现了这个类中的方法。 toString方法 将Java对象转换成字符串的表示形式。 public String toString() {return getClass().getName() "" Integer.toHexString(hashCode()); }默认实现是:完…

【C语言】编译和链接详解

hi,各位,让我们开启今日份博客~ 小编个人主页点这里~ 目录 一、翻译环境和运行环境1、翻译环境1.1预处理(预编译)1.2编译1.2.1词法分析1.2.2语法分析1.2.3语义分析 1.3汇编1.4链接 2.运行环境 一、翻译环境和运行环境 在ANSI C…

DataWhale 速通AI编程开发:(基础篇)第1章 环境下载、安装与配置

课程地址:Datawhale-学用 AI,从此开始 vscode 更新为最新版 目前绝大多数deepseek非官方渠道均兼容openai的api格式,这里以硅基流动为例进行演示,其他非官方渠道同理。 点击链接注册账号之后,点击“实名认证“完成实名&#xff0…

本地知识库RAG总结

目录 RAG流程: 知识库的要求: 知识抽取: 知识存储: 向量化: 知识检索: 应用客户端: RAG智能问答应用几个痛点: 如何提升召回率改进思路: 如何提升回答专业性: RAG评测: 总结: 参考…

torch_geometric 安装

环境监测: import torch print(torch.__version__) # 查看pytorch安装的版本号 print(torch.cuda.is_available()) # 查看cuda是否可用。True为可用,即是gpu版本pytorch print(torch.cuda.get_device_name(0)) # 返回GPU型号 …

网页打印很简单!用web打印插件lodop轻松实现文件打印

最近,给客户发一个事件提醒软件,其中客户要求实现打印功能,因为是用asp.net mvc 开发首先考虑到用水晶报表来实现(crystalReport),以前开发c# winform程序,感觉水晶报表还是蛮好的,但…

北京迅为iTOP-RK3568开发板OpenHarmony系统南向驱动开发实操-HDF驱动配置LED

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…

驻场运维服务方案书(Word文件)

目 录 第一章 背景分析 1.1. 项目背景 1.2. 项目目标 1.3. 系统现状 1.3.1. 网络系统 1.3.2. 设备清单梳理 1.3.3. 应用系统 第二章 需求分析及理解 2.1. 在重要日期能保障信息系统安全 2.2. 信息系统可长期安全、持续、稳定的运行 2.3. 提升发现安全问题、解决安全…

【时时三省】(C语言基础)用printf函数输出数据2

山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 格式字符 在输出时,对不同类型的数据要指定不同的格式声明,而格式声明中最重要的内容是格式字符。常用的有以下几种格式字符。 ( 1 ) d格式符。用来输出一个有符号的…

django框架 [面试篇]

Django 是一个基于 Python 的web框架,遵循"快速开发,不重复造轮子(dont repeat yourself)"的原则,帮助用户构建web应用。 而 Django 它本身提供了一些全栈式的一些组件,包括了 ORM,模板引擎,表单…

信息学奥赛一本通 1449:【例题2】魔板

题目 1449:【例题2】魔板 分析 首先注意:输入是按顺时针给出的,但我们处理时需要按正常顺序排,可以用以下代码读入 string s(8, 0); // 初始化全零字符串 cin>>s[0]>>s[1]>>s[2]>>s[3]; cin>>…

你的完美主义:从缺陷到超能力

所属专栏:《逻辑辨证系列》 前情回顾: 《完美还是完成》(一):完成还是完美—完成大于完美 时间、机会、情绪成本 先完成 … 本期: 《完美还是完成》(二):你的完美主…

浅谈Linux中的Shell及其原理

浅谈Linux中的Shell及其原理 Linux中Shell的运行原理github地址前言一、Linux内核与Shell的关系1.1 操作系统核心1.2 用户与内核的隔离 二、Shell的演进与核心机制2.1 发展历程2.2 核心功能解析2.3 shell的工作流程1. 用户输入命令2. 解析器拆分指令3. 扩展器处理动态内容变量替…