""" Redis客户端封装模块 ============================================================= 提供Redis操作的统一封装,支持字符串、哈希、列表、集合、有序集合等数据类型。 自动处理序列化/反序列化,业务代码无需关心底层存储格式。 使用方式: from utils.redis_client import redis redis.set('key', 'value', expire=3600) value = redis.get('key') """ import json import pickle from typing import Any, Union, List, Dict, Tuple from extensions.redis import redis_client class RedisClient: """ Redis客户端封装类 提供丰富的Redis操作方法,支持字符串、哈希、列表、集合、有序集合等数据类型 """ def __init__(self, client=None): """ 初始化Redis客户端 Args: client: FlaskRedis实例,如果为None则使用全局redis_client """ self.client = client or redis_client # ==================== 基础方法 ==================== def set(self, key: str, value: Any, expire: int = None, nx: bool = False, xx: bool = False) -> bool: """设置键值对""" serialized_value = self._serialize(value) if nx and xx: raise ValueError("nx和xx不能同时为True") if nx: result = self.client.setnx(key, serialized_value) if expire and result: self.client.expire(key, expire) return result elif xx: result = self.client.set(key, serialized_value, xx=True) if expire and result: self.client.expire(key, expire) return result else: if expire: return self.client.setex(key, expire, serialized_value) else: return self.client.set(key, serialized_value) def get(self, key: str, default: Any = None, deserialize: bool = True) -> Any: """获取键值""" data = self.client.get(key) if data is None: return default if deserialize: return self._deserialize(data) return data def delete(self, *keys: str) -> int: """删除一个或多个键""" if not keys: return 0 return self.client.delete(*keys) def exists(self, *keys: str) -> int: """检查键是否存在""" if not keys: return 0 return self.client.exists(*keys) def expire(self, key: str, seconds: int) -> bool: """设置键的过期时间""" return self.client.expire(key, seconds) def expireat(self, key: str, timestamp: int) -> bool: """设置键在指定时间戳过期""" return self.client.expireat(key, timestamp) def ttl(self, key: str) -> int: """获取键的剩余过期时间""" return self.client.ttl(key) def keys(self, pattern: str = '*') -> List[str]: """获取匹配模式的所有键(使用SCAN,避免KEYS阻塞)""" result = [] cursor = 0 while True: cursor, batch = self.client.scan(cursor=cursor, match=pattern, count=100) result.extend(batch) if cursor == 0: break return result # ==================== 哈希表操作 ==================== def hset(self, name: str, key: str, value: Any) -> int: """设置哈希表中的字段值""" serialized_value = self._serialize(value) return self.client.hset(name, key, serialized_value) def hget(self, name: str, key: str, deserialize: bool = True) -> Any: """获取哈希表中的字段值""" data = self.client.hget(name, key) if data is None: return None if deserialize: return self._deserialize(data) return data def hgetall(self, name: str, deserialize: bool = True) -> Dict[str, Any]: """获取哈希表中所有字段和值""" data = self.client.hgetall(name) if not data: return {} if deserialize: return {k.decode(): self._deserialize(v) for k, v in data.items()} return {k.decode(): v for k, v in data.items()} def hdel(self, name: str, *keys: str) -> int: """删除哈希表中的字段""" if not keys: return 0 return self.client.hdel(name, *keys) def hincrby(self, name: str, key: str, amount: int = 1) -> int: """为哈希表中的字段值增加指定数值""" return self.client.hincrby(name, key, amount) # ==================== 列表操作 ==================== def lpush(self, name: str, *values: Any) -> int: """从列表左侧插入元素""" serialized_values = [self._serialize(v) for v in values] return self.client.lpush(name, *serialized_values) def rpush(self, name: str, *values: Any) -> int: """从列表右侧插入元素""" serialized_values = [self._serialize(v) for v in values] return self.client.rpush(name, *serialized_values) def lpop(self, name: str, deserialize: bool = True) -> Any: """从列表左侧弹出元素""" data = self.client.lpop(name) if data is None: return None if deserialize: return self._deserialize(data) return data def rpop(self, name: str, deserialize: bool = True) -> Any: """从列表右侧弹出元素""" data = self.client.rpop(name) if data is None: return None if deserialize: return self._deserialize(data) return data def lrange(self, name: str, start: int, end: int, deserialize: bool = True) -> List[Any]: """获取列表指定范围的元素""" data = self.client.lrange(name, start, end) if not data: return [] if deserialize: return [self._deserialize(item) for item in data] return [item for item in data] def llen(self, name: str) -> int: """获取列表长度""" return self.client.llen(name) # ==================== 集合操作 ==================== def sadd(self, name: str, *values: Any) -> int: """向集合中添加元素""" serialized_values = [self._serialize(v) for v in values] return self.client.sadd(name, *serialized_values) def srem(self, name: str, *values: Any) -> int: """从集合中移除元素""" serialized_values = [self._serialize(v) for v in values] return self.client.srem(name, *serialized_values) def smembers(self, name: str, deserialize: bool = True) -> set: """获取集合中所有元素""" data = self.client.smembers(name) if not data: return set() if deserialize: return {self._deserialize(item) for item in data} return {item for item in data} def sismember(self, name: str, value: Any) -> bool: """判断元素是否在集合中""" serialized_value = self._serialize(value) return self.client.sismember(name, serialized_value) # ==================== 有序集合操作 ==================== def zadd(self, name: str, mapping: Dict[Any, float]) -> int: """向有序集合中添加元素""" serialized_mapping = {self._serialize(k): v for k, v in mapping.items()} return self.client.zadd(name, serialized_mapping) def zrange(self, name: str, start: int, end: int, withscores: bool = False, deserialize: bool = True) -> Union[List[Any], List[Tuple[Any, float]]]: """获取有序集合指定范围的元素(按分数升序)""" data = self.client.zrange(name, start, end, withscores=withscores) if not data: return [] if withscores: if deserialize: return [(self._deserialize(item[0]), item[1]) for item in data] return [(item[0], item[1]) for item in data] else: if deserialize: return [self._deserialize(item) for item in data] return list(data) def zrevrange(self, name: str, start: int, end: int, withscores: bool = False, deserialize: bool = True) -> Union[List[Any], List[Tuple[Any, float]]]: """获取有序集合指定范围的元素(按分数降序)""" data = self.client.zrevrange(name, start, end, withscores=withscores) if not data: return [] if withscores: if deserialize: return [(self._deserialize(item[0]), item[1]) for item in data] return [(item[0], item[1]) for item in data] else: if deserialize: return [self._deserialize(item) for item in data] return list(data) # ==================== 高级操作 ==================== def pipeline(self): """获取管道对象,用于批量执行命令""" return self.client.pipeline() def incr(self, key: str, amount: int = 1) -> int: """增加键的值(用于计数器)""" return self.client.incrby(key, amount) def decr(self, key: str, amount: int = 1) -> int: """减少键的值""" return self.client.decrby(key, amount) def lock(self, key: str, timeout: int = 10, blocking: bool = True, blocking_timeout: int = None) -> Any: """获取分布式锁""" return self.client.lock(key, timeout=timeout, blocking=blocking, blocking_timeout=blocking_timeout) # ==================== 私有方法 ==================== def _serialize(self, value: Any) -> Union[str, bytes]: """序列化值""" if isinstance(value, str): return value elif isinstance(value, (int, float, bool)): return str(value) elif isinstance(value, (dict, list, tuple)): return json.dumps(value, ensure_ascii=False) else: return pickle.dumps(value) def _deserialize(self, value: Union[str, bytes]) -> Any: """反序列化值""" if not value: return None if isinstance(value, bytes): try: value = value.decode('utf-8') except UnicodeDecodeError: try: return pickle.loads(value) except Exception: return value try: return json.loads(value) except (json.JSONDecodeError, TypeError): return value # 创建全局Redis客户端实例 redis = RedisClient()