Cache-Aside 应用层主动管理缓存:读时延后加载,写时更新DB并删除缓存 最终一致性 读操作可能延迟 低 最常用,读多写少, ...
Cache-Aside 模式实战应用层主动管理缓存的完整指南1. 什么是 Cache-Aside 模式Cache-Aside旁路缓存是应用层主动管理缓存的最常用模式。它的核心思想是应用程序直接管理缓存的读写而不是通过缓存中间件自动处理。这种模式特别适合读多写少的场景例如用户信息查询、商品详情展示、配置数据读取等。### 核心流程-读操作先查缓存缓存未命中则查数据库然后将数据写入缓存延迟加载-写操作先更新数据库然后删除缓存确保数据一致性### 为什么选择 Cache-Aside- 实现简单对现有系统侵入性小- 能有效降低数据库负载提升读性能- 最终一致性可接受短暂的数据不一致## 2. 实战代码示例Python 实现### 示例1基础 Cache-Aside 实现带日志和超时机制pythonimport timeimport jsonimport redisimport pymysqlfrom typing import Optional, Dict, Anyclass CacheAsideManager: Cache-Aside 模式管理器 支持读时延迟加载、写时删除缓存、自动过期 def __init__(self, redis_hostlocalhost, redis_port6379, mysql_hostlocalhost, mysql_userroot, mysql_passwordpassword, mysql_dbtest): # 初始化 Redis 连接 self.redis_client redis.Redis( hostredis_host, portredis_port, decode_responsesTrue # 自动解码字节串为字符串 ) # 初始化 MySQL 连接池 self.mysql_conn pymysql.connect( hostmysql_host, usermysql_user, passwordmysql_password, databasemysql_db, charsetutf8mb4, cursorclasspymysql.cursors.DictCursor ) self.cache_ttl 300 # 缓存过期时间 5 分钟 def get_user(self, user_id: int) - Optional[Dict[str, Any]]: 读操作Cache-Aside 延迟加载 1. 先查缓存 2. 如果未命中查数据库 3. 将结果写入缓存带 TTL cache_key fuser:{user_id} # 步骤1查询缓存 cached_data self.redis_client.get(cache_key) if cached_data: print(f[CACHE HIT] User {user_id} found in cache) return json.loads(cached_data) print(f[CACHE MISS] User {user_id} not in cache, querying database...) # 步骤2查询数据库 with self.mysql_conn.cursor() as cursor: sql SELECT id, username, email, created_at FROM users WHERE id %s cursor.execute(sql, (user_id,)) user_data cursor.fetchone() if not user_data: print(f[DB NOT FOUND] User {user_id} does not exist) return None # 步骤3写入缓存延迟加载 # 使用 JSON 序列化以支持复杂数据结构 serialized json.dumps(user_data, defaultstr) # defaultstr 处理日期等类型 self.redis_client.setex(cache_key, self.cache_ttl, serialized) print(f[CACHE SET] User {user_id} cached for {self.cache_ttl}s) return user_data def update_user(self, user_id: int, new_data: Dict[str, Any]) - bool: 写操作先更新数据库再删除缓存 保证最终一致性新数据写库后删除旧缓存下次读时加载新数据 print(f[WRITE] Updating user {user_id} in database...) try: with self.mysql_conn.cursor() as cursor: # 步骤1更新数据库 sql UPDATE users SET username %s, email %s WHERE id %s cursor.execute(sql, ( new_data.get(username), new_data.get(email), user_id )) self.mysql_conn.commit() print(f[DB UPDATE] User {user_id} updated successfully) # 步骤2删除缓存关键步骤 cache_key fuser:{user_id} deleted_count self.redis_client.delete(cache_key) if deleted_count 0: print(f[CACHE DELETE] Cache for user {user_id} removed) else: print(f[CACHE DELETE] No cached data for user {user_id}) return True except Exception as e: print(f[ERROR] Failed to update user {user_id}: {str(e)}) self.mysql_conn.rollback() return False def close(self): 清理资源 self.redis_client.close() self.mysql_conn.close()# 使用示例if __name__ __main__: manager CacheAsideManager() # 读操作测试 user manager.get_user(1001) print(fRead user: {user}) # 再次读取应该命中缓存 user_again manager.get_user(1001) print(fRead again (cached): {user_again}) # 写操作测试 success manager.update_user(1001, { username: new_name, email: newexample.com }) print(fUpdate result: {success}) # 读取更新后的数据从数据库加载新数据到缓存 updated_user manager.get_user(1001) print(fUpdated user: {updated_user}) manager.close()### 示例2高并发场景下的 Cache-Aside 优化带防缓存击穿pythonimport threadingimport randomimport timefrom functools import wrapsclass OptimizedCacheAside: 优化版 Cache-Aside解决缓存击穿、雪崩问题 使用互斥锁防止缓存击穿热点数据重建时 使用随机 TTL 防止缓存雪崩 def __init__(self): # 模拟缓存和数据库实际环境用 Redis/MySQL self.cache {} self.database { 1: {name: Alice, age: 25}, 2: {name: Bob, age: 30}, 3: {name: Charlie, age: 35}, } self.locks {} # 每个 key 有独立的锁 self.lock threading.Lock() self.cache_ttl_base 300 # 基础 TTL 5 分钟 def _get_lock(self, key: str) - threading.Lock: 获取或创建 key 对应的锁 with self.lock: if key not in self.locks: self.locks[key] threading.Lock() return self.locks[key] def get_user(self, user_id: int) - dict: 读操作带防缓存击穿机制的 Cache-Aside 使用互斥锁确保同一时间只有一个线程重建缓存 cache_key fuser:{user_id} # 步骤1先查缓存快速路径 if cache_key in self.cache: cached self.cache[cache_key] if cached[expire_time] time.time(): print(f[Thread-{threading.get_ident()}] Cache HIT for user {user_id}) return cached[data] else: # 缓存过期删除旧缓存 del self.cache[cache_key] # 步骤2获取锁防止缓存击穿 key_lock self._get_lock(cache_key) acquired key_lock.acquire(timeout3) # 最多等 3 秒 if not acquired: # 如果超时未获取锁直接查数据库降级策略 print(f[Thread-{threading.get_ident()}] Lock timeout, falling back to DB) return self._query_database(user_id) try: # 步骤3二次检查缓存防止其他线程已加载 if cache_key in self.cache: cached self.cache[cache_key] if cached[expire_time] time.time(): print(f[Thread-{threading.get_ident()}] Double-check: cache already loaded) return cached[data] # 步骤4查询数据库 print(f[Thread-{threading.get_ident()}] Loading user {user_id} from database...) time.sleep(0.1) # 模拟数据库查询延迟 user_data self._query_database(user_id) # 步骤5写入缓存带随机 TTL 防止雪崩 random_ttl self.cache_ttl_base random.randint(-60, 60) # ±1分钟随机 self.cache[cache_key] { data: user_data, expire_time: time.time() random_ttl } print(f[Thread-{threading.get_ident()}] Cached user {user_id} with TTL {random_ttl}s) return user_data finally: key_lock.release() def _query_database(self, user_id: int) - dict: 模拟数据库查询 time.sleep(0.05) # 模拟 I/O 延迟 data self.database.get(user_id) if data is None: raise ValueError(fUser {user_id} not found) return data.copy() def update_user(self, user_id: int, new_data: dict) - bool: 写操作更新数据库并删除缓存保证一致性 # 步骤1更新数据库 print(fUpdating user {user_id} in database...) time.sleep(0.1) # 模拟数据库写入延迟 self.database[user_id] new_data # 步骤2删除缓存 cache_key fuser:{user_id} if cache_key in self.cache: del self.cache[cache_key] print(fCache for user {user_id} deleted) return True# 并发测试def concurrent_read_test(): 模拟高并发读操作 manager OptimizedCacheAside() threads [] # 创建 10 个线程同时读取同一个用户 for _ in range(10): t threading.Thread(targetlambda: manager.get_user(1)) threads.append(t) print(Starting concurrent reads for user 1...) start_time time.time() for t in threads: t.start() for t in threads: t.join() elapsed time.time() - start_time print(fAll reads completed in {elapsed:.3f}s) print(fOnly one thread should have called the database)if __name__ __main__: concurrent_read_test()## 3. 关键设计要点与陷阱### 3.1 为什么写操作要删除缓存而不是更新-删除更简单避免复杂的数据同步逻辑-避免并发问题如果更新缓存和数据库操作不是原子的可能导致脏数据-延迟加载天然解决一致性下次读操作会从数据库拉取最新数据### 3.2 最终一致性带来的问题-短暂的不一致窗口更新数据库 - 删除缓存期间其他线程可能读到旧缓存-解决方案 - 对于强一致性要求的场景可以使用先删缓存后更新DB但可能引发更多问题 - 使用消息队列异步更新缓存 - 设置合理的缓存过期时间### 3.3 缓存雪崩与击穿防护| 问题 | 原因 | 解决方案 ||------|------|----------|| 缓存雪崩 | 大量缓存同时过期 | 随机 TTL、多级缓存 || 缓存击穿 | 热点数据过期后高并发请求 | 互斥锁重建缓存 || 缓存穿透 | 查询不存在的数据 | 布隆过滤器、空值缓存 |## 4. 性能优化实践python# 批量加载优化pipeline 减少网络开销def batch_get_users(self, user_ids: list) - dict: 批量获取用户使用 Redis pipeline 优化 pipe self.redis_client.pipeline() # 步骤1批量查询缓存 for uid in user_ids: pipe.get(fuser:{uid}) cache_results pipe.execute() # 步骤2找出未命中的 ID miss_ids [] for i, result in enumerate(cache_results): if result is None: miss_ids.append(user_ids[i]) # 步骤3批量查询数据库 if miss_ids: with self.mysql_conn.cursor() as cursor: format_strings ,.join([%s] * len(miss_ids)) sql fSELECT * FROM users WHERE id IN ({format_strings}) cursor.execute(sql, miss_ids) db_results cursor.fetchall() # 步骤4批量写入缓存 pipe self.redis_client.pipeline() for user in db_results: pipe.setex(fuser:{user[id]}, self.cache_ttl, json.dumps(user)) pipe.execute()## 5. 总结Cache-Aside 模式是应用层缓存管理的基石它的优势在于1.简单可靠读时延迟加载、写时删除缓存的逻辑直观容易理解和维护2.性能优秀适合读多写少场景能显著降低数据库负载可实现 90% 的缓存命中率3.灵活性高可以针对不同数据设置不同 TTL、缓存策略但也要注意-最终一致性不是银弹对一致性要求极高的场景如金融交易需要额外处理-缓存击穿/雪崩需要提前设计防护机制-写操作后的缓存删除必须确保成功可通过重试机制或异步删除在实际项目中建议将 Cache-Aside 作为默认缓存策略配合布隆过滤器防穿透、互斥锁防击穿、随机 TTL 防雪崩可以构建一个稳定高效的缓存系统。记住缓存是系统加速器但永远不能替代数据库作为数据权威来源。

相关新闻

3步掌握HTTrack:将任何网站完整保存到本地的终极指南

3步掌握HTTrack:将任何网站完整保存到本地的终极指南

3步掌握HTTrack:将任何网站完整保存到本地的终极指南 【免费下载链接】httrack HTTrack Website Copier, copy websites to your computer (Official repository) 项目地址: https://gitcode.com/gh_mirrors/ht/httrack 你是否曾需要离线访问某个网站&#x…

2026/7/28 1:19:09 阅读更多 →
ComfyUI-Manager终极指南:三步打造你的AI绘画扩展中心

ComfyUI-Manager终极指南:三步打造你的AI绘画扩展中心

ComfyUI-Manager终极指南:三步打造你的AI绘画扩展中心 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable various custo…

2026/7/28 1:19:09 阅读更多 →
一键备份QQ空间说说:GetQzonehistory终极指南,永久保存你的青春记忆

一键备份QQ空间说说:GetQzonehistory终极指南,永久保存你的青春记忆

一键备份QQ空间说说:GetQzonehistory终极指南,永久保存你的青春记忆 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 还记得那些年你在QQ空间留下的青春足迹吗&am…

2026/7/28 1:19:09 阅读更多 →

最新新闻

3个步骤让魔兽争霸3在现代电脑上完美运行:WarcraftHelper兼容性修复指南

3个步骤让魔兽争霸3在现代电脑上完美运行:WarcraftHelper兼容性修复指南

3个步骤让魔兽争霸3在现代电脑上完美运行:WarcraftHelper兼容性修复指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 你是否还在为魔兽…

2026/7/28 1:27:12 阅读更多 →
【企业微信】基于iPad协议的联系人管理接口:支持内外部联系人操作与多渠道添加功能

【企业微信】基于iPad协议的联系人管理接口:支持内外部联系人操作与多渠道添加功能

一、简要总结 本文档是企业微信 iPad 协议接口的联系人模块功能说明,共包含20 个细分操作接口,所有接口统一采用POST请求、ContentType:application/json格式,uuid 为唯一必传核心参数,服务域名以[172.0.0.1:8083](17…

2026/7/28 1:27:12 阅读更多 →
功能详解 02 · 登录、退出与 Session:Cookie ttms_sid 如何认人

功能详解 02 · 登录、退出与 Session:Cookie ttms_sid 如何认人

🥰个人主页:会编程的土豆(欢迎来访) 💎作者简介:后端学习者 ❄️个人专栏:数据结构与算法,数据库,leetcode ✨那些你一个人走过的夜路,终将化作照亮未来的光 …

2026/7/28 1:27:12 阅读更多 →
​​​​​​功能详解 01 · 用户注册:从表单到数据库一行

​​​​​​功能详解 01 · 用户注册:从表单到数据库一行

🥰个人主页:会编程的土豆(欢迎来访) 💎作者简介:后端学习者 ❄️个人专栏:数据结构与算法,数据库,leetcode ✨那些你一个人走过的夜路,终将化作照亮未来的光 …

2026/7/28 1:27:12 阅读更多 →
目前 Claude / GPT 的订阅建议与反代避坑

目前 Claude / GPT 的订阅建议与反代避坑

半年来在 Claude 和 GPT 的 CLI 上累计消耗了约 30,000$ 用量的 tokens(Claude : GPT 2: 1,Fable 5 出来之后 GPT 用量为 0)。这期间关于模型对比体验的文章断断续续其实写了不少,但 Agent 相关技术和概念迭代太快,新…

2026/7/28 1:27:11 阅读更多 →
HarmonyOS应用开发实战:猫猫大作战-@Extend 的使用

HarmonyOS应用开发实战:猫猫大作战-@Extend 的使用

前言 Extend 装饰器用于为特定组件类型扩展样式方法,与 Styles 的通用属性集合不同,Extend 可以访问组件特有的属性。 本文以「猫猫大作战」的 Text 标题样式为锚点,讲解 Extend 的使用。 提示:本系列不讲 ArkTS 基础语法与环境…

2026/7/28 1:26:11 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/27 4:33:59 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/27 6:31:56 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/27 4:01:12 阅读更多 →

月新闻