Redis篇

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

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

查询一个不存在的数据,redis中没有数据,每次请求都查数据库。

解决方案 :缓存空数据

布隆过滤器

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

缓存击穿:给某一个key设置过期时间,key过期的时候,有大量key请求过来,直接请求数据库。

解决方案 :互斥锁:多线程。

逻辑过期:不设置过期时间

线程1:查询缓存,发现逻辑过期时间,已过期,去获取互斥锁,然后开启新线程,

查询数据库,重建缓存数据,写入缓存跟过期时间,释放锁。

线程3,查询

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

缓存雪崩:同一时间内key大量过期,或者redis服务宕机,直接请求到数据库,带来巨大压力。

解决方案:给不同的Key的TTL添加随机值

利用Redis集群提高服务的可用性

给缓存业务添加降级限流策略

给业务添加多级缓存

顺口溜:

《缓存三兄弟》

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

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

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

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

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

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

双写一直性

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

写操作:延迟双删

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

RDB:RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据

RDB的执行原理?

bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。

fork采用的是copy-on-write技术:

当主进程执行读操作时,访问共享内存;

当主进程执行写操作时,则会拷贝一份数据,执行写操作。

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

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

定期删除:

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

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

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

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

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

Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用

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

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

Redis支持8种不同策略来选择要删除的key:

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算法进行淘汰

数据淘汰策略-使用建议

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

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

3.如果业务中有置顶的需求,可以使用 volatile-lru 策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据。

如果业务中有短时高频访问的数据,可以使用 allkeys-lfu 或 volatile-lfu 策略。

redids 分布式锁-使用场景

Redis实现分布式锁主要利用Redis的setnx命令。setnx是SET if not exists(如果不存在,则 SET)的简写。

底层基于redis的setnx命令做了改进封装,使用lua脚本保证命令的原子性

利用hash结构,记录线程标示和重入次数;

利用watchDog延续锁时间;

控制锁重试等待

Redlock红锁解决主从数据一致的问题(不推荐)性能差

如果业务非要保证强一致性,建议采用zookeeper实现的分布式锁

redids 集群方案

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

主从同步数据的流程:

全量同步:

1.从节点请求主节点同步数据(replication id、 offset )

2.主节点判断是否是第一次请求,是第一次就与从节点同步版本信息(replication id和offset)

3.主节点执行bgsave,生成rdb文件后,发送给从节点去执行

4.在rdb生成执行期间,主节点会以命令的方式记录到缓冲区(一个日志文件)

5.把生成之后的命令日志文件发送给从节点进行同步

增量同步:

1.从节点请求主节点同步数据,主节点判断不是第一次请求,不是第一次就获取从节点的offset值

2.主节点从命令日志中获取offset值之后的数据,发送给从节点进行数据同步

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

哨兵选主规则

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

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

如果slave-prority一样,则判断slave节点的offset值,越大优先级越高

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

redis集群脑裂,该怎么解决呢?

集群脑裂是由于主节点和从节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到主节点,所以通过选举的方式提升了一个从节点为主,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在老的主节点那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会将老的主节点降为从节点,这时再从新master同步数据,就会导致数据丢失

解决:我们可以修改redis的配置,可以设置最少的从节点数量以及缩短主从数据同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失

分片集群:

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

海量数据存储问题

高并发写的问题

使用分片集群可以解决上述问题,分片集群特征:

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

每个master都可以有多个slave节点

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

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

Redis分片集群中数据是怎么存储和读取的?

Redis 分片集群引入了哈希槽的概念,Redis 集群有 16384 个哈希槽

将16384个插槽分配到不同的实例

读写数据:根据key的有效部分计算哈希值,对16384取余(有效部分,如果key前面有大括号,大括号的内容就是有效部分,如果没有,则以key本身做为有效部分)余数做为插槽,寻找插槽所在的实例