缓存问题解决方案

《服务器开发技术、方法与实用解决方案》

一、缓存预热

在系统刚启动或活动刚开始时,如果缓存中没有数据,那么大量请求将直接访问数据库。如果瞬时访问流量巨大,则可能导致数据库因过载而宕机,甚至引发系统雪崩。因此需要将缓存的数据加载到缓存系统中,以减轻数据库的压力

1. 应用启动预热策略

本地缓存,在应用启动时进行缓存预热。如在Spring中实现InitializingBean接口中的afterProperitesSet方法实现

但是该策略仅适合预热本地缓存,且与应用深度耦合

2.任务调度预热策略

如果预热场景涉及的数据规模非常大,可以采用任务调度预热

使用分布式任务调度中间件定时、周期触发缓存预热数据。常用的中间件有阿里的SchedulerX、蚂蚁的AntScheduler、XXL-JOB、ElasticJob

分布式任务调度中间件的基本架构:

  • Scheduler Console:控制台。可视化集中管理平台
  • Scheduler Server:服务端,负责客户端任务的调度触发、任务执行状态的监测、集群间任务分配等
  • Scheduler Client:客户端,多个,负责具体任务执行

预热步骤

  1. 数据分片:将需要处理的数据按照一定规则划分为若干份
  2. 数据加载:根据客户端业务服务器的数量、机房属性等将分片单元拆分为若干份,指派到多台客户端业务服务器中
  3. 数据处理:客户端根据任务调度中间件下发的业务参数(id)访问数据库,并将读取的商品数据写入分布式缓存

周期调度:数据规模巨大时,无法通过一次调度完成缓存预热,可以采用周期调度,指定每个调度周期预热的数据量。为了避免在数据加载环节重复服务数据,需要对已预热过的数据打标,如更新预热时间、预热版本号等。在加载数据时,通过标记来识别、加载数据

3. 模拟请求预热策略

任务调度预热和应用启动预热都需要定制开发,对业务应用有入侵。

可以采用模拟请求预热,通过模拟用户请求来实现数据预热,如模拟调用数据查询接口。该方式对业务应用的侵入非常小

4. 缓存预热思路

  1. 分析业务场景和用户行为来圈定需要预热的数据范围
  2. 根据待预热数据的特定设计合适的预热策略
  3. 根据业务需要为不同的预热策略编排执行时间

二、缓存淘汰

缓存大多是基于内存实现的,空间相对有限。为了高效利用存储空间,当缓存触及设定的空间上限时,通常需要借助淘汰算法将部分数据移除

1. LRU

LRU(最近最少使用)算法根据数据的历史访问记录优先淘汰最近未被使用的数据,核心思想为:如果数据最近被访问过,那么将来被访问的频率会更高。

实现一般采用Hash表+双向链表,将被访问的数据放到链表头部,淘汰链表尾部的数据

LRU算法简单、高效,但是一些偶发性、周期性的批量操作会导致LRU算法的缓存命中率急剧下降。如一次性读取大量数据块,这些数据块会占用大量空间,并淘汰最近最少使用的数据。但是这些数据并非是经常被访问的数据,从而导致缓存命中率低下

2. LFU

LFU(最近最不常用)算法根据数据的历史访问频次来淘汰数据,核心思想为:如果数据最近被使用的频率高,则将来大概率会被再次使用

LRU倾向于保留最近使用的数据,LFU倾向于保留使用频率较高的数据。LFU能避免偶发性情况对命中率的影响,但是一旦访问内容发生较大变化,LFU需要更长的时间来适应,因为历史的频次记录会使已无用的数据保留较长一段时间

3. ARC

ARC(自适应缓存替换)算法兼具LRU和LFU的优点,既可以根据时间进行优化,缓存最近使用的数据,又可以根据频率进行优化,同时还能根据负载来灵活调整缓存策略。ARC的命中率要显著优于LRU和LFU。

实现原理见P218

三、缓存更新

1. Cache Aside

  • 读场景:先读缓存、若缓存命中则从缓存取数据,直接返回;若缓存未命中,则从数据库读取数据,成功后将数据放到缓存中
  • 写场景:先更新数据库,再失效缓存
  1. 是否可以先更新缓存,再更新数据库
    一旦出现缓存成功而更新数据库失败的情况,会导致缓存错误值,导致业务上无法接受的一致性问题
  2. 是否可以先失效缓存,再更新数据库
    通常数据库的写操作耗时大于读操作,一旦更新数据库期间有读请求并未命中缓存,会导致写回的数据为脏数据
  3. 为什么是失效缓存,而不是更新缓存
    如A、B两个线程都执行写操作,A线程早于B执行(B数据是最新的),但A线程晚于B线程更新缓存,这样就会导致缓存中的数据是A线程更新的旧值,而不是B线程更新的最新值
  4. 先更新数据库,再失效缓存是否存在问题?
    可能存在更新数据成功,失效缓存失败,导致缓存脏数据的问题。这种场景最常用的解决方案是为缓存设置失效时间,到期自动失效

事实上,保证缓存和数据库的强一致性是非常困难的,一般须通过Paxos或2PC来保证一致性,但这些协议耗时或实现复杂,实用性不高。在大多数使用缓存场景中,通常有一个重要前提,可接受弱一致性

2. Read/Write Through 模式

Read/Write Through 模式将更新数据库的操作交由缓存代理,从应用的角度看,缓存是主存储,应用层的读写操作均面向缓存,缓存服务代理对数据库的读写

  • Read Through:若缓存未命中,则由缓存服务负责加载数据写入缓存
  • Write Through:数据更新时,若没有命中缓存,则直接更新数据库;如命中缓存,则更新缓存,由缓存服务更新数据库(同步更新)

3. Write Behind Caching

Read/Write Through 模式同步更新数据库和缓存,而Write Behind Caching只更新缓存,缓存会异步批量更新数据库(类似Linux的Writeback机制)

但是该机制无法保证数据强一致性,且可能会丢失数据(没有完美的机制)

四、缓存雪崩

缓存雪崩是指缓存因为某些原因(如大量key集中过期、服务器宕机等)导致大量查询请求到达服务端数据库,造成数据库压力骤增、甚至宕机、进而引起整个系统崩溃的现象

1. 缓存常驻策略

为缓存设置较长的有效期,保证缓存在业务高峰期一定不会过期

2. 多级缓存策略

本地缓存作为一级缓存,分布式缓存作为二级缓存。多级缓存相结合,可以有效分担查询请求压力,防止雪崩

3. 过期时间优化策略

  • 为缓存有效期增加随机值,防止集中失效
  • 将热点Key有效期设置为永久有效(内存到达限定值时依旧会淘汰)
  • 逻辑过期与异步更新:为每个Key维护一个逻辑过期时间,当逻辑过期时间小于当前时间时,说明当前缓存逻辑已经失效,需要进行更新。如Redis中设置Key的过期时间为60min,Value中设置逻辑过期时间为30min。若命中缓存时发现已到达30min逻辑过期时间,则在返回数据的同时,异步更新这个Key的缓存

4. 加锁重建策略

如果以上策略均失效,在高并发场景下大量Key依旧集中过期或热点Key过期,则需要控制通过查询数据库重建缓存的请求量,常用策略是加锁和限流

若缓存为命中,则对Key加锁,只有获得锁的线程才能访问数据库并加载数据重建缓存,最后释放锁。未获得锁的线程直接返回空结果或休眠一段时间后重试

缺点是不能保证系统的吞吐量,有损用户体验

五、缓存穿透

缓存穿透是指查询一条缓存和数据库都不存在的数据,会导致查询这条数据的请求会穿透缓存,直接查询数据库,最后返回空。如果用户发起大量请求去查询这条根本不存在的数据,则会对数据库造成极大压力

1. 缓存空值策略

未不存在的Key缓存一个空值,对于之后的请求会命中缓存中的空值,直接返回空数据,减少对数据库的访问。

缺点:

  • 浪费空间:需要缓存空值
  • 防御能力若:若攻击者构建不重复的请求,该策略就会被攻破,失去防御能力

2. 布隆过滤器

布隆过滤器可以在不查询数据库的情况下准确地判断数据是否一定不存在,从而过滤请求

布隆过滤器由一个固定大小的二进制向量(或位图)和一系列映射函数组成。元素被加入到布隆过滤器中时,该元素经k个哈希函数生成k个哈希值,然后将位图对应的k个点置为1。
如果需要判断某个元素是否在布隆过滤器中,只需对给定元素进行相同的哈希计算得到k个哈希值,如果有任何一个点为0,则说明这个元素一定不在布隆过滤器中

提前将真实存在的数据的Key(如商品Id)添加到布隆过滤器中,用户查询时,若不存在,则直接返回;若存在,继续查询缓存,缓存未命中则进一步查询数据库

实现方案:

  • Guava:适用于单机环境
  • Redis:Redis中的Bitmaps数据结构可以实现布隆过滤器
  • RedisBloom:开源的Redis插件,可提供布隆过滤器、布谷鸟过滤器、TopK等功能

缺陷:

  • 由于哈希碰撞存在误判,无法判断是否一定存在
  • 不支持删除元素,因为布隆过滤器每一个Bit位不是独占的,若直接删除可能会影响其他元素

3. 布谷鸟过滤器

布谷鸟过滤器支持删除元素,但是存在误删的概率,且插入操作的复杂度高,且会随着插入元素的增加而增加

六、缓存热点

当大量用户集中访问热点数据时,压力将汇聚于单一缓存服务器实例,可能导致热点数据所在的服务器过载,缓存服务不可用

1. 前置缓存策略

将热点数据提前缓存在应用服务器的本地内存中,对于数据查询请求,首先查询本地缓存,命中则直接返回

缺陷:

  • 无法防御未知热点,对于难以预见的突发热点无效
  • 数据更新存在延迟。本地缓存可能会存在短时数据不一致

2. 热点散列策略

多副本策略,将热点数据复制多份,分别缓存在多台缓存服务器上,减轻缓存热点导致的单台缓存服务器压力

实践中,散列数量一般与分布式缓存集群的服务器数量相等。

实现方案,如Redis Cluster

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/89591.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jenkins学习笔记4

配置构建流程: Jenkins任务创建: 1)创建新任务: 把这个Accept first connection改成 No Validation。问题得到解决。 说明下,要确认下主分支的名称是master还是main。 构建触发器这块暂时没有需要配置的。 传输文件…

【yolov1】详解yolov1理论 代码

目标检测要解决的3大问题: 1、有没有? 图片中是否有要检测的物体?(检测物体,判定前景背景) 2、是什么? 这些物体分别是什么?(检测到的物体是什么) 3、在…

vuereact质检工具(eslint)安装使用总结

1、ESLint ESLint工具主要类似java中的checkStyle和findbugs,是检查代码样式和逻辑规范的工具。 1.1、ESLint安装流程 打开VSCode软件,打开扩展中心,下载ESLint插件 图1.1 点击后面的install按进行安装,如图1.2所示&#xff1…

spring boot flowable多人前加签

1、前加签插件 package com.xxx.flowable.cmd;import com.xxx.auth.security.user.SecurityUser; import com.xxx.commons.ApplicationContextHolder; import com.google.common.collect.Lists; import org.apache.commons.collections.CollectionUtils; import org.apache.co…

单元测试

一、什么是单元测试 二、Junit单元测试框架 三、如何使用Junit单元测试框架 package study.StringUtils;public class StringUtil {public static void printNumber(String name){if (namenull){System.out.println ("名字的长度是:0");return;}System.ou…

性能测试监控-java分析工具Arthas

Arthas监控docker中的jvm_ray_my的博客-CSDN博客 Quick Start | arthas arthas是什么 Arthas是阿里巴巴开源的Java诊断工具,它可以帮助开发人员快速、方便地定位和解决Java应用的问题。 Arthas主要包括以下功能: 诊断Java应用中的性能问题&#xff…

【面试题精讲】Java移位运算符

“ 有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准https://blog.zysicyj.top ” 首发博客地址[1] 面试题手册[2] 系列文章地址[3] 1. 什么是移位运算符? 在 Java 中,移位运算符用于对二进制数进行位移操作。它们…

天眼查询企业信息API接口

"天眼"一般是指"天眼查",这是一个提供全国企业信息查询的API接口。天眼查以"天眼"作为用户logo,基于人工智能算法的数据采集和分析技术,为企业和个人提供全量、精准、实时、权威的企业信息查询服务。 天眼查A…

国产5G手机20天销量不及苹果一天,被iPhone15按在地上摩擦

iPhone15的上市销售,最大竞争对手无疑是国产5G手机,如今iPhone15上市首日的销量数据也已经出炉,高达200万台,这比国产5G手机卖20天的销量还要多,iPhone15让国产手机领教了什么叫遥遥领先。 据了解苹果对iPhone15的预期…

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 试题 A: 子 2023

[蓝桥杯 2023 国 B] 子 2023 试题 A: 子 2023 【问题描述】 小蓝在黑板上连续写下从 1 1 1 到 2023 2023 2023 之间所有的整数,得到了一个数字序列: S 12345678910111213 ⋯ 20222023 S 12345678910111213\cdots 20222023 S12345678910111213⋯2…

[React] React高阶组件(HOC)

文章目录 1.Hoc介绍2.几种包装强化组件的方式2.1 mixin模式2.2 extends继承模式2.3 HOC模式2.4 自定义hooks模式 3.高阶组件产生初衷4.高阶组件使用和编写结构4.1 装饰器模式和函数包裹模式4.2 嵌套HOC 5.两种不同的高阶组件5.1 正向的属性代理5.2 反向的继承 6.如何编写高阶组…

css知识学习系列(4)-每天10个知识点

目录 1. **CSS中的“box-sizing”属性与“border”属性有什么关系?**2. **在CSS中,如何使用“calc()”函数进行计算?有什么使用技巧?**3. **在CSS中,如何使用“import”引入外部样式表?有哪些注意事项&…

第5章 网络编程

5.1 OSI模型有哪几层?以及各自的作用 难度:★ 重点:★ 白话解析 无它,唯记尔;实在记不住也需要有个印象。 参考答案 OSI(Open System Interconnection):开放式系统互联参考模型。它分为7层,自顶而下分别是:应用层、表示层、会话层、传输层、网络层、数据链路层和物…

【数据结构与算法】 - 时间复杂度和空间复杂度、二分查找、线性查找

数据结构与算法 1. 数据结构的定义2. 二分查找2.1 二分查找的定义2.2 二分查找分析2.3 二分查找实现2.4 二分查找算法图解2.5 二分算法引发的问题2.6 二分算法改良版2.7 二分算法改良版解析2.8 二分算法改良版图解2.9 二分算法改良版注意事项 3. 时间复杂度3.1 时间复杂度的概念…

【c语言中数组和指针的联系】

C语言中的数组和指针之间存在紧密的联系,因为数组名本质上就是指向数组首元素的指针。这种联系使得在处理数组时可以像处理指针一样操作,同时也使得数组和指针可以互相转换。 下面详细说明数组和指针之间的联系,并提供一些示例来演示这种联系…

LeetCode算法二叉树—226. 翻转二叉树

目录 226. 翻转二叉树 代码: 运行结果: 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 示例 1: 输入:root [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]示例 2: 输入…

OpenCV读取图像时按照BGR的顺序HWC排列,PyTorch按照RGB的顺序CHW排列

OpenCV读取RGB图像 在OpenCV中,读取的图片默认是HWC格式,即按照高度、宽度和通道数的顺序排列图像尺寸的格式。我们看最后一个维度是C,因此最小颗粒度是C。 例如,一张形状为2562563的RGB图像,在OpenCV中读取后的格式…

Educational Codeforces Round 155 (Rated for Div. 2)

Dashboard - Educational Codeforces Round 155 (Rated for Div. 2) - Codeforces A. Rigged! 只要后面有选手大于或等于第一个选手的力量和耐力就会获胜或者平局&#xff0c;故这种情况判为-1&#xff0c;其余情况以第一位选手的力量为重量可以取到最优解 #include<bits…

IDEA 启动 java web 老项目

背景&#xff1a;一套 java web 老代码&#xff0c;使用 eclipse 工具开发。内网&#xff0c;无 eclipse 开发工具&#xff0c;只有 IDEA。 代码目录结构如下&#xff1a; demo/.settings/* demo/src/com/demo/controller/* demo/webapp/js/* demo/webapp/jsp/* demo/webapp/M…

MySQL学习笔记18

MySQL的备份与恢复&#xff1a; 制定数据库备份策略进行备份&#xff0c;并且把数据导入到测试环境。 核心技术&#xff1a; 1&#xff09;掌握MySQL的备份工具使用及各自特点&#xff1b; 2&#xff09;熟悉Shell脚本&#xff1b; 3&#xff09;熟悉MySQL数据的导入导出&a…