缓存系统-基本概述

目录

一、系统概述

二、名词解释

三、淘汰策略

1、LRU

2、LFU

3、FIFO

4、TTL

5、Random

四、读写模式

1、Cache Aside(旁路缓存)

2、Write Through(直写)

3、Write Back(回写)

五、问题方案

1、缓存穿透

2、缓存雪崩

3、缓存击穿

4、最佳实践


一、系统概述

缓存(Cache)是通过临时存储高频访问数据来加速数据访问的技术组件,本质是"空间换时间"的典型实践。其核心价值体现在:

  • 性能加速:缩短数据访问路径(CPU缓存 vs 内存访问速度差达100倍)
  • 资源节约:减少对底层数据源的访问压力(数据库查询成本降低80%+)
  • 系统稳定:应对突发流量冲击(如秒杀场景QPS可达10万级)

二、名词解释

  1. 缓存命中率:请求缓存时,数据存在的概率(命中次数 / 总请求次数),命中率>90%为高效,<70%需优化(如调整淘汰策略或容量);
  2. 缓存穿透:请求不存在的数据(如恶意攻击或无效ID),绕过缓存直击后端;
  3. 缓存击穿:热点数据过期瞬间,高并发请求压垮数据库;
  4. 缓存雪崩:大量缓存同时过期,导致请求洪峰冲击后端;
  5. 缓存污染:缓存中存储了大量非高频访问或无效数据,导致缓存命中率下降,进而降低系统整体性能的现象。本质是缓存资源被低价值数据占用,无法有效服务高频请求。
  6. TTL(Time To Live):缓存数据存活时间;
  7. 冷热数据分离:高频/低频数据分区存储
  8. 缓存预热:系统启动时加载热点数据

三、淘汰策略

缓存系统通过淘汰策略在容量不足时决定移除哪些数据,核心目标是最大化缓存命中率。

策略

时间复杂度

空间开销

适用场景

命中率

LRU

O(1)

通用场景(Web缓存)

★★★★☆

LFU

O(1)~O(log n)

热点数据集中(视频推荐)

★★★★★

FIFO

O(1)

简单顺序访问(日志缓冲)

★★☆☆☆

TTL

O(log n)

时效性数据(会话/验证码)

★★★☆☆

Random

O(1)

低成本容忍场景

★★☆☆☆

1、LRU

核心思想:优先淘汰最久未被访问的数据
数据结构:哈希表 + 双向链表(哈希表存储键值对,链表维护访问顺序)
操作流程

  • 数据访问
    • 命中缓存:将节点移到链表头部
    • 未命中:从数据库加载,新节点插入链表头部
  • 淘汰触发
    • 链表尾部节点(最久未访问)被移除
    • 同步删除哈希表中对应键

优点

  • 高效反映时间局部性(最近访问的数据更可能再被访问)
  • 时间复杂度:O(1)(哈希表定位 + 链表移动)

缺点

  • 突发批量访问可能污染缓存(如全表扫描)
  • 链表维护增加内存开销

2、LFU

核心思想:优先淘汰访问频率最低的数据
数据结构:双哈希表(键值存储 + 频率-键列表) + 最小堆/双向链表
操作流程

  • 数据访问
    • 命中缓存:增加计数,调整在频率链表中的位置
  • 淘汰触发
    • 移除最低频率链表中的最早节点(LRU作为次级策略)
    • 更新最小频率值

优点

  • 精准保护高频访问数据
  • 适合长期热点数据场景(如电商热门商品)

缺点

  • 新数据易被淘汰(初始频率低)
  • 维护成本高(需频率排序)
  • 历史高频但不再访问的数据可能滞留

3、FIFO

核心思想:按进入缓存的顺序淘汰
数据结构:队列(数组/链表实现)
操作流程

(1)数据写入:新数据加入队尾

(2)淘汰触发:移除队首数据

优点

  • 实现简单(仅需队列)
  • 零额外内存开销

缺点

  • 忽略访问模式(可能淘汰热点数据)
  • 缓存命中率通常最低

4、TTL

核心思想:基于过期时间自动淘汰
数据结构:哈希表 + 时间堆(或轮询检查)
操作流程

  • 数据写入:设置过期时间戳(当前时间 + TTL)
  • 淘汰触发
    • 主动:定期扫描过期数据(定时器)
    • 被动:访问时检查过期并删除

优点

  • 保证数据时效性(适合会话缓存)
  • 避免手动清理

缺点

  • 可能提前移除仍有价值的数据
  • 扫描机制消耗CPU(大缓存需优化)

5、Random

核心思想:随机选择数据淘汰
数据结构:动态数组(如Python list)
操作流程

  • 淘汰触发:随机选择键删除
  • 数据维护:数组动态调整

优点

  • 实现极其简单
  • 无状态维护成本

缺点

  • 可能误删高频数据
  • 性能波动不可预测

四、读写模式

主要是要保证数据的一致性。

1、Cache Aside(旁路缓存)

旁路缓存模式,也称为懒加载(Lazy Loading),是最常见的缓存模式。

应用程序直接与缓存和数据库(或主数据存储)交互。

优点:实现简单,缓存只保存实际被请求的数据,节省内存。
缺点:缓存未命中时,需要访问数据库,可能导致延迟。另外,在写操作后立即读,可能会因为缓存失效而读到旧数据(需要等到下次加载),但通常通过删除缓存保证一致性。

读流程

  • 缓存命中:应用程序首先检查缓存。如果数据存在(命中),则直接返回缓存数据。
  • 缓存未命中

        (1)应用程序从数据库中读取数据。

        (2)将读取到的数据写入缓存(以便后续读取命中)。

        (3)返回数据。

写流程

  • 应用程序直接更新数据库。
  • 同时,使缓存中对应的数据失效(删除缓存项)。这样,下次读取时,会触发缓存未命中,从而从数据库加载最新数据并重新填充缓存。


 

2、Write Through(直写)

在直写模式中,缓存作为数据库的代理层。

写操作总是先经过缓存,然后由缓存同步更新到数据库。

优点:缓存和数据库始终保持一致(强一致性)。读操作很少会访问数据库,因为写操作已经更新了缓存。
缺点:写操作延迟较高,因为需要等待数据库写入完成。如果数据不经常被读取,那么写入缓存可能造成资源浪费(因为每次写都更新缓存,即使很少读)。

读流程

  • 缓存命中:直接返回缓存数据。
  • 缓存未命中

        (1)缓存从数据库中加载数据(或由应用程序触发加载)。

        (2)将数据放入缓存。

        (3)返回数据。

写流程

(1)应用程序更新缓存(如果数据在缓存中不存在,则创建缓存项)。

(2)缓存立即将数据同步写入数据库(通常在一个事务内)。

(3)只有在数据库写入成功后,写操作才算完成。


 

3、Write Back(回写)

回写模式(也称为Write-Behind)中,写操作首先写入缓存,然后异步批量写入数据库。缓存作为写操作的缓冲区。

优点:写操作非常快,因为应用程序不需要等待数据库写入。可以合并多次写操作,减少数据库压力。
缺点:数据不一致的风险(缓存和数据库在异步同步前不一致)。如果缓存崩溃,尚未写入数据库的数据会丢失。因此,通常需要额外的机制(如写日志)来保证数据持久性。

读流程

  • 缓存命中:直接返回缓存数据。
  • 缓存未命中

        (1)从数据库中加载数据到缓存。

        (2)返回数据。

写流程

(1)应用程序更新缓存(如果数据不在缓存中,则先加载到缓存再更新,或者直接创建新的缓存项)。

(2)缓存标记数据为“脏”(dirty),表示需要同步到数据库。

(3)缓存立即返回成功给应用程序(无需等待数据库写入)。

(4)缓存会在之后的某个时间点(例如,缓存满时、定时任务、或者低负载时)将“脏”数据批量写入数据库。

 

五、问题方案

1、缓存穿透

问题原因

        当查询不存在的数据时(如无效ID、不存在的用户名),每次请求都会穿透缓存层直接访问数据库。在恶意攻击场景下(如脚本批量请求随机ID),数据库会持续承受无效查询压力,导致性能急剧下降甚至崩溃。这种现象与正常缓存未命中的区别在于:正常未命中是偶发的,而穿透是持续性的无效查询。

// 大量恶意调用
getData("invalid_id_1");
getData("invalid_id_2");
...// 缓存访问接口
std::string getData(const std::string& key) 
{auto data = cache.get(key); // 缓存查询if (data.empty()) {data = db.query(key);   // 缓存未命中,查询数据库cache.set(key, data);   // 写入缓存}return data;
}

解决方案

(1)布隆过滤器(Bloom Filter)

  • 在缓存层前设置布隆过滤器作为屏障
  • 工作原理:使用多个哈希函数将键映射到位数组中,查询时:
    • 若键不在过滤器中 → 直接返回空(拦截非法请求)
    • 若键可能存在 → 继续查询缓存/数据库
  • 特点:存在误判率(通常<1%),但内存效率极高(1亿键仅需约100MB)

(2)空值缓存(Cache Null Object)

  • 对查询结果为空的键,缓存特殊标记(如"NULL")
  • 设置较短过期时间(5-30秒),防止恶意请求耗尽空间
  • 需配合监控清理机制,避免存储过多无效键
// C++ 布隆过滤器+空值缓存实现
class BloomFilter {
private:std::vector<bool> bits;std::vector<std::hash<std::string>> hashers;public:BloomFilter(size_t size, int hash_count) : bits(size), hashers(hash_count) {}void add(const std::string& key) {for (auto& hash_fn : hashers) {size_t pos = hash_fn(key) % bits.size();bits[pos] = true;}}bool may_contain(const std::string& key) {for (auto& hash_fn : hashers) {size_t pos = hash_fn(key) % bits.size();if (!bits[pos]) return false;}return true;}
};// 使用示例
BloomFilter filter(1000000, 3); // 100万位,3个哈希std::string get_data(const std::string& key) {// 布隆过滤器拦截if (!filter.may_contain(key)) return "";// 缓存查询auto data = cache.get(key);if (data == "NULL") return ""; // 空值标识if (data.empty()) {data = db.query(key);if (data.empty()) {cache.set(key, "NULL", 15); // 缓存空值15秒return "";}cache.set(key, data, 3600); // 缓存有效数据}return data;
}

2、缓存雪崩

问题原因

        当大量缓存在同一时间段集中过期(如缓存初始化时设置相同TTL),瞬时会有海量请求穿透到数据库。典型场景包括:

  • 系统启动时批量加载缓存
  • 定时任务刷新缓存
  • 缓存服务器重启

雪崩效应会导致数据库出现流量尖峰,引发连锁故障(如连接池耗尽、CPU过载)。

解决方案

(1)差异化过期时间

  • 基础过期时间 + 随机偏移(如30分钟 ± 5分钟)
  • 确保缓存失效时间均匀分布,避免集中失效

(2)双层缓存策略

  • 主缓存:设置较短TTL(30分钟),承担日常请求
  • 备份缓存:设置长TTL(24-48小时)
  • 当主缓存失效时:
    • 先返回备份缓存数据
    • 异步重建主缓存
  • 保证即使主缓存失效,仍有备份数据可用

(3)热数据永不过期

  • 对核心热数据(如首页推荐)采用逻辑过期:
    • 物理上永不过期
    • 后台线程定期更新数据
    • 数据对象包含逻辑过期时间戳
// C++ 双层缓存+随机TTL实现
std::string get_data_avalanche_protected(const std::string& key) {// 随机数生成器static thread_local std::mt19937 rng(std::random_device{}());static std::uniform_int_distribution<int> dist(-300, 300);// 优先查询主缓存if (auto data = cache.get("primary_" + key); !data.empty()) return data;// 查询备份缓存if (auto backup = cache.get("backup_" + key); !backup.empty()) {// 异步重建主缓存std::thread([key, backup] {int ttl = 1800 + dist(rng); // 30分钟基础+随机偏移cache.set("primary_" + key, backup, ttl);}).detach();return backup;}// 查询数据库auto data = db.query(key);if (!data.empty()) {int primary_ttl = 1800 + dist(rng);cache.set("primary_" + key, data, primary_ttl);cache.set("backup_" + key, data, 86400); // 备份24小时}return data;
}

3、缓存击穿

问题原因

当某个热点Key突然失效时,瞬时海量并发请求同时涌入数据库。与雪崩的区别在于:

  • 雪崩:大量不同Key同时失效
  • 击穿:单个热点Key失效引发风暴

核心危害

  • 单点数据库压力暴增(万级QPS)
  • 可能引发连接池耗尽
  • 重建缓存时的重复查询浪费资源

解决方案

(1)互斥锁重建(分布式锁)

  • 当缓存失效时,仅允许一个线程执行数据库查询
  • 其他线程阻塞等待或重试
  • 关键点:
    • 锁粒度:Key级别锁而非全局锁
    • 锁超时:防止死锁(通常5-10秒)
    • 双重检查:获取锁后再次验证缓存

(2)逻辑过期永不过期

  • 缓存永不物理删除
  • 数据结构包含逻辑过期时间戳
  • 请求处理流程:
    • 返回当前缓存数据(无论是否过期)
    • 异步检查过期状态
    • 过期则触发重建
  • 优点:零等待时间,保证高并发下的可用性

(3)热点数据监控+预加载

  • 实时监控Key访问频率
  • 识别热点Key后:
    • 延长其TTL
    • 提前异步刷新
    • 在多个缓存节点复制
// C++ 互斥锁+逻辑过期实现
class HotspotProtection {
private:std::shared_mutex global_mutex;std::unordered_map<std::string, std::shared_ptr<std::mutex>> key_mutexes;struct CacheItem {int64_t logical_expire; // 逻辑过期时间戳(毫秒)std::string data;};public:std::string get_data(const std::string& key) {// 获取当前缓存项auto item = cache.get<CacheItem>(key);// 未逻辑过期直接返回if (item.logical_expire > get_system_time_millis()) return item.data;// 获取Key级别锁std::shared_ptr<std::mutex> key_mutex;{std::shared_lock read_lock(global_mutex);auto it = key_mutexes.find(key);if (it != key_mutexes.end()) key_mutex = it->second;}if (!key_mutex) {std::unique_lock write_lock(global_mutex);key_mutex = key_mutexes[key] = std::make_shared<std::mutex>();}// 锁定并重建std::unique_lock lock(*key_mutex);// 双重检查item = cache.get<CacheItem>(key);if (item.logical_expire > get_system_time_millis()) return item.data;// 查询数据库auto new_data = db.query(key);int64_t new_expire = get_system_time_millis() + 3600000; // 1小时后过期// 更新缓存cache.set(key, CacheItem{new_expire, new_data});return new_data;}
};

4、最佳实践

防御策略

适用场景

优点

缺点

布隆过滤器

防恶意请求/无效键

内存高效,拦截精确

存在误判率

空值缓存

处理不存在数据

简单易实现

可能存储大量无效键

随机TTL

防批量缓存同时失效

有效分散压力

无法应对热点Key失效

双层缓存

高可用场景

主备切换平滑

增加内存开销

互斥锁重建

防热点Key击穿

保证数据一致性

增加请求延迟

逻辑过期

超高并发热点数据

零等待时间,极致性能

实现复杂度高

分层防御体系

  • 第一层:布隆过滤器拦截非法请求
  • 第二层:空值缓存处理无效查询
  • 第三层:随机TTL+双层缓存防雪崩
  • 第四层:互斥锁+逻辑过期防击穿

热点数据特殊处理

设置热点阈值,对于热点 key,延长 TTL、多节点复制

// 热点Key识别与预加载
class HotspotManager {
public:void monitor_access(const std::string& key) {access_count[key]++;if (access_count[key] > 1000) {   // 达到热点阈值extend_ttl(key, 3600);        // 延长TTLpreload_replica(key);        // 多节点复制}}
};

熔断降级机制

  • 当数据库压力超过阈值时:
    • 自动触发熔断
    • 返回降级内容(如默认推荐)
    • 记录日志异步补偿

持续监控指标

缓存命中率 > 95% # 低于阈值告警

数据库QPS < 3000 # 超过阈值扩容

穿透请求量 < 100/s # 超过阈值启动防御

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

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

相关文章

基于GNU Radio Companion搭建的BPSK收发通信实验

目录 一、实验目的和要求 二、实验内容 1.Lab5 仿真设计一个BPSK的数字收发射系统 Lab6 实际使用RTLSDR解调BPSK信号 一、实验目的和要求 1.了解软FM的工作方式和原理,数字通信的码间串扰及星座图 2.掌握并正确使用RTL-SDL硬件和Gnuradio软件 3.正确使用Gnraduo软件,建…

华为OD机试-返回矩阵中非1的元素、个数/数值同化-BFS(JAVA 2025B卷)

import java.util.*;/*** author 308413* version Ver 1.0* date 2025/6/18* description 返回矩阵中非1的元素*/ public class Non1ElementInMatrix {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int N scanner.nextInt();int M scan…

Redis学习笔记——黑马点评 消息队列25-30

前言&#xff1a; 学习收获&#xff1a; Redis消息队列&#xff1a; 消息队列&#xff08;Message Queue&#xff09;&#xff0c;字面意思就是存放消息的队列。最简单的消息队列包括3个角色&#xff1a; 消息队列&#xff1a;存储和管理消息&#xff0c;也被称为消息代理生…

基于Django+Vue3的草莓病害检测系统设计与实现,Web前后端分离,YOLOv8 Web目标检测系统

这里写自定义目录标题 基于DjangoVue3的草莓病害检测系统 基于DjangoVue3的草莓病害检测系统 本项目结合 YOLOv8 与 Django Vue3 &#xff0c;构建了一个通用的 Web 前后端系统&#xff0c;便于用户进行目标检测的操作和展示&#xff0c;实现对图片、视频实时目标检测和摄像头…

【MFC】树控件的使用详解

目录 添加线条链接 添加折叠小按钮 设置树控件的节点和对应的图标 设置默认选中项 设置选中项切换响应函数 涉及接口介绍&#xff1a; 首先我们通过资源视图可以添加一个树形控件&#xff0c;如下&#xff1a; 添加线条链接 在树形控件中&#xff0c;有一个属性“Has…

跨境卖家警报。抽绳背包版权案立案,TRO在即速排查

近日Shenzhenshi Jingyida Trading Co., LTD委托律所Dewitty And Associates, Chtd.对其热销的抽绳设计多功能运动背包发起跨境版权维权&#xff0c;保护范围涵盖产品外观设计。 案件基本情况&#xff1a; 起诉时间&#xff1a;2025-6-12 案件号&#xff1a;25-cv-06509 原…

Android Activity全面解析:从创建到生命周期的完整指南

Activity作为Android四大组件之一&#xff0c;是构建用户界面的核心单元。笔者通过郭霖著的第一行代码入门安卓&#xff0c;内容基本都取自书中&#xff0c;这篇博客作为笔者的笔记同时精简了一些书中内容分享在csdn中 一、Activity的创建与基础配置 1.1 创建Activity的基本步…

深入理解 Python 的 secrets 模块:打造更安全的随机数生成机制

深入理解 Python 的 secrets 模块&#xff1a;打造更安全的随机数生成机制 在构建涉及用户身份认证、权限管理、加密通信等系统时&#xff0c;开发者最不能忽视的一个问题就是“安全性”。安全问题的核心之一在于“随机性”——尤其是密码、验证码、Token、Session、API Key 的…

CHAPTER 19 Concurrency Models in Python

一、A Bit of Jargon 1、关键术语解析 1.1 并发 (Concurrency) 定义: 并发是指同时处理多个待处理任务的能力&#xff0c;这些任务可以依次或并行&#xff08;如果可能&#xff09;进行&#xff0c;最终每个任务都会成功或失败。 理解: 单核 CPU: 即使是单核 CPU 也可以实…

DCM4CHEE Archive Light 开发环境部署(5)-IDEA集成调试配置

系列文章目录 DCM4CHEE Archive light 开发环境部署(1)-前言DCM4CHEE Archive light 开发环境部署(2)-PostgreSQLDCM4CHEE Archive light 开发环境部署(3)-OpenLDAPDCM4CHEE Archive light 开发环境部署(4)-Wildfly(JBoss)DCM4CHEE Archive light 开发环境部署(5)-IDEA集成…

在rust中执行命令行输出中文乱码解决办法

如果你使用标准的依赖库执行命令中包含中文的话&#xff0c; 就会发现中文乱码&#xff0c;如果你的输出中没有中文&#xff0c;就可以正常输出&#xff0c;因为windows的命令行默认使用的是gbk编码。。。。。 #[tauri::command] pub async fn run_command(command: String) -…

判断当前浏览器卡不卡

方法一&#xff1a;使用 requestAnimationFrame 和时间戳计算平均 FPS let frameCount 0; let lastTime performance.now(); let fps 0; let isSlow false; // 是否卡顿的标志function calculateFPS(currentTime) {frameCount;// 每隔大约 1000 毫秒&#xff08;1秒&#…

51c嵌入式~电路~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/11748634 一、延长电子元器件的货架寿命 本文探讨了电子元器件的货架寿命问题&#xff0c;重点讨论了氧化、湿度敏感等级&#xff08;MSL&#xff09;与货架寿命之间的关系。文章通过具体例子说明了氧化对电子元器件可…

Eureka 与 Feign(一)

Eureka 与 Feign 知识解析 1. Eureka Spring Cloud Eureka 是服务发现组件&#xff0c;包含&#xff1a; Eureka Server&#xff1a;注册中心&#xff0c;管理服务实例Eureka Client&#xff1a;服务实例&#xff0c;向注册中心注册/获取服务信息 核心功能&#xff1a; 服…

AN动画软件|Animate 2025百度云下载与安装教程指南

如大家所了解的&#xff0c;‌Animate全称Adobe Animate&#xff0c;常常也被简称为AN。它是一款2D动画制作软件‌&#xff0c;其前身为Flash Professional CC&#xff0c;2016年更名为Animate CC&#xff0c;支持Flash SWF文件及HTML5动画创作&#xff0c;广泛应用于网页交互、…

提示词工程中常见协议框架应用实例

一、生成式诊断催化协议(Generative Diagnosis Catalysis, GDC) 技术原理:基于神经符号系统的因果推理引擎,融合贝叶斯网络与强化学习 实施场景: class DiagnosticCatalyst:def __init__(self, domain="医疗诊断"):self.causal_graph

资深Java工程师的面试题目(七)JDK JVM

以下是针对 Java 面试者 的 JVM 和 JDK 相关题目&#xff0c;涵盖核心知识点、实际应用场景和进阶问题&#xff1a; 一、JVM 基础 1. JVM 内存模型 题目&#xff1a; 请描述 JVM 的内存模型及其组成部分&#xff0c;并说明每个区域的作用。 解析&#xff1a; JVM 内存模型分…

【系统设计【4】】设计一个限流器:从理论到实践的完整解决方案

文章目录 第一步&#xff1a;理解问题并确定设计范围1、为什么需要限流器2、需求澄清的艺术3、需求总结与优先级 第二步&#xff1a;提出高层次设计并获得认同1. 限流器的部署位置选择2. 限流算法的选择与权衡3. 高层架构设计 第三步&#xff1a;深入设计1、限流规则的设计与管…

基于DETR目标检测项目

DETR见解 DETR&#xff08;Detection Transformer&#xff09;是一种端到端的目标检测模型&#xff0c;由Facebook AI Research&#xff08;FAIR&#xff09;于2020年提出。DETR采用了Transformer架构&#xff0c;与传统的基于区域的目标检测方法有所不同&#xff0c;它通过全…

ZooKeeper 集群部署

ZooKeeper 集群部署 前言安装部署资源下载JDK 部署Zookeeper 部署 前言 在 Linux 服务器上部署 Zookeeper 之前&#xff0c;需要先安装 JDK。以下是相关版本及环境信息&#xff1a; JDK 版本 jdk-17_linux-x64_bin.tar.gz Zookeeper 部署的版本 3.5.7 操作系统版本 Red Hat E…