📚 缓存系列 · 第 1 篇(共 7 篇)📌 本篇主角:缓存的基本原理——为什么"多查一次"比"重新算"快得多📌 你将学到:缓存的本质、Python 字典缓存体验、什么时候该用缓存📌 难度指数:⭐(零基础可学)
你有没有想过一个问题:为什么第二次打开网页比第一次快?
第一次打开:有时转圈 2 秒才出来。第二次打开:瞬间就显示。
差别在哪?——缓存。
计算机世界里,"缓存"这个词无处不在:浏览器有缓存、CPU 有缓存、数据库有缓存、应用有缓存。它不是什么高深技术,核心思想就一句话:
把算过/查过的结果存起来,下次直接用,不用重新算/重新查。
这篇我们从最简单的 Python 代码入手,搞懂缓存到底是怎么回事。
一、为什么需要缓存?先看一个真实案例
小张写了一个查询用户信息的接口:
import timedef get_user_from_db(user_id): """从数据库查询用户信息(模拟耗时 100ms)""" time.sleep(0.1) # 模拟数据库查询耗时 return {"id": user_id, "name": f"用户{user_id}"}
用户请求 100 次:
# 100 次请求,每次都要查数据库for i in range(100): user = get_user_from_db(1) # 每次都查 user_id=1print(f"总耗时: {100 * 0.1} 秒") # 10 秒!
问题很明显: 同一个用户的信息,查了 100 次数据库,每次都要等 100ms,总共 10 秒。
但如果第一次查完之后,把结果存起来,后面 99 次直接读存的结果:
import time# 简单的缓存:用字典存结果cache = {}def get_user(user_id): """带缓存的用户查询""" if user_id in cache: # 有缓存,直接返回(不用查数据库) return cache[user_id] # 没缓存,查数据库 time.sleep(0.1) user = {"id": user_id, "name": f"用户{user_id}"} cache[user_id] = user # 存到缓存里 return user# 100 次请求for i in range(100): user = get_user(1)# 只有第 1 次查了数据库,后面 99 次都是直接读缓存print(f"总耗时: 约 {0.1} 秒") # 只有 0.1 秒!
结果:10 秒 → 0.1 秒,快了 100 倍。
💡 缓存的核心价值:用空间换时间。 把结果存起来(占用内存),避免重复计算/查询(节省时间)。
二、缓存的本质:一张"速查表"
你可以把缓存想象成一张速查表:
考试作弊(不是):第一次算:1+1=2,记在小本本上第二次问:1+1=?,直接翻小本本 → 2第三次问:还是 1+1=?,还是翻小本本 → 2
缓存 = 小本本,记着你算过的结果,下次直接用。
用 Python 字典实现最简单的缓存:
# 一张速查表cache = {}def expensive_calculation(n): """一个耗时 1 秒的计算""" time.sleep(1) return n * n# 无缓存:每次都要算 1 秒result1 = expensive_calculation(5) # 耗时 1sresult2 = expensive_calculation(5) # 又耗时 1s(重复计算!)result3 = expensive_calculation(5) # 又耗时 1s# 有缓存:算过一次,后面直接查表def cached_calculation(n): if n in cache: return cache[n] # 查表,秒回 result = expensive_calculation(n) cache[n] = result # 存到表里 return resultresult1 = cached_calculation(5) # 耗时 1s(第一次计算)result2 = cached_calculation(5) # 耗时 0s(查表)result3 = cached_calculation(5) # 耗时 0s(查表)
3 次调用,从 3 秒 → 1 秒,省了两倍。
三、缓存什么时候有用?
缓存不是万能的,它只适合特定场景:
一句话:重复的、昂贵的、不常变的查询/计算,才值得缓存。
四、缓存的三个核心问题
用缓存之前,先想清楚这三个问题:
问题 1:存多久?(过期时间)
缓存不是永久的。数据会过期,比如:
用户改了名字 → 缓存里的旧名字就错了商品价格变了 → 缓存里的旧价格就错了
所以缓存要设置过期时间,到期自动删掉,重新查新的。
import timecache = {}def get_user(user_id, expire=60): """带过期时间的缓存""" if user_id in cache: data, timestamp = cache[user_id] # 检查是否过期 if time.time() - timestamp < expire: return data # 没过期,返回缓存 else: del cache[user_id] # 过期了,删掉 # 查数据库 time.sleep(0.1) user = {"id": user_id, "name": f"用户{user_id}"} cache[user_id] = (user, time.time()) # 存数据 + 时间戳 return user
问题 2:存多少?(容量限制)
内存是有限的,不能无限存。缓存满了怎么办?
策略1:先进先出(FIFO)—— 最早进来的先删掉策略2:最近最少使用(LRU)—— 最久没用的先删掉策略3:随机删除 —— 随机挑一个删掉
最常用的是 LRU(Least Recently Used),后面我们会专门手写一个。
问题 3:怎么保证缓存和数据库一致?
用户改了数据库 → 缓存里的旧数据还在 → 读到脏数据
解决办法:
五、Python 体验缓存:3 行代码
不用任何第三方库,Python 字典就能实现缓存:
cache = {}def get_data(key): if key in cache: return cache[key] # 有缓存,直接返回 # 模拟耗时操作 import time time.sleep(0.5) result = f"data_for_{key}" cache[key] = result # 存到缓存 return result# 第一次调用(慢)import timestart = time.time()get_data("user_1")print(f"第一次: {time.time() - start:.2f}s") # 0.50s# 第二次调用(快)start = time.time()get_data("user_1")print(f"第二次: {time.time() - start:.2f}s") # 0.00s
输出:
第一次: 0.50s第二次: 0.00s
第二次直接读缓存,快了几百倍。
六、缓存用在哪些地方?
缓存几乎无处不在:
本篇讲的是应用层缓存——Python 代码里怎么用缓存提速。
七、一句话总结
缓存 = 把结果存起来,下次直接用核心思想 = 用空间换时间适用场景 = 重复的、昂贵的、不常变的查询/计算
理解了缓存的本质,下一篇学 Python 的本地缓存工具就会非常顺畅。
📝 下篇预告:Python 本地缓存:一行装饰器,让函数快 10 倍
不用手写缓存逻辑,functools.lru_cache 一行搞定。
关注我,软件测试实战干货持续更新 🚀