八股文系列Redis

缓存穿透

缓存穿透指什么

缓存穿透是指查询⼀个在缓存和数据库中都不存在的数据。由于缓存没有这个数据,所以每次查询都会"穿透"缓存直接查询数据库,如果有⼤量此类查询,会给数据库带来极⼤的压⼒。

查询流程

解决方案

设置value为null

缓存空数据,查询返回的数据为空,仍把这个空结果进行缓存

  • 优点:简单
  • 缺点:消耗内存,可能导致不一致性
布隆过滤器

**bitmap(位图):**相当于是一个以 (bit) 位为单位的数组,数组中每个单元只能存储二进制数0或1。

布隆过滤器作用:布隆过滤器可以用于检索一个元素是否在一个集合中。

误判率:数组越小误判率就越大,数组越大误判率就越小,但是同时带来了更多的内存消耗。一般误判率是5%。

  • **布隆过滤器优点:**内存占用较少,没有多余key。
  • **布隆过滤器优点:**实现复杂,存在误判。

缓存击穿

概念

给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮

查询流程

解决方案

设置热点数据永久不过期

如果某些数据特别热⻔,访问的频率远远超过其他数
据,那么可以将这部分数据设置为永不过期,这样就可以避免⼤量请求突然落
到数据库上

使用互斥锁
  • 优点:强一致性
  • 缺点:性能差
  • 适用于与交易相关的业务

当线程1查询缓存的时候,未命中缓存,然后获取互斥锁成功。此时线程1去查询数据库,将数据写入缓存,最后释放锁。

在线程1查询缓存的时候,未命中缓存,然后线程2去获取互斥锁,获取失败,因为线程1获取到了互斥锁,此时线程2只能休眠一会再重试。当线程1写入缓存成功后并释放锁的同时,线程2重试命中缓存,命中成功直接获取缓存。

缓存雪崩

概念

缓存雪崩是指在同一时段大量的缓存key同时失效 或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

查询流程

解决方案

缓存数据的过期时间设置为随机

避免所有数据同时过期,不会出现瞬间⼤量的数据库请求。

使用多级缓存策略

使⽤⼀级缓存和⼆级缓存,⼀级缓存的失效时间短于⼆级缓存,⼀级缓存失效后,请求会访问⼆级缓存,⽽不是直接落到数据库。

缓存预热

在业务低峰期,对将要过期的缓存进行预热,预热的⽅式就是提前去更新数据。

使⽤高可用的缓存集群

即使某些节点挂掉,仍然可以保证系统的可⽤性。

redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)

概念:

  • 当修改了数据库的数据也要同时更新缓存的数据,缓存和数据库的数据要保持一致。

解决方案

延迟双删:

是指在更新数据库之前先删除缓存,然后再去更新数据库,更新数据库后,再去延时删除缓存。

​ 之所以要延迟删除缓存是因为大多数的数据库并不是一个,一个数据库存在数据库损坏、宕机等问题。数据库需要做主从同步的操作,把数据库(主数据库)的数据同步到另一个数据库(从数据库)中,以实现数据的备份、负载均衡、灾难恢复等功能。数据库在做主从同步时有延迟。所以延迟双删,需要等待主从同步完成。

​ 缺点:不能确定主从同步完成的时间,如果没有数据同步完成,查询缓存时容易出现脏数据,所以延迟双删有查询出脏数据的风险。

分布式锁(强一致性)

通过加锁的方式,保证执行时只有一个线程去操作,其他线程需要等待有锁的线程释放锁。(性能太低)

可以通过加共享锁、排它锁提高性能。

**共享锁:**其他线程可以共享读的操作,在读操作时不能进行写的操作。

**排它锁:**线程在加锁后对其他线程阻塞,不允许其他线程进行读写操作。

缺点:虽然数据保持了强一致性,但是性能低。适用于对数据要求必须一致的业务使用。

使用异步通知保证数据的最终一致性。

MQ(消息中间件)异步通知:

​ 当请求操作完数据库后,发送一个通知给MQ(消息中间件),在缓存服务中去监听MQ(消息中间件),收到通知后去更新Redis缓存。异步通知能够保证数据的最终一致性。主要是靠MQ(消息中间件)的可靠性。

基于Canal的异步通知:

​ 当MySQL数据库中的数据发生变化时(如插入、更新、删除操作),对应的binlog中会记录下这些数据变更事件,Canal接收到数据变更后,去通知缓存去更新数据。

好处:对于业务代码几乎是零侵入的。

数据过期策略

假如redis的key过期之后,会立即删除吗?

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

Redis数据删除策略-定时删除

节约内存,到时就删除,立即释放不必要的内存占用。

优点:

CPU压力较大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量。

缺点:

对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放 。

Redis数据删除策略-惰性删除

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

优点:

对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。

缺点:

对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存中,内存永远不会释放 。

Redis数据删除策略-定期删除

每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)

定期清理的两种模式:
  • SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf 的hz 选项来调整这个次数。
  • FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms。
优点:

可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。

缺点:

难以确定删除操作执行的时长和频率。

数据淘汰策略

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

八种不同的数据淘汰策略

  • noeviction: 不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略。
  • volatile-ttl: 对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰。
  • allkeys-random:对全体key ,随机进行淘汰。
  • volatile-random:对设置了TTL的key ,随机进行淘汰。
  • allkeys-lru: 对全体key,基于LRU算法进行淘汰。
  • volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰。
  • allkeys-lfu: 对全体key,基于LFU算法进行淘汰。
  • volatile-lfu: 对设置了TTL的key,基于LFU算法进行淘汰。

LRU算法与LFU算法

LRU(Least Recently Used) :最近最少使用。用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。

列如:key1是在3s之前访问的,key2是在9s之前访问的,删除的就是key2。
LFU (Least Frequently Used) : 最少频率使用。会统计每个key的访问频率,值越小淘汰优先级越高。

列如:key1最近5s访问了4次, ey2最近5s访问了9次,删除的就是key1。

淘汰策略使用建议

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

主从复制

单节点Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。

主从同步原理:

  1. 首先从节点执行replicaof命令用来建立连接, 然后从节点请求数据同步。主节点收到请求以后进行判断是否第一次进行同步,通过判断replid是否一致。因为刚开始每个数据库的replid都不同,只有当第一次同步后主从replid才会一致。因此主节点会根据replid判断是否是第一次同步。
  2. 如果是第一次同步,那么主节点会返回它的节点信息给从数据库,从节点保存版本信息以后,主节点会进行bgsave将redis的数据生成RDB文件,然后将RDB文件发送给从数据库。然后从数据库删除本地文件加载RDB文件。在生成RDB文件也需要时间,因此也会有主节点数据的写入,因此在生成RDB文件期间将写入的记录保存到repl_baklog中。再将repl_baklog中的命令发送给从节点,从节点收到后执行收到命令实现主从同步。
  3. 如果不是第一次同步,那么在第2步从节点会将自己的偏移量和replid发送给主节点。主节点判断replid不是第一次同步,就会将数据版本信息返回给从节点,里面也包括replid和offset。从节点然后保存这些信息。然后同步的时候不再发送RDB文件,而是将repl_baklog文件发送给从节点。至于发送多少呢?由偏移量计算觉得,比如从节点偏移量是50,主节点偏移量是80,那么主节点就会发送50-80这些偏移量的数据。当发送repl_baklog后,会将从节点的偏移量改变为最新即80。

增量同步流程:

  1. 从节点发送replid和offset后,主节点通过replid是不是第一次同步,如果不是第一次同步回continue给从从节点。
  2. 然后主节点去repl_baklog获取offset数据和从节点的offset进行对比,发送偏移量的数据给从节点。
  3. 从节点获取数据后进行同步。

全量同步流程:

  1. 从节点请求主节点同步数据 (replication id、 offset)。
  2. 主节点判断是否是第一次请求,是第一次就与从节点同步版本信息 (replication id和offset)
  3. 主节点执行bgsave,生成rdb文件后,发送给从节点去执行。
  4. 在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)。
  5. 把生成之后的命令日志文件发送给从节点进行同步。

哨兵模式,集群脑裂

哨兵的作用

Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:

  • 监控: Sentinel会不断检查您的master和slave是否按预期工作。
  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主。
  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端。

服务状态监控

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

  • 主观下线: 如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
  • 客观下线: 若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。

判断主节点下线流程

  1. 如果此时主服务器宕机,哨兵1检测到了,系统并不会⽴即进⾏failover(故障转移)过程
  2. 此时仅仅是哨兵1主管的认为主服务不可⽤,此现象为主观下线
  3. 当后续的哨兵也检测到主服务器不可⽤时,并且数量达指定数量时
  4. 哨兵之间就会进⾏⼀次投票,投票结果由1个哨兵发起,进⾏failover操作
  5. 切换成功之后,就会通过发布订阅模式,让各个哨兵把⾃⼰监控的从服务器切换主机,这个过程为客观下线

哨兵选主规则

  • 首先判断主与从节点断开时间长短如超过指定值就排该从节点。因为断开时间越长,丢失的数据越多,所以选择断开时间短的,丢失数据少的节点为主节点。
  • 然后判断从节点的slave-priority值,越小优先级越高。
  • 如果slave-prority一样,则判断slave节点的offset值,越大优先级越高。因为offset的值越大,说明该从节点与主节点的数据差越少,所以选offset大的为主节点。
  • 最后是判断slave节点的运行id大小,越小优先级越高。

故障转移过程

  1. Sentinel给备选的节点发送slaveof on one命令,让该节点成为Maskter
  2. Sentinel给其他slave发送 "slaveof ip 端⼝" 命令,开始从Master上同步数据
  3. 最后Sentinel将故障节点标记为slave(执⾏slaveof ip 端⼝命令),故障节点恢复以后也会成为新Master的slave

rdids的脑裂

假如此时因为网络的原因 ,将主节点和从节点分开来了。并且主节点是一个分区,其他从节点是另外一个分区。这个时候从节点分区就会从从节点选出一个主节点,然后就会出现两个master主节点,就好像脑裂了一样。

不过老的master没有挂,只是网络出现的问题,客户端还可以去对老的主节点进行写数据。这就是脑裂的现象。但是老的主节点写入的数据不能同步到新的主节点。

之后网络恢复了,哨兵会将老的master强制降为salve节点。这个savle就会送master中同步数据,就会把自己的数据清空然后同步master的数据。

然后客户端连接新的master,变成正常情况。不过丢失的数据依旧丢失了

解决方案

redis中有两个配置参数

  • min-replicas-to-write 1 表示最少的salve节点为1个。必须每个主节点至少有一个从节点,才可以接受客户端的请求,否则失败。
  • min-replicas-max-lag 5表示数据复制和同步的延迟不能超过5秒。

达不到要求就拒绝请求就可以避免大量的数据丢失

分片集群、数据读写规则

主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:

  • 海量数据存储问题
  • 高并发写的问题

分片集群特征

  • 集群中有多个master,每个master保存不同数据。
  • 每个master都可以有多个slave节点。
  • master之间通过ping监测彼此健康状态。
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点。

数据读写

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

对于每个master,会根据master的数量对哈希槽进行均分。然后对于每个写或者读的key经过CRC16的计算其hash值并取模16384就可以获取所在的集群位置。

相关推荐
·云扬·3 分钟前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德6 分钟前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫32 分钟前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i40 分钟前
完全卸载MariaDB
数据库·mariadb
期待のcode42 分钟前
Redis的主从复制与集群
运维·服务器·redis
纤纡.1 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn1 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露1 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星1 小时前
sql语言之分组语句group by
java·数据库·sql
符哥20081 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql