Redis 知识点一

参考
Redis - 常见缓存问题 - 知乎
Redis的缓存更新策略 - Sherlock先生 - 博客园
三种缓存策略:Cache Aside 策略、Read/Write Through 策略、Write Back 策略-CSDN博客

1.缓存问题

1.1.缓存穿透

大量请求未命中缓存,直接访问数据库。

解决办法:

1)对请求进行校验,不合理的直接返回

2)将查询不到的数据也存入缓存,但是过期时间设置短一点

3)设置布隆过滤器

1.1.1.布隆过滤器

数据结构

位数组(Bit Array):初始全为0的二进制位序列。

哈希函数集合:多个独立哈希函数(如 hash1, hash2, hash3)。

操作流程

添加元素:

对元素应用所有哈希函数,得到多个索引位置。

将位数组中对应索引位置设为1。

查询元素:

对元素应用所有哈希函数,得到多个索引位置。

若任一位置为0:元素一定不存在。

若所有位置为1:元素可能存在(存在误判概率)。

操作命令

SETBIT key offset value

GETBIT key offset

1.2.缓存雪崩

大量缓存同时过期,导致请求直击数据库。

解决办法:

1)给所有的缓存设置不一样的过期时间

2)构建多级缓存。如:本地cache、redis、数据库

1.3.缓存击穿

某个热点缓存过期或者被移出,导致大量请求直击数据库。

解决办法:

1)对热点数据加分布式锁

2)后台异步续期

3)多缓存策,使用备份缓存,当主缓存失效,则查询备份缓存,若备份缓存中存在,再更新到主缓存。

1.4.缓存污染

大量不经常被访问的数据把缓存占满了。

1.4.1.淘汰策略

noeviction

该策略是Redis的默认策略。在这种策略下,一旦缓存被写满了,再有写请求来时,Redis 不再提供服务,而是直接返回错误。这种策略不会淘汰数据,所以无法解决缓存污染问题。一般生产环境不建议使用。

allkeys-lru

从所有键中使用LRU(Least Recently Used,最近最少使用)算法淘汰键。

适用场景:缓存场景中,保留频繁访问的键,逐出很少被访问的键。

volatile-lru

从设置了过期时间的键中使用LRU算法进行淘汰。

适用场景:缓存一些有过期时间的数据,并根据访问频率进行内存管理。

allkeys-random

从所有键中随机选择并删除某个键。

适用场景:缓存数据访问频率没有明显差异的情况。

volatile-random

从设置了过期时间的键中随机选择并删除某个键。

适用场景:缓存带有过期时间的数据,且删除哪个数据不重要的场景。

volatile-ttl

在设置了过期时间的键中,根据过期时间的先后进行删除,越早过期的越先被删除。

适用场景:希望优先清理即将过期的数据的场景。

allkeys-lfu(Redis 4.0+)

从所有键中使用LFU(Least Frequently Used,最不常用)算法淘汰键。

适用场景:需要根据使用频率进行淘汰的场景,适用于访问频率有明显差异的数据集。

volatile-lfu(Redis 4.0+)

从设置了过期时间的键中使用LFU算法进行淘汰。

适用场景:与volatile-lru类似,但更关注使用频率。

1.4.2.过期键处理方式

惰性处理:当访问到了这个key再去删除

定时处理:定时遍历所有的key,找到过期的key并删除。

定时依次遍历所有的 DB,默认每 100ms 执行一次。

从 DB 的过期列表中随机取20个 Key ,判断是否过期,如果过期,则清理。

如果有5个以上的 Key 过期,则重复步骤2,否则继续处理下一个 DB 。

在清理过程中,如果达到 CPU 的 25% 时间,退出清理过程。

2.内存和数据库一致性问题

2.1.延迟双删-读多写少

1)删除缓存

2)更新数据库

3)过一段时间后再一次删除缓存

2.2.异步写回-写多读少

讲多次写入归并到缓存队列中,定时的将缓存中的数据计算之后写入数据库。

2.3.写操作时,为什么是删除缓存,而不是更新缓存?

1多线程并发修改的问题,例:

线程A更新数据库(值V1→V2)。

线程B更新数据库(值V2→V3)。

线程B更新缓存(值V3)。

线程A更新缓存(值V2)。

2 更新缓存会面临多次无效写操作(多次对该数据进行写时,缓存中的值会多次update,实际有意义的值只有最后一次写时更新的值),而删除逻辑就更加简单。

2.4.先改数据库还是先删缓存?

2.4.1.如果先删缓存

失败,则直接抛出异常,不存在不一致问题

成功,但是数据库修改失败,则还需更新缓存,可能导致不一致。

成功,数据库也成功,仍然存在不一致问题,例:

A线程删除缓存

B线程查询发现内存不存在,则查询数据库,并将数据库的值存入缓存

A线程更新数据库

在先删缓存,再改数据库的情况下,即使都成功了,仍旧可能存在不一致问题,如何解决?

延迟双删

2.4.2.如果先改数据库

失败,则直接抛出异常,不存在不一致问题

成功,但是缓存删除失败,存在不一致问题

成功,缓存删除也成功,不存在不一致问题

数据库修改成功,但是缓存删除失败,如何处理?

失败重试:失败后,通过消息队列重新删除。

异步更新:监听日志,监听到修改操作,则执行删除任务,如果删除失败,再发送消息队列。

2.5.缓存更新策略

2.5.1.Cache Aside(旁路缓存)-读多写少

应用程序同时和数据库/缓存交互。

读:

如果命中缓存,则直接返回。

如果没命中,则查询数据库,回写缓存。

写:

先更新数据库,再删除缓存。

2.5.2.Read/Write Through(读写穿透)

应用程序只和缓存交互。

读:

如果命中缓存,则直接返回。

写:

如果缓存中存在,则直接更新缓存,再通过缓存组件,同步更新到数据库。

如果缓存没命中,则直接更新数据库,然后返回。

2.5.3.Write Back(写回)-写多读少

Write Back(写回)策略在更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。

3.Redis发布订阅模式

3.1.基于channel的发布订阅

订阅者可以订阅多个channel,一个channel也可以有多个订阅者。

发布者向channel发布消息后,channel的所有订阅者都能收到消息。

订阅者只能收到订阅之后的消息,订阅前发布的消息接收不到。

发布:publish

订阅:subscribe

实现原理:

字典+链表

每个channel作为一个字典项。字典项后面跟着一个链表,链表上的每个节点就是一个订阅者。

3.2.基于pattern的发布订阅

通过通配符订阅,多个channel

发布:publish

订阅:psubscribe

Subscribe:订阅单个channel

Psubscribe:通过通配符,订阅多个channel

实现原理:

基于链表实现,每个订阅者都是链表上的一个节点,节点描述了订阅的pattern。

相关推荐
007php0071 天前
百度面试题解析:微服务架构、Dubbo、Redis及其一致性问题(一)
redis·百度·docker·微服务·容器·职场和发展·架构
长安城没有风1 天前
从入门到精通【Redis】Redis 典型应⽤ --- 分布式锁
数据库·redis·分布式
爬山算法1 天前
Redis(69)Redis分布式锁的优点和缺点是什么?
数据库·redis·分布式
2401_837088502 天前
Redis通用命令
数据库·redis·缓存
zl9798992 天前
Redis-缓存问题(穿透、击穿、雪崩)
redis
来旺2 天前
互联网大厂Java面试全解析及三轮问答专项
java·数据库·spring boot·安全·缓存·微服务·面试
摇滚侠2 天前
Spring Boot 3零基础教程,yml文件中配置和类的属性绑定,笔记15
spring boot·redis·笔记
摇滚侠2 天前
Spring Boot 3零基础教程,WEB 开发 HTTP 缓存机制 笔记29
spring boot·笔记·缓存
cr7xin2 天前
基于Session和Redis实现短信验证码登录
数据库·redis·缓存
DemonAvenger2 天前
深入浅出 Redis 布隆过滤器:从原理到实战,10 年经验总结
数据库·redis·性能优化