| import collections |
| import sys |
| import threading |
| import logging |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| class LRUCache: |
| """ |
| 基于LRU(最近最少使用)策略的内存缓存。 |
| 支持按条目数量和内存大小进行限制。 |
| """ |
|
|
| def __init__(self, capacity: int = 100, max_memory_mb: int = 512): |
| """ |
| 初始化LRU缓存。 |
| :param capacity: 最大缓存条目数量。 |
| :param max_memory_mb: 最大缓存内存限制(MB)。 |
| """ |
| self.capacity = capacity |
| self.max_memory_bytes = max_memory_mb * 1024 * 1024 |
| self.cache = collections.OrderedDict() |
| self.current_memory_bytes = 0 |
| self.lock = threading.Lock() |
| logger.info(f"LRU缓存初始化:容量={capacity}条,最大内存={max_memory_mb}MB") |
|
|
| def _get_item_size(self, key, value): |
| """ |
| 估算单个缓存条目(键值对)的内存大小(字节)。 |
| 注意:sys.getsizeof 不会递归计算对象大小,对于复杂对象可能不准确。 |
| """ |
| return sys.getsizeof(key) + sys.getsizeof(value) |
|
|
| def get(self, key): |
| """ |
| 从缓存中获取值。如果键存在,则将其标记为最近使用。 |
| :param key: 缓存键。 |
| :return: 对应的值,如果不存在则返回None。 |
| """ |
| with self.lock: |
| if key not in self.cache: |
| return None |
| value = self.cache.pop(key) |
| self.cache[key] = value |
| logger.debug(f"从缓存获取: {key}") |
| return value |
|
|
| def set(self, key, value): |
| """ |
| 将键值对存入缓存。如果缓存超出容量或内存限制,则移除最不常用的条目。 |
| :param key: 缓存键。 |
| :param value: 要缓存的值。 |
| """ |
| with self.lock: |
| if key in self.cache: |
| old_value = self.cache.pop(key) |
| self.current_memory_bytes -= self._get_item_size(key, old_value) |
|
|
| item_size = self._get_item_size(key, value) |
|
|
| |
| while ( |
| self.current_memory_bytes + item_size > self.max_memory_bytes |
| and len(self.cache) > 0 |
| ): |
| old_key, old_value = self.cache.popitem(last=False) |
| self.current_memory_bytes -= self._get_item_size(old_key, old_value) |
| logger.warning( |
| f"缓存内存超出限制,移除最旧条目: {old_key}," |
| f"当前内存: {self.current_memory_bytes / (1024 * 1024):.2f}MB" |
| ) |
|
|
| |
| while len(self.cache) >= self.capacity and len(self.cache) > 0: |
| old_key, old_value = self.cache.popitem(last=False) |
| self.current_memory_bytes -= self._get_item_size(old_key, old_value) |
| logger.warning(f"缓存条目超出容量,移除最旧条目: {old_key},当前条目数: {len(self.cache)}") |
|
|
| self.cache[key] = value |
| self.current_memory_bytes += item_size |
| logger.debug( |
| f"设置缓存: {key},当前条目数: {len(self.cache)}," |
| f"当前内存: {self.current_memory_bytes / (1024 * 1024):.2f}MB" |
| ) |
|
|
| def delete(self, key): |
| """ |
| 从缓存中删除指定键。 |
| :param key: 缓存键。 |
| """ |
| with self.lock: |
| if key in self.cache: |
| value = self.cache.pop(key) |
| self.current_memory_bytes -= self._get_item_size(key, value) |
| logger.debug(f"从缓存删除: {key}") |
|
|
| def clear(self): |
| """ |
| 清空缓存。 |
| """ |
| with self.lock: |
| self.cache.clear() |
| self.current_memory_bytes = 0 |
| logger.info("缓存已清空。") |
|
|
| def __len__(self): |
| """ |
| 返回缓存中的条目数量。 |
| """ |
| with self.lock: |
| return len(self.cache) |
|
|
| def get_current_memory_usage_mb(self) -> float: |
| """ |
| 获取当前缓存占用的内存大小(MB)。 |
| """ |
| with self.lock: |
| return self.current_memory_bytes / (1024 * 1024) |
|
|
|
|
| |
| cache_instance = None |
|
|
|
|
| def get_cache_instance(capacity: int = 100, max_memory_mb: int = 512): |
| """ |
| 获取LRU缓存的单例实例。 |
| """ |
| global cache_instance |
| if cache_instance is None: |
| cache_instance = LRUCache(capacity, max_memory_mb) |
| return cache_instance |
|
|