Redis缓存问题与缓存更新机制详解

2025-01-20 16:50

本文主要是介绍Redis缓存问题与缓存更新机制详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更...

一、缓存问题

1.1 缓存穿透

1.1.1 问题来源

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

1.1.2 解决方案

1.1.2.1 缓存空对象

  • 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。
  • 这样可以防止攻击用户反复用同一个id暴力攻击。

1.1.2.2 使用布隆过滤器

  • 类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。
  • 布隆过滤器的关键就在于hash算法和容器大小。

1.2 缓存击穿

1.2.1 问题来源

缓存击穿是指缓存某些热点数据失效(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。

1.2.2 解决方案

1.2.2.1 设置热点数据永远不过期

可以在刷缓存js时,设置热点数据不过期。

1.2.2.2 新增后台定时更新缓存线程(逻辑不过期)

后台新增一个缓存更新线程,缓存快要过期前刷新缓存时间,防止缓存失效。

1.2.2.3 使用分布式互斥锁

可以使用Redis提供的分布式互斥锁,保证只有一个请求查询数据库和更新缓存,其他请求阻塞等待缓存更新完成后在访问缓存。

1.2.2.4 接口限流与熔断,降级

重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。

1.3 缓存雪崩

1.3.1 问题来源

缓存雪崩是指Redis缓存不能正常提供服务了(阻塞、服务宕机、大面积缓存失效等造成),导致所有请求都落到了数据库上,增加了数据库压力或者导致数据库宕机。

1.3.2 解决方案

1.3.2.1 缓存过期时间随机

缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

1.3.2.2 分布式部署

采用分布式部署方式部署缓存,避免缓存服务单节点,同时将热点数据均匀分布在不同的缓存数据库中。

1.3.2.3 设置热点数据永远不过期

可以在刷缓存时,设置热点数据不过期。

1.3.2.4 接口限流与熔断,降级

重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。

二、缓存更新机制

2.1 缓存更新策略分类

内存淘汰超时剔除主动更新
说明重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。给缓存数据添加TTL时间,到期后自动删除缓存,下次查询时更新缓存编写业务逻辑,在修改数据的同时,更新缓存
一致性一般
维护成本

2.2 内存淘汰机制

2.2.1 noeviction

不淘汰,这是默认的淘汰策略;

当内存达到限制后,写请求python(set)会返回错误,读请求(get)和删除请求(del)可以继续进行

2.2.2 volatile-lru

内存不足时,在设置了过期时间的key中,优先删除最近最少使用的key

2.2.3 volatile-lfu

内存不足时,在设置了过期时间的key中,优先删除使用频率最少的key

2.2.4 volatile-ttl

内存不足时,在设置了过期时间的key中,优先删除存活剩余时间最少的key

2.2.5 volatile-random

内存不足时,在设置了过期时间的key中,随机删除某个key

2.2.6 allkey-lru

内存不足时,在全体key范围内,优先删除最近最少使用的key

2.2.7 allkey-lfu

内存不足时,在全体key范围内,优先删除使用频率最少的key

2.2.8 allkey-random

内存不足时,在全体key范围内,随机删除某个key

2.3 超时剔除

2.3.1 定时删除

设置一个定时任务,随机抽取部分过期时间的key,检查是否过期,过期了就清除掉

2.3.2 惰性删除

查询获取数据时,检查缓存是否过期,过期则删除,没过期不删除

Redis 默认采用惰性删编程除+定时删除结合的过期策略

2.4 主动更新

2.4.1 主动更新策略

2.4.1.1 Cache Aside Pattern

  • 由缓存的调用者
  • 在更新数据库的同时更新缓存

2.4.1.2 Read/Write Through Pattern

  • 缓存和数据库整合为一个服务,由服务来维护一致性。
  • 调用者调用服务,不用关心一致性问题。

2.4.1.3 Write Behind Caching Pattern

调用http://www.chinasem.cn者只操作缓存,由其他线程异步的将缓存数据持久化到数据库,最终保持一致。

在企业中使用最多的主动更新策略是 Cache Aside Pattern。也就是我们自己编码来保证数据的一致性。

2.4.2 主动更新策略需要考虑的三个问题

2.4.1 删除缓存还是更新缓存?

  • 2.4.1.1 删除缓存

更新数据库时让缓存失效,查询时再更新缓存。(延迟加载)一般选择这个方案。

这个方案比较合理一点,可以避免过多的无效写操作,缓存删除后,只要没人来查询这条数据,数据就不会被写入缓存,这样就可以避免大量无效的写操作

  • 2.4.1.2 更新缓存

每次更新数据库都更新缓存,无效写操作比较多。

这种方式的缺点很明显,举个例子:假如我更新了100次数据库,然后又同时更新了100次缓存,但是在更新的时候并没有人来查这个数据,那么我更新这100次缓存好像也没啥用吧,相当于前99次都是无用功,只有最后一次才是有用的。这就是无效写操作过多的原因。

2.4.2 如何保证缓存与数据库的操作同时成功或失败?

1)单体系统,将缓存与数据库操作放在一个事务中。

2)分布式系统,利用TCC等分布式事务方案。

2.4.3 先操作缓存还是数据库?

  • 2.4.3.1 先删除缓存,再操作数据库

Redis缓存问题与缓存更新机制详解

这种方式存在很明显的问题,假设有两个并发操作,线程A更新,线程B查询。线程A先删除缓存,然后还没来得及更新数据库,CPU资源被线程B抢走,线程B查询缓存发现没有命中(因为已经被线程A删除了www.chinasem.cn),查询数据库,然后把结果写入到缓存中。这个时候线程A终于抢到CPU资源了,然后更新数据库,此时就会造成数据不一致问题。

  • 2.4.3.2 先操作数据库,再删除缓存

Redis缓存问题与缓存更新机制详解

这种处理方式使用的频率是最高的,因为出错的概率非常小,只有一种比较极端的情况才会出现数据一致性问题。

同样有两个并发请求,线程A查询、线程B更新,当线程A查询的时候,缓存刚好失效,然后就去查询数据库拿到数据,在准备写入缓存的时候,CPU资源被线程B抢走,线程B开始更新数据库,然后删除缓存(这一步其实等于无用,因为缓存已经过期)。此时线程A再次获取到CPU资源,然后写入缓存,此时写入的是更新前的旧数据,会产生数据一致性问题。

看起来这确实也是一个问题,但是我们仔细分析一下这种情况都需要满足哪些条件:

  • 1)并发读写操作
  • 2)读缓存时,缓存刚好失效
  • 3)写数据库操作要比写缓存快

写数据库是操作磁盘,写缓存是操作内存的,所以不太可能会出现写磁盘的速度快于写内存的。因此使用这种方式出现数据一致性的概率是很小的。

  • 2.4.3.3 延时双删策略

Redis缓存问题与缓存更新机制详解

延迟双删策略是分布式系统中数据库存储和缓存数据保持一致性的常用策略,但它不是强一致。其实不管哪种方案,都避免不了Redis存在脏数据的问题,只能减轻这个问题,要想彻底解决,得要用到同步锁和对应的业务逻辑层面解决。

前面两种方案的不足点我们进行了分析,第二种方式的使用频率比较高,但是也有一些小缺陷,虽然说发生的概率很低,但是这个概率到了线上会不会发生也不好说,所以就有了延时双删策略对第二种方式做补充。

所谓延时双删就是先进行缓存清除,再执行数据库操作,最后(延迟N秒)再执行缓存清除。延迟N秒的时间要大于一次写操作的时间,这个延时N秒就是了完善保证第二种策略中不足,可以保证线程A的写缓存和线程B的修改数据库、删除缓存都执行完毕,然后再删除缓存一次,就可以保证后面再来的查询请求可以查询到最新数据。

ps: 一般的延时时间设置为3S左右,具体情况要根据业务场景取最佳值。

2.5 缓存更新机制总结

  • 1)内存淘汰:不用自己维护,利用Redis内存淘汰机制,自动删除部分缓存数据,这些被删除的数据在下一次被查询时更新。这种方式一致性最差。
  • 2)超时剔除:给缓存数据加上过期时间 ,到期后自动删除,下次查询时更新,数据一致性问题大概率会出现。维护成本比较低。
  • 3)主动更新:编写业务逻辑,在修改数据库的同时更新缓存,一致性比较好,维护成本比较高。一般采用先操作数据库再更新缓存的方式。

一般在数据一致性要求比较低的场景下可以使用内存淘汰机制,比如商城首页的分类信息,这些东西基本上是不会变化的。如果一致性要求比较高,我们可以采用主动更新+超时剔除兜底的方式来处理。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持China编程(www.chinasem.cn)。

这篇关于Redis缓存问题与缓存更新机制详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153141

相关文章

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

IDEA和GIT关于文件中LF和CRLF问题及解决

《IDEA和GIT关于文件中LF和CRLF问题及解决》文章总结:因IDEA默认使用CRLF换行符导致Shell脚本在Linux运行报错,需在编辑器和Git中统一为LF,通过调整Git的core.aut... 目录问题描述问题思考解决过程总结问题描述项目软件安装shell脚本上git仓库管理,但拉取后,上l

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash