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。

相关推荐
伤不起bb4 小时前
Redis 哨兵模式
数据库·redis·缓存
卑微的Coder4 小时前
Redis Set集合命令、内部编码及应用场景(详细)
java·数据库·redis
2501_915373884 小时前
Redis线程安全深度解析:单线程模型的并发智慧
数据库·redis·安全
呼拉拉呼拉4 小时前
Redis知识体系
数据库·redis·缓存·知识体系
霖檬ing4 小时前
Redis——主从&哨兵配置
数据库·redis·缓存
卜及中7 小时前
【Redis/2】核心特性、应用场景与安装配置
数据库·redis·缓存
fat house cat_9 小时前
【redis】线程IO模型
java·redis
敖云岚10 小时前
【Redis】分布式锁的介绍与演进之路
数据库·redis·分布式
让我上个超影吧13 小时前
黑马点评【基于redis实现共享session登录】
java·redis