首页 > 应用开发 > Python > Python缓存一致性处理
# Python缓存一致性处理
缓存一致性是确保缓存数据与底层数据源保持同步的重要问题。在Python中处理缓存一致性有多种方法,下面我将介绍几种常见的策略和实现方式。
## 1. 缓存失效策略
### 1.1 时间过期策略
```python
from datetime import datetime, timedelta
import functools
def timed_cache(seconds: int, maxsize: int = 128):
def wrapper_cache(func):
func = functools.lru_cache(maxsize=maxsize)(func)
func.lifetime = timedelta(seconds=seconds)
func.expiration = datetime.utcnow() + func.lifetime
@functools.wraps(func)
def wrapped_func(*args, **kwargs):
if datetime.utcnow() >= func.expiration:
func.cache_clear()
func.expiration = datetime.utcnow() + func.lifetime
return func(*args, **kwargs)
return wrapped_func
return wrapper_cache
@timed_cache(seconds=60)
def get_expensive_data():
# 获取数据的逻辑
pass
```
### 1.2 基于事件的失效
```python
from functools import lru_cache
# 使用装饰器
@lru_cache(maxsize=128)
def get_data(key):
# 从数据库获取数据
pass
# 当数据变更时清除缓存
def update_data(key, value):
# 更新数据库
get_data.cache_clear() # 清除整个缓存
# 或者更精确地清除特定键的缓存
get_data.cache_pop(key, None)
```
## 2. 使用缓存库
### 2.1 Redis缓存
```python
import redis
import pickle
r = redis.Redis(host='localhost', port=6379, db=0)
def get_data_with_cache(key):
# 尝试从缓存获取
cached_data = r.get(key)
if cached_data:
return pickle.loads(cached_data)
# 缓存中没有,从数据库获取
data = get_data_from_db(key)
# 存入缓存,设置过期时间
r.setex(key, 3600, pickle.dumps(data)) # 1小时过期
return data
def update_data_with_cache(key, value):
# 更新数据库
update_data_in_db(key, value)
# 更新缓存
r.setex(key, 3600, pickle.dumps(value))
```
### 2.2 使用Django缓存框架
```python
from django.core.cache import cache
def get_user_profile(user_id):
cache_key = f"user_profile_{user_id}"
profile = cache.get(cache_key)
if profile is None:
profile = UserProfile.objects.get(user_id=user_id)
cache.set(cache_key, profile, timeout=3600) # 1小时过期
return profile
def update_user_profile(user_id, data):
# 更新数据库
profile = UserProfile.objects.get(user_id=user_id)
profile.update(data)
# 更新缓存
cache_key = f"user_profile_{user_id}"
cache.set(cache_key, profile, timeout=3600)
```
## 3. 高级策略
### 3.1 读写穿透 (Read/Write Through)
```python
class CacheThrough:
def __init__(self):
self.cache = {}
def get(self, key):
if key in self.cache:
return self.cache[key]
# 缓存未命中,从数据库加载
value = self.load_from_db(key)
self.cache[key] = value
return value
def set(self, key, value):
# 先更新数据库
self.save_to_db(key, value)
# 再更新缓存
self.cache[key] = value
def load_from_db(self, key):
# 数据库读取逻辑
pass
def save_to_db(self, key, value):
# 数据库写入逻辑
pass
```
### 3.2 写回 (Write Behind)
```python
import threading
import time
class WriteBehindCache:
def __init__(self):
self.cache = {}
self.dirty_keys = set()
self.lock = threading.Lock()
self.running = True
self.worker = threading.Thread(target=self.flush_worker)
self.worker.start()
def get(self, key):
with self.lock:
return self.cache.get(key)
def set(self, key, value):
with self.lock:
self.cache[key] = value
self.dirty_keys.add(key)
def flush_worker(self):
while self.running:
time.sleep(10) # 每10秒刷新一次
self.flush()
def flush(self):
with self.lock:
dirty = self.dirty_keys.copy()
self.dirty_keys.clear()
# 批量更新数据库
self.batch_save_to_db(dirty)
def batch_save_to_db(self, keys):
# 批量保存到数据库的逻辑
pass
def stop(self):
self.running = False
self.worker.join()
self.flush() # 最后一次刷新
```
## 4. 分布式缓存一致性
### 4.1 使用版本号或时间戳
```python
def get_data_with_version(key):
# 获取数据和版本号
data, version = get_from_db_with_version(key)
# 检查缓存中的数据是否过期
cached_data, cached_version = cache.get(key, (None, None))
if cached_version == version:
return cached_data
# 缓存过期,更新缓存
cache.set(key, (data, version))
return data
def update_data_with_version(key, new_data):
# 更新数据库并获取新版本号
new_version = update_db_and_get_version(key, new_data)
# 更新缓存
cache.set(key, (new_data, new_version))
```
## 5. 最佳实践
1. **选择合适的缓存策略**:根据数据访问模式选择LRU、LFU或FIFO等策略
2. **设置合理的过期时间**:避免缓存数据长时间不更新
3. **考虑缓存穿透**:对不存在的键也进行缓存(缓存空值)
4. **防止缓存雪崩**:设置不同的过期时间或使用互斥锁
5. **监控缓存命中率**:评估缓存效果
```python
# 防止缓存穿透的示例
def get_product(product_id):
https://zq.zhaopin.com/moment/71139063
https://zq.zhaopin.com/moment/71138886
https://zq.zhaopin.com/moment/71138784
https://zq.zhaopin.com/moment/71138499
https://zq.zhaopin.com/moment/71138430
https://zq.zhaopin.com/moment/71138309
https://zq.zhaopin.com/moment/71138245
https://zq.zhaopin.com/moment/71138244
https://zq.zhaopin.com/moment/71138240
https://zq.zhaopin.com/moment/71138237
https://zq.zhaopin.com/moment/71138236
https://zq.zhaopin.com/moment/71137873
https://zq.zhaopin.com/moment/71137764
https://zq.zhaopin.com/moment/71137708
https://zq.zhaopin.com/moment/71137605
https://zq.zhaopin.com/moment/71134097
https://zq.zhaopin.com/moment/71134036
# 尝试从缓存获取
product = cache.get(f"product_{product_id}")
if product is None:
# 从数据库获取
product = db.get_product(product_id)
if product is None:
# 缓存空值,短时间过期
cache.set(f"product_{product_id}", "NULL", timeout=60)
else:
# 缓存真实值
cache.set(f"product_{product_id}", product, timeout=3600)
return None if product == "NULL" else product
```
通过合理选择和应用这些策略,可以在Python应用中有效处理缓存一致性问题,提高系统性能的同时保证数据的准确性。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/34366/viewspace-3101107/ ,如需转载,请注明出处,否则将追究法律责任。