引入Redis

This commit is contained in:
雅诺狐
2025-12-08 17:42:57 +08:00
parent f9b193c86d
commit da27c865d0
8 changed files with 1109 additions and 82 deletions

View File

@@ -0,0 +1,210 @@
"""缓存后端抽象基类
定义统一的缓存接口,支持多种缓存后端实现:
- MemoryCache: 内存多级缓存L1 + L2
- RedisCache: Redis 分布式缓存
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any
@dataclass
class CacheStats:
"""缓存统计信息
Attributes:
hits: 命中次数
misses: 未命中次数
evictions: 淘汰次数
total_size: 总大小(字节)
item_count: 条目数量
"""
hits: int = 0
misses: int = 0
evictions: int = 0
total_size: int = 0
item_count: int = 0
@property
def hit_rate(self) -> float:
"""命中率"""
total = self.hits + self.misses
return self.hits / total if total > 0 else 0.0
@property
def eviction_rate(self) -> float:
"""淘汰率"""
return self.evictions / self.item_count if self.item_count > 0 else 0.0
class CacheBackend(ABC):
"""缓存后端抽象基类
定义统一的缓存操作接口,所有缓存实现必须继承此类
"""
@abstractmethod
async def get(self, key: str) -> Any | None:
"""从缓存获取数据
Args:
key: 缓存键
Returns:
缓存值,如果不存在返回 None
"""
pass
@abstractmethod
async def set(
self,
key: str,
value: Any,
ttl: float | None = None,
) -> None:
"""设置缓存值
Args:
key: 缓存键
value: 缓存值
ttl: 过期时间None 表示使用默认 TTL
"""
pass
@abstractmethod
async def delete(self, key: str) -> bool:
"""删除缓存条目
Args:
key: 缓存键
Returns:
是否成功删除
"""
pass
@abstractmethod
async def exists(self, key: str) -> bool:
"""检查键是否存在
Args:
key: 缓存键
Returns:
键是否存在
"""
pass
@abstractmethod
async def clear(self) -> None:
"""清空所有缓存"""
pass
@abstractmethod
async def get_stats(self) -> dict[str, Any]:
"""获取缓存统计信息
Returns:
包含命中率、条目数等统计数据的字典
"""
pass
@abstractmethod
async def close(self) -> None:
"""关闭缓存连接/清理资源"""
pass
async def get_or_load(
self,
key: str,
loader: Any,
ttl: float | None = None,
) -> Any | None:
"""获取缓存或通过 loader 加载
Args:
key: 缓存键
loader: 数据加载函数(同步或异步)
ttl: 过期时间(秒)
Returns:
缓存值或加载的值
"""
import asyncio
# 尝试从缓存获取
value = await self.get(key)
if value is not None:
return value
# 缓存未命中,使用 loader 加载
if loader is not None:
if asyncio.iscoroutinefunction(loader):
value = await loader()
else:
value = loader()
if value is not None:
await self.set(key, value, ttl=ttl)
return value
return None
async def delete_pattern(self, pattern: str) -> int:
"""删除匹配模式的所有键(可选实现)
Args:
pattern: 键模式(支持 * 通配符)
Returns:
删除的键数量
"""
# 默认实现:不支持模式删除
raise NotImplementedError("此缓存后端不支持模式删除")
async def mget(self, keys: list[str]) -> dict[str, Any]:
"""批量获取多个键的值(可选实现)
Args:
keys: 键列表
Returns:
键值对字典,不存在的键不包含在结果中
"""
# 默认实现:逐个获取
result = {}
for key in keys:
value = await self.get(key)
if value is not None:
result[key] = value
return result
async def mset(
self,
mapping: dict[str, Any],
ttl: float | None = None,
) -> None:
"""批量设置多个键值对(可选实现)
Args:
mapping: 键值对字典
ttl: 过期时间(秒)
"""
# 默认实现:逐个设置
for key, value in mapping.items():
await self.set(key, value, ttl=ttl)
@property
@abstractmethod
def backend_type(self) -> str:
"""返回缓存后端类型标识"""
pass
@property
def is_distributed(self) -> bool:
"""是否为分布式缓存(默认 False"""
return False