news 2026/10/21 6:47:19

HoRain云--Python缓存策略全解析:本地缓存、Redis与缓存一致性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HoRain云--Python缓存策略全解析:本地缓存、Redis与缓存一致性

引言

缓存是提升性能的银弹,但用不好会导致数据不一致、内存泄漏。本文从本地缓存到分布式缓存,讲解缓存模式、失效策略和一致性方案。

一、缓存模式

1.1 Cache-Aside(旁路缓存)

最常用:先查缓存,未命中查数据库,回写缓存。

python

复制

下载

def get_user(user_id): key = f"user:{user_id}" cached = redis.get(key) if cached: return json.loads(cached) user = db.query_user(user_id) if user: redis.setex(key, 3600, json.dumps(user)) return user

1.2 Read-Through

缓存层负责加载,应用只与缓存交互。

1.3 Write-Through

写操作同时更新缓存和数据库。

1.4 Write-Behind

先写缓存,异步刷数据库。

二、本地缓存

2.1 functools.lru_cache

python

复制

下载

from functools import lru_cache @lru_cache(maxsize=128) def expensive_func(n): print(f"计算 {n}") return n ** 2 expensive_func(10) # 计算10 expensive_func(10) # 命中缓存

带TTL的版本(Python 3.9+无原生支持):

python

复制

下载

import time from functools import wraps def ttl_cache(seconds): def decorator(func): cache = {} @wraps(func) def wrapper(*args): now = time.time() if args in cache: result, expire = cache[args] if now < expire: return result result = func(*args) cache[args] = (result, now + seconds) return result return wrapper return decorator @ttl_cache(60) def get_config(key): return db.query_config(key)

2.2 cachetools

bash

复制

下载

pip install cachetools

python

复制

下载

from cachetools import TTLCache, LRUCache, cached cache = TTLCache(maxsize=1000, ttl=300) @cached(cache) def get_user(user_id): return db.query_user(user_id)

2.3 多级缓存

python

复制

下载

class TwoLevelCache: def __init__(self, local, remote): self.local = local # TTLCache self.remote = remote # Redis def get(self, key): value = self.local.get(key) if value is not None: return value value = self.remote.get(key) if value is not None: self.local[key] = value return value def set(self, key, value, ttl=300): self.local[key] = value self.remote.setex(key, ttl, value)

三、Redis缓存

3.1 基础操作

python

复制

下载

import redis import json r = redis.Redis(host="localhost", port=6379, decode_responses=True) # 字符串 r.set("key", "value", ex=60) r.get("key") # JSON r.setex("user:1", 3600, json.dumps({"id": 1, "name": "Tom"})) # Hash r.hset("user:1", mapping={"id": 1, "name": "Tom"}) r.hgetall("user:1")

3.2 缓存装饰器

python

复制

下载

import json import hashlib from functools import wraps def redis_cache(prefix, ttl=300): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): key_data = f"{prefix}:{args}:{sorted(kwargs.items())}" key = hashlib.md5(key_data.encode()).hexdigest() cached = r.get(key) if cached: return json.loads(cached) result = func(*args, **kwargs) r.setex(key, ttl, json.dumps(result, default=str)) return result return wrapper return decorator @redis_cache("get_user", ttl=600) def get_user(user_id): return db.query_user(user_id)

四、缓存三大问题

4.1 缓存穿透

查询不存在的数据,请求直达数据库。

python

复制

下载

NULL_VALUE = "__NULL__" def get_user(user_id): key = f"user:{user_id}" cached = r.get(key) if cached is not None: return None if cached == NULL_VALUE else json.loads(cached) user = db.query_user(user_id) if user is None: r.setex(key, 60, NULL_VALUE) # 缓存空值 return None r.setex(key, 3600, json.dumps(user)) return user

布隆过滤器:

bash

复制

下载

pip install pybloom-live

python

复制

下载

from pybloom_live import BloomFilter bf = BloomFilter(capacity=1000000, error_rate=0.001) bf.add("user:1") if "user:1" in bf: # 可能存在,查缓存 pass else: # 一定不存在 return None

4.2 缓存击穿

热点key过期瞬间,大量请求打数据库。

python

复制

下载

import threading _locks = {} def get_user_with_lock(user_id): key = f"user:{user_id}" cached = r.get(key) if cached: return json.loads(cached) lock = _locks.setdefault(key, threading.Lock()) with lock: # 双重检查 cached = r.get(key) if cached: return json.loads(cached) user = db.query_user(user_id) r.setex(key, 3600, json.dumps(user)) return user

分布式锁版本用Redis:

python

复制

下载

lock_key = f"lock:{key}" if r.set(lock_key, "1", nx=True, ex=10): try: # 查数据库、回写缓存 pass finally: r.delete(lock_key)

4.3 缓存雪崩

大量key同时过期。

python

复制

下载

import random def set_with_jitter(key, value, base_ttl=3600): ttl = base_ttl + random.randint(0, 600) r.setex(key, ttl, value)

其他措施:

  • 多级缓存

  • 限流降级

  • 预热

五、缓存一致性

5.1 先更新数据库还是先删缓存

推荐:先更新数据库,再删除缓存(Cache-Aside)。

python

复制

下载

def update_user(user_id, data): db.update_user(user_id, data) r.delete(f"user:{user_id}")

延迟双删:

python

复制

下载

def update_user(user_id, data): r.delete(f"user:{user_id}") db.update_user(user_id, data) time.sleep(0.5) # 延迟 r.delete(f"user:{user_id}")

5.2 订阅binlog

用Canal订阅MySQL binlog,异步更新缓存,保证最终一致。

5.3 版本号

python

复制

下载

def get_user(user_id): version = r.get(f"user:version:{user_id}") or 0 key = f"user:{user_id}:{version}" # ...

六、缓存淘汰策略

Redis支持:

  • noeviction:不淘汰,写入报错

  • allkeys-lru:所有key中LRU

  • allkeys-lfu:所有key中LFU

  • volatile-lru:过期key中LRU

  • volatile-ttl:优先淘汰TTL短的

bash

复制

下载

# redis.conf maxmemory 2gb maxmemory-policy allkeys-lru

七、实战:商品详情缓存

python

复制

下载

class ProductCache: def __init__(self, redis_client, db): self.redis = redis_client self.db = db self.local = TTLCache(maxsize=1000, ttl=60) def get(self, product_id): key = f"product:{product_id}" # L1:本地缓存 if key in self.local: return self.local[key] # L2:Redis cached = self.redis.get(key) if cached: data = json.loads(cached) self.local[key] = data return data # L3:数据库 product = self.db.query_product(product_id) if not product: return None data = product.to_dict() ttl = 3600 + random.randint(0, 300) self.redis.setex(key, ttl, json.dumps(data)) self.local[key] = data return data def invalidate(self, product_id): key = f"product:{product_id}" self.local.pop(key, None) self.redis.delete(key)

八、总结

缓存核心:选对模式、防止三大问题、保证一致性。本地缓存快但有延迟,Redis统一但有网络开销,多级缓存兼顾。记住:缓存不是数据库,要能容忍丢失。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 6:47:16

AI写小说工具实测:7款神器卡文破局与日更能力全面对比

前几天一位写连载的朋友跟我抱怨&#xff1a;卡文卡到怀疑人生&#xff0c;日更压力大到凌晨三点还在对着空白文档发呆。我说你手边那么多AI写小说神器怎么不用&#xff1f;他说用了几款&#xff0c;要么生成的文字一股AI味&#xff0c;要么越写越跑偏&#xff0c;索性弃了。这…

作者头像 李华
网站建设 2026/10/21 6:46:14

知识工作插件:面向认知效率的可组装能力协议

1. 项目概述&#xff1a;这不是插件&#xff0c;而是一套知识工作者的“操作系统增强层”“knowledge-work-plugins”这个标题乍看像某个开源仓库的命名&#xff0c;但拆开来看——knowledge work&#xff08;知识工作&#xff09;、plugins&#xff08;插件&#xff09;&#…

作者头像 李华
网站建设 2026/10/21 6:45:16

枕包机六轴定位与凸轮控制:基于欧姆龙NJ的完整方案

枕包机这套东西&#xff0c;在包装行业里算是典型的“多轴协同”设备&#xff1a;送膜、制袋、充填、横封、切断、输出&#xff0c;一环扣一环。欧姆龙NJ501-1500在这类设备上用得不少&#xff0c;六轴定位控制加凸轮控制&#xff0c;再把梯形图程序写得规规矩矩&#xff0c;设…

作者头像 李华
网站建设 2026/10/21 6:42:03

skills不是标签:构建可验证、有时效、带上下文的技术能力坐标系

1. “skills”不是标签&#xff0c;是能力坐标系的动态刻度最近在几个技术社区和职业发展类平台翻看内容时&#xff0c;注意到一个有趣现象&#xff1a;越来越多的项目文档、简历模块、甚至内部系统权限配置里&#xff0c;开始高频出现一个看似极简却异常模糊的字段——skills。…

作者头像 李华
网站建设 2026/10/21 6:41:44

CTF实战:Python反序列化黑名单绕过技巧全解析

最近复盘了一场2024年的CTF赛事&#xff0c;里面有一道Python反序列化漏洞题目&#xff0c;出题人给的黑名单看着很全&#xff0c;eval、exec、os、system、subprocess这些常见入口全部被拉进了过滤列表。很多选手在第一版payload被打回之后就卡住了&#xff0c;但实际这题并没…

作者头像 李华
网站建设 2026/10/21 6:41:34

Agent-Skills架构:智能体能力的原子化、声明化与可治理设计

1. 项目概述&#xff1a;一个被严重低估的“技能容器”概念“agent-skills”这个词组乍看像技术黑话&#xff0c;但拆开来看——agent 是智能体&#xff0c;skills 是技能&#xff0c;合起来就是“智能体所具备的能力集合”。它不是某个具体工具、框架或开源库的名字&#xff0…

作者头像 李华