多级缓存设计实践

缓存是什么?

缓存技术是一种用于加速数据访问的优化策略。它通过将频繁访问的数据存储在高速存储介质(如内存)中,减少对慢速存储设备(如硬盘或远程服务器)的访问次数,从而提升系统的响应速度和性能。

缓存的基本原理是:当某个数据被请求时,系统首先检查缓存中是否已存储该数据。如果缓存中存在,则直接返回缓存中的数据,称为“缓存命中”;如果缓存中没有该数据,则从源数据存储(如数据库或远程服务器)中获取数据,并将其存入缓存,以便下次请求时使用。

缓存的常见类型:

  1. 内存缓存:将数据存储在内存中,常见的缓存系统有 Redis 和 Memcached。内存的访问速度远远高于硬盘,因此可以大幅提升性能。

  2. 磁盘缓存:将数据存储在硬盘或 SSD 中,相比内存访问较慢,但可以存储更大量的数据。适用于需要较大存储空间但对速度要求不如内存严格的场景。

  3. 浏览器缓存:网页浏览器会缓存静态资源(如图片、JavaScript 文件等)以减少重复请求,从而提高用户体验和加载速度。

  4. CDN缓存:内容分发网络(CDN)将静态内容缓存到靠近用户的边缘节点,以降低延迟,提高访问速度。

  5. 服务缓存:如Go的本地缓存
  6. MySQL缓存:change buffer 机制,查询缓存机制(已废弃)

性能比较:内存类大于磁盘类,客户端类大于服务端类,近的节点大于远的节点(存储介质&距离)

常见的缓存策略:

  1. LRU(Least Recently Used):当缓存空间不足时,优先淘汰最近最少使用的数据。(冷热数据)

  2. FIFO(First In, First Out):先进先出,最早加入缓存的数据会被优先移除。(新旧数据)

  3. TTL(Time To Live):每个缓存数据都有一个有效期,到期后会被自动清除或重新加载。(定时器机制)

  4. Write-through:每次对缓存进行修改时,同时将数据写入原始数据存储。(双写,保证数据一致性)

  5. Lazy Loading(懒加载):仅当需要时才加载数据到缓存中。(节省资源)

  6. Cache Invalidation(缓存失效):当数据源发生变化时,缓存中的数据需要被标记为无效或更新,以保持数据的一致性。(驱动更新)

示例:

Redis 提供了多种淘汰策略来处理内存达到上限时的情况。以下是 Redis 常见的淘汰策略:

  1. noeviction
    当内存不足时,Redis 不会淘汰任何数据,并且会返回错误。适用于不允许丢失数据的场景。

  2. allkeys-lru
    基于最少最近使用(LRU,Least Recently Used)算法来淘汰最不常用的键。当 Redis 达到内存限制时,会从所有的键中淘汰最少使用的键。

  3. volatile-lru
    只对设置了过期时间的键进行 LRU 淘汰。如果内存不足,Redis 会选择最久未使用的、设置了过期时间的键来删除。

  4. allkeys-random
    随机删除键,适用于在内存不足时,删除任意一个键。

  5. volatile-random
    随机删除设置了过期时间的键。它只删除有过期时间的键,不会影响没有过期时间的键。

  6. volatile-ttl
    根据过期时间(TTL,Time To Live)来淘汰键,选择即将过期的键进行删除。这种策略适用于缓存场景,删除那些快过期的键。

常见的缓存问题:

1. 缓存穿透

缓存穿透是指客户端请求的数据既不在缓存中,也不在数据库中,导致每次请求都直接访问数据库,绕过了缓存。

解决方案

  • 空缓存策略:对于不存在的数据,可以将其在缓存中设置一个空值(如 null 或特殊标记),并设置一个较短的过期时间。这样下次请求相同数据时,可以直接返回空缓存,避免每次都去数据库查询。
  • 缓存过滤:在请求数据前,进行数据验证和过滤(如验证用户输入),确保请求的数据是有效的,减少无效请求。

2. 缓存雪崩

缓存雪崩指的是缓存中的大量数据在同一时间过期,导致大量请求同时访问数据库,造成数据库压力剧增,甚至崩溃。

解决方案

  • 随机过期时间:设置缓存的过期时间时,加上随机偏差,避免多个缓存项在同一时刻过期。
  • 分布式缓存:将缓存分布到多个服务器上,避免单个缓存服务器故障导致整个缓存系统崩溃。
  • 提前加载缓存:根据访问量和数据的热点情况,提前将关键数据加载到缓存中,避免全量加载导致的瞬时压力。

3. 缓存击穿

缓存击穿是指缓存中某个热点数据过期或被删除后,有大量请求同时访问该数据,导致直接访问数据库,从而引发数据库压力过大。

解决方案

  • 加锁或队列机制:在缓存失效时,对该数据加锁或使用队列来保证同一时间只有一个请求去数据库查询并更新缓存,其他请求等待查询结果。
  • 缓存预热:可以在缓存失效前,提前加载或更新缓存,避免缓存失效后直接访问数据库。

4. 缓存一致性问题

缓存与数据库之间的数据可能会出现不一致的情况。例如,数据在数据库中更新了,但缓存没有及时更新,导致用户看到的数据是过期的。

解决方案

  • 缓存失效策略:在数据更新后,立即删除缓存中相应的条目。可以通过应用程序逻辑主动更新缓存,或者设置合适的过期时间。
  • 双写一致性:在对数据库进行修改时,同时修改缓存中的数据,确保缓存和数据库的数据一致性。
  • 延迟一致性:如果严格一致性要求不高,可以允许一定时间内缓存与数据库不一致,稍后通过后台任务同步缓存。

5. 缓存污染

缓存污染是指缓存中存入了错误、无效或不再使用的数据,这些数据会导致缓存效率下降,甚至对应用性能造成影响。

解决方案

  • 数据校验:在将数据写入缓存之前,进行有效性检查,确保缓存的数据是正确的。
  • 定期清理:定期扫描和清理缓存中的无效或过期数据,保持缓存的健康状态。
  • 合理设置缓存过期时间:缓存过期时间应根据数据的变化频率来合理设置,避免缓存存储过多过期或不常用的数据。

6. 缓存并发问题

当多个请求同时访问缓存中的同一数据时,可能会导致缓存更新的冲突,尤其是在高并发场景下,可能会造成缓存不一致或资源竞争。

解决方案

  • 使用锁机制:可以为缓存数据增加锁(如互斥锁),保证同一时间只有一个请求可以更新缓存,避免并发更新冲突。
  • 异步更新:通过异步方式更新缓存,避免长时间的同步阻塞。

7. 缓存击穿和过期

数据缓存的生命周期管理是一个复杂的问题,如果没有合理的策略,缓存中的数据可能在一段时间后过期,导致大量请求直接访问数据库。

解决方案

  • 永不过期的缓存:对于一些不容易发生变化的数据,可以设置为永不过期,避免频繁更新缓存。
  • 使用合适的缓存策略:比如定期刷新缓存、主动更新缓存等,确保缓存始终保持有效。

8. 内存溢出

当缓存数据过多,超出了缓存系统的内存限制时,可能会导致缓存系统发生内存溢出,影响系统的稳定性。

解决方案

  • 限制缓存大小:根据应用的实际需求,设置合适的缓存大小限制,避免缓存过多的数据。
  • LRU(最少最近使用)淘汰策略:对于不常使用的数据,可以采用 LRU 或其他淘汰策略来清理缓存,避免内存溢出。

9. 缓存穿透/渗透与数据泄露

在一些恶意攻击或错误请求的情况下,缓存可能会泄露敏感数据,特别是在没有加密或限制访问控制的情况下。

解决方案

  • 数据加密:对于缓存中的敏感数据进行加密存储,防止数据泄露。
  • 访问控制:根据不同用户权限设置缓存访问策略,防止不合法请求访问敏感数据。

10. 缓存更新延迟

在某些情况下,缓存更新的延迟可能会导致系统反应迟缓,特别是在高并发环境中。

解决方案

  • 异步更新缓存:对于非实时性要求非常高的场景,可以通过异步任务更新缓存,避免阻塞主流程。
  • 批量更新缓存:在数据有大批量变化时,可以采用批量更新策略,而不是频繁更新单条缓存数据。

缓存的优点:

  • 提高性能:通过减少对慢速存储的访问,缓存可以显著加快数据的读取速度。
  • 减轻服务器负担:减少对数据库或其他后端系统的请求,降低它们的负载。
  • 节省带宽:通过缓存远程内容(如网站资源或API响应),减少带宽消耗。

缓存的挑战:

  • 一致性问题:缓存中的数据可能与原始数据不同步,导致数据不一致。通常需要通过策略(如缓存失效、刷新机制)来解决这一问题。
  • 内存占用:缓存使用内存或磁盘存储,会占用一定的资源,需要合理规划和管理。
  • 缓存穿透:恶意用户可能直接绕过缓存,频繁访问源数据,从而导致源系统的压力增加。

为什么要设计多级缓存?什么是CPU的多级缓存?

CPU 多级缓存(Multi-level Cache,简称 L1/L2/L3 Cache)是为了提高 CPU 访问内存的速度而设计的一种缓存层次结构。由于直接从主内存读取数据的速度相对较慢,因此,现代 CPU 采用了多级缓存来加速数据访问,减少访问内存的延迟。CPU 多级缓存通常包括以下几层:

1. L1 缓存(一级缓存)

  • 位置:L1 缓存在 CPU 核心内,距离处理器最接近。
  • 速度:L1 缓存是所有缓存层中速度最快的,通常在几纳秒(ns)级别。
  • 容量:L1 缓存的容量较小,一般在 16KB 到 128KB 之间。
  • 用途:L1 缓存通常分为两个部分:
    • L1 数据缓存:存储数据。
    • L1 指令缓存:存储即将执行的指令。
  • 特点:L1 缓存是最小的,但也最快,主要存储当前正在处理的数据和指令。由于其速度快,L1 缓存的命中率对整体性能影响较大。

2. L2 缓存(二级缓存)

  • 位置:L2 缓存位于 CPU 核心内或与多个核心共享(这取决于 CPU 架构)。
  • 速度:L2 缓存的速度比 L1 缓存稍慢,但仍比主内存快,通常在 10 纳秒左右。
  • 容量:L2 缓存的容量比 L1 大,通常在 128KB 到几 MB 之间。
  • 用途:L2 缓存用于存储从 L1 缓存中淘汰的数据,或者那些 L1 缓存没有命中的数据。它在 L1 缓存和 L3 缓存之间起到了桥梁作用,缓解了 CPU 核心访问主内存时的瓶颈。
  • 特点:L2 缓存要比 L1 大,速度稍慢,但通常每个核心都拥有独立的 L2 缓存。

3. L3 缓存(三级缓存)

  • 位置:L3 缓存通常是多个 CPU 核心共享的缓存层级,它位于 L2 缓存之上,甚至可能在多核处理器中共享给所有核心。
  • 速度:L3 缓存的速度比 L1 和 L2 缓存慢,但仍比访问主内存快,通常在几十纳秒(ns)级别。
  • 容量:L3 缓存的容量最大,一般从几 MB 到几十 MB 不等,具体取决于处理器架构。
  • 用途:L3 缓存用于存储那些在 L1 和 L2 缓存中都未命中的数据,它起到了对多个核心的共享存储作用,帮助缓解访问主内存的压力。
  • 特点:L3 缓存是多个核心共享的,容量大但速度较慢。其设计目的是减少多个 CPU 核心之间的访问冲突和数据不一致。

4. 主内存(RAM)

当缓存(L1、L2、L3)都没有命中时,CPU 会访问主内存(RAM)。虽然主内存的存取速度比缓存慢得多(通常在几十到几百纳秒之间),但它的容量较大,能够存储更多的数据。

5. 缓存命中率和访问延迟

  • 缓存命中率:缓存命中率指的是 CPU 请求的数据在缓存中找到的概率。缓存命中率越高,CPU 能更快地从缓存中获取数据,性能表现越好。
  • 访问延迟:访问延迟是指从 CPU 发出请求到数据返回的时间。L1 缓存的访问延迟最短,而主内存的访问延迟最长。

多级缓存的设计目的

  1. 提高性能:通过层级化缓存,尽量减少 CPU 访问较慢的主内存的次数,从而提高整体计算性能。
  2. 平衡速度和容量:L1 缓存速度最快,但容量小;L3 缓存容量大,但速度较慢。通过层级缓存的设计,可以在速度和容量之间找到平衡。
  3. 减少内存带宽压力:多个级别的缓存减少了 CPU 对主内存的访问,降低了内存带宽的压力,提高了 CPU 处理能力。

多级缓存的业务实现

L1 :客户端缓存(SDK实现-Redis/服务缓存)

L2 :  服务端缓存(本地缓存)

L3:  第三方缓存(Redis)

1、在管理功能进行埋点,持久化DB数据到Redis,保证数据一致性

  • 数据双写 ,先写Redis,再写数据库,数据库失败,则回滚缓存数据(单条记录要加锁)
  • 数据检测脚本,监控redis 数据和数据库的一致性,晚上跑,自动更新修复数据

2、服务缓存- Go使用本地缓存,作为临时缓存挡住一部分请求

  • 缓存时间设置为 1 min ,配置到 Apollo,主要挡住突发流量以及热点数据流量
  • 降级处理,本地缓存失效,走Redis获取,再存储到本地缓存

3、SDK 获取时,在当前服务写缓存,缓存时间5秒

好处:

1、多级缓存机制,保证服务高可用

坏处:

1、增加维护成本

2、数据实时性变差(要看业务能否接受)

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

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

相关文章

Linux网络编程之---多线程实现并发服务器

下面我们来使用tcp集合多线程实现并发服务器 一.服务端 #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h>typedef struct sockinfo {char ip[16];unsigne…

Web API基本认知

作用和分类 作用&#xff1a;就是使用JS去操作html和浏览器 分类&#xff1a;DOM&#xff08;文档对象模型&#xff09;、BOM&#xff08;浏览器对象模型&#xff09; 什么是DOM DOM&#xff08;Document Object Model ——文档对象模型&#xff09;是用来呈现以及与任意 HTM…

《Python基础》之Numpy库

目录 简介 一、创建数组 1、根据列表创建数组 2、创建全0数组 3、创建全1数组 4、创建单位矩阵 5、创建随机数数组 二、查看数组的属性 三、 数组的操作 1、索引和切片 2、变形 3、拼接 &#xff08;1&#xff09;、vstack() 纵向拼接 &#xff08;2&#xff09;、hs…

人工智能-卷积神经网络(学习向)

一.概述&#xff1b; 卷积神经网络&#xff08;Convolutional Neural Network, CNN&#xff09;是一种专门用于处理具有类似网格结构的数据&#xff08;如图像&#xff09;的深度学习模型。 主要用于处理机器视觉任务。 主要功能&#xff1b; 1.图像分类 2.目标检测 3.图像分割…

思维导图+实现一个登录窗口界面

QQ2024122-205851 import sys from PyQt6.QtGui import QIcon, QPixmap, QMovie from PyQt6.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QLabel, QVBoxLayout# 封装我的窗口类 class LoginWidget(QWidget):# 构造函数def __init__(self):# 初始化父类su…

使用 Pytorch 构建 Vanilla GAN

文章目录 一、说明二、什么是 GAN&#xff1f;三、使用 PyTorch 的简单 GAN&#xff08;完整解释的代码示例&#xff09;3.1 配置变量3.2 、PyTorch 加速3.3 构建生成器3.4 构建鉴别器 四、准备数据集五、初始化函数六、前向和后向传递七、执行训练步骤八、结果 一、说明 使用…

Windows常用DOS指令(附案例)

文章目录 1.dir 查看当前目录2.cd 进入指定目录3.md 创建指定目录4.cd> 创建指定文件5.rd 删除指定空目录6.del 删除指定文件7.copy 复制文件8.xcopy 批量复制9.ren 改名10.type 在命令行空窗口打开文件11.cls 清空DOS命令窗口12.chkdsk 检查磁盘使用情况13.time 显示和设置…

【Maven】Nexus私服

6. Maven的私服 6.1 什么是私服 Maven 私服是一种特殊的远程仓库&#xff0c;它是架设在局域网内的仓库服务&#xff0c;用来代理位于外部的远程仓库&#xff08;中央仓库、其他远程公共仓库&#xff09;。一些无法从外部仓库下载到的构件&#xff0c;如项目组其他人员开发的…

【CSS】小球旋转loading加载动画

效果 css小球旋转loading动画 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document<…

Web day07 项目实战

目录 Restful风格&#xff1a; 代码结构&#xff1a; 1). Controller层 2). Service层 3). Mapper&#xff08;dao&#xff09;层 4).yml文件&#xff1a; 数据封装&#xff1a; 1). 手动结果映射 2). 起别名 3). 开启驼峰命名(推荐) 删除部门&#xff1a; 新增部门&a…

rest-assured multiPart上传中文名称文件,文件名乱码

rest-assured是一个基于java语言的REST API测试框架&#xff0c;在使用rest-assured的multipart 上传文件后&#xff0c;后端获取的文件名称乱码。截图如下&#xff1a; 原因是rest-assured multipart/form-data默认的编码格式是US-ASCII&#xff0c;需要设置为UTF-8。 Befo…

【Git操作】-- 将已存在的项目复制一份到另一个分组空间下

目录 1、需求描述 2、操作步骤 2.1 配置 2.2、git 上创建新项目 2.3 添加到旧的项目中 2.3、将新项目添加到待复制的项目上 3、Push an existing Git repository 4、浏览器打开新项目 nn_bigdata 5、其他&#xff1a;如果项目已经拉取到本地&#xff0c;那么可以使用以…

搭建环境-PHP简介及环境搭建教程

搭建环境-PHP简介及环境搭建教程 前言 在现代Web开发中,PHP是一种广泛使用的服务器端脚本语言,它以简洁、高效和跨平台的特性受到开发者的青睐。无论是小型网站还是大型企业应用,PHP都能提供强大的支持。本文将为您详细介绍PHP的基本概念、特点,以及如何搭建PHP开发环境。…

Python中通过点运算符来访问命名空间中参数args方法

Python中通过点运算符来访问命名空间中参数args方法 在Python中&#xff0c;在使用args进行参数传入时&#xff0c;通常是调用argparse模块的ArgumentParser来创建对象。这种设计虽然使得访问命令行参数更加方便&#xff0c;可以通过点运算符来访问命名空间中的参数。但是当封装…

Unity类银河战士恶魔城学习总结(P156 Audio Settings音频设置)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了音频的大小设置与保存加载 音频管理器 UI_VolumeSlider.cs 定义了 UI_VolumeSlider 类&#xff0c;用于处理与音频设置相关的…

基于单片机的WIFI、语音、储存、时钟、闹钟、定位系统

所有仿真详情导航&#xff1a; PROTEUS专栏说明-CSDN博客 目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采用DS1302时钟模块&#xff0c;通过LCD1602显示实时时间&#xff0c;也可以储存时间在AT2DC02中&#xff0c…

贪心算法专题(四)

目录 1. 单调递增的数字 1.1 算法原理 1.2 算法代码 2. 坏了的计算器 2.1 算法原理 2.2 算法代码 3. 合并区间 3.1 算法原理 3.2 算法代码 4. 无重叠区间 4.1 算法原理 4.2 算法代码 5. 用最少数量的箭引爆气球 5.1 算法原理 ​5.2 算法代码 1. 单调递增的数字…

Creating Server TCP listening socket *:6379: bind: No error

启动redis报错&#xff1a;Creating Server TCP listening socket *:6379: bind: No error 解决方案&#xff1a; 1、直接在命令行中输入 redis-cli.exe 2、输入shutdown&#xff0c;关闭 3、输exit&#xff0c;退出 4、重新输入 redis-server.exe redis.windows.conf&…

【HM-React】02. React基础-下

React表单控制 受控绑定 概念&#xff1a;使用React组件的状态&#xff08;useState&#xff09;控制表单的状态 function App(){const [value, setValue] useState()return (<input type"text" value{value} onChange{e > setValue(e.target.value)}/>) …

基于python的汽车数据爬取数据分析与可视化

一、研究背景 基于提供的代码片段和讨论&#xff0c;我们可以得出一个与网络抓取、数据处理和数据可视化相关的研究背景&#xff0c;该背景涉及到汽车行业。以下是研究背景的陈述&#xff1a; "在迅速发展的汽车行业中&#xff0c;准确和及时的数据对各方利益相关者至关…