超强Redis基础学习 优化 使用 常见问题

问题大纲

为什么Redis可以这么快?

它是纯内存操作,内存本身就很快

其次,它是单线程的,Redis服务器核心是基于非阻塞的IO多路复用机制,单线程避免了多线程的频繁上下文切换问题

Redis的持久化机制

Redis提供了持久化机制给我们用,分别是RDB和AOF

RDB指的就是:根据我们自己配置的时间或者手动去执行BGSAVE或SAVE命令,Redis就会去生成RDB文件,实际上就是一个经过压缩的二进制文件,Redis可以通过这个文件在启动的时候来还原我们的数据

AOF则是把Redis服务器接收到的所有写命令都记录到日志 Redis重跑一遍这个记录下的日志文件,就相当于还原了数据

RDB会执行SAVE或BESAVE命令,redis单个线程处理会导致低效率吗?

BGSAVE命令实际上会fork出一个子进程来进行完成持久化(生成RDB文件)

在fork的过程中,父进程(主线程)肯定是阻塞的。fork完之后,是fork出来的子进程去完成持久化,因此即使是单线程,效率也不会很低

其实Redis在较新的版本中,有些地方都使用了多线程来进行处理, 只不过,核心的处理命令请求和响应还是单线程

主从架构

为了Redis「高可用」,现在基本都会给Redis做「备份」:多启一台Redis服务器,形成「主从架构」

「从服务器」的数据由「主服务器」复制过去,主从服务器的数据是一致的

如果主服务器挂了,那可以「手动」把「从服务器」升级为「主服务器」,缩短不可用时间

「主服务器」是如何把自身的数据「复制」给「从服务器」的呢

「复制」也叫「同步」,在Redis使用的是「PSYNC」命令进行同步,该命令有两种模型:完全重同步和部分重同步

如果是第一次「同步」或者切换目标同步,数据差距过大,那就会采用「完全重同步」模式进行复制;如果只是由于网络中断,只是「短时间」断连,那就会采用「部分重同步」模式进行复制

(假如主从服务器的数据差距实在是过大了,还是会采用「完全重同步」模式进行复制)

缓存穿透

原因:入侵者大量查询不存在的数据 使得Redis不断去访问数据库 然而Redis也无法缓存,就导致每次都会查询数据库...数据库的并发度不高 就会宕机

解决办法

布隆过滤器:作用:拦截不存在的数据

布隆过滤器 原理:把数据的id通过多次哈希计算标记数组,新来个数据就计算哈希,看是否能验证标记,但是会有以下的误判情况

缓存击穿

某一个key设置了过期时间,当key过期的时候,恰好对这个key有大量的并发请求,导致所有的请求都落到数据库上 这些请求可能会瞬间把DB压垮

解决方案

互斥锁
逻辑过期:数据不一定完全准确,但性能方面很优

缓存雪崩

统一时段内大量的缓存key同时失效或者Redis服务

双写一致性

有两种考察情况:

一种是要求实时性很高的

一种是允许延时一致(即延时以后再数据一致,不严格要求)

延迟双删---延时一致方案

有缺陷,但是面试爱问,如果只删除一次缓存,那么无论什么顺序都有可能脏读

而且,其实三步清除缓存也仍然有概率脏读

MQ很适用于延时一致的业务,具体用以下方式实现

互斥锁方案---要求强一致性的方案

共享锁(读锁):加锁以后只能共享读操作

排他锁(写锁):加锁以后阻塞其他读写操作,仅当前线程进行写操作

Redis集群

使用集群的原因

redis服务器升级至一定程度后 持久化的成本过高

当 Redis 需要持久化数据时,它需要将数据从内存中写入磁盘,这个过程会产生一定的性能开销。

Redis 的 RDB(Redis DataBase)持久化机制是通过 fork 子进程来实现的。当 Redis 需要持久化数据时,它会 fork 一个子进程,然后让子进程将数据写入一个临时文件。当持久化过程完成后,Redis 会使用这个临时文件来替换旧的 RDB 文件。

然而,如果 Redis 使用的内存过大,fork 子进程的过程可能会导致主线程阻塞时间过长。这是因为 fork 子进程需要复制父进程的内存空间,如果内存空间过大,这个过程就会变得非常耗时。因此,如果 Redis 使用的内存过大,就可能会导致主线程在 fork 子进程时阻塞过长时间,从而降低了 Redis 的性能。

集群解决方案

Redis Cluster是一个用于实现Redis集群的解决方案,它能够避免哨兵机制复杂的Master监控与选举操作,并方便地实现数据的分片处理,以提供更加高效的Redis解决方案。

Redis Cluster把所有的数据划分为16384个不同的槽位,并可以根据机器的性能把不同的槽位分配给不同的Redis实例。每个节点都与其他节点有关联,只需获得一个节点的信息,其他节点的信息也就可以获取到。

这些槽位被称作哈希槽,通过hash算法分配槽位,放入要缓存的数据

每个实例都会向其他实例/传播/自己负责的哈希槽,这样每台redis实例都记录有所有的关系信息了! 有了这样的映射关系,客户端也就知道去哪个实例上操作了

集群新增或者删除实例

当发生了删除或者新增时, 那么redis实例负责的哈希槽关系会发生变化

但是客户端是无感知的,因为客户端会发送请求到原来的redis实例

原来的实例会给出moved命令,告诉客户端重定向

客户端收到以后就会去新的实例请求, 并且更新本地缓存

总的来说就是

如果集群Redis实例存在变动,由于Redis实例之间会「通讯」

所以等到客户端请求时,Redis实例总会知道客户端所要请求的数据在哪个Redis实例上

如果已经迁移完毕了,那就返回「move」命令告诉客户端应该去找哪个Redis实例要数据,并且客户端应该更新自己的缓存(映射关系)

如果正在迁移中,那就返回「ack」命令告诉客户端应该去找哪个Redis实例要数据

为什么是16384个哈希槽?

「服务端 路由」的大致原理

服务端路由一般指的一种代理层, 专门对接客户端的请求 ,然后转发到Redis集群进行处理

现在比较流行的是Codis

它与Redis Cluster最大的区别是,Redis Cluster是直连Redis实例,而Codis则客户端直连Proxy,再由Proxy进行分发到不同的Redis实例进行处理

在Codis对Key路由的方案跟Redis Cluster很类似,Codis初始化出1024个哈希槽,然后分配到不同的Redis服务器中 哈希槽与Redis实例的映射关系由Zookeeper进行存储和管理

扩容Codis Redis实例的流程是怎么样的?

1.「原实例」某一个Solt的部分数据发送给「目标实例」。2.「目标实例」收到数据后,给「原实例」返回ack。3.「原实例」收到ack之后,在本地删除掉刚刚给「目标实例」的数据。4.不断循环1、2、3步骤,直至整个solt迁移完毕

Codis也是支持「异步迁移」的,针对上面的步骤2,「原实例」发送数据后,不等待「目标实例」返回ack,就继续接收客户端的请求。

未迁移完的数据标记为「只读」,不会影响到数据的一致性。如果对迁移中的数据存在「写操作」,那会让客户端进行「重试」,最后会写到「目标实例」上

相关推荐
ruanjiananquan996 分钟前
c,c++语言的栈内存、堆内存及任意读写内存
java·c语言·c++
西岭千秋雪_29 分钟前
Redis性能优化
数据库·redis·笔记·学习·缓存·性能优化
chuanauc33 分钟前
Kubernets K8s 学习
java·学习·kubernetes
一头生产的驴1 小时前
java整合itext pdf实现自定义PDF文件格式导出
java·spring boot·pdf·itextpdf
YuTaoShao1 小时前
【LeetCode 热题 100】73. 矩阵置零——(解法二)空间复杂度 O(1)
java·算法·leetcode·矩阵
zzywxc7871 小时前
AI 正在深度重构软件开发的底层逻辑和全生命周期,从技术演进、流程重构和未来趋势三个维度进行系统性分析
java·大数据·开发语言·人工智能·spring
Uluoyu1 小时前
redisSearch docker安装
运维·redis·docker·容器
YuTaoShao3 小时前
【LeetCode 热题 100】56. 合并区间——排序+遍历
java·算法·leetcode·职场和发展
程序员张33 小时前
SpringBoot计时一次请求耗时
java·spring boot·后端
llwszx6 小时前
深入理解Java锁原理(一):偏向锁的设计原理与性能优化
java·spring··偏向锁