227 lines
5.7 KiB
Markdown
227 lines
5.7 KiB
Markdown
# ObjectCache 技术文档
|
||
|
||
```markdown
|
||
# ObjectCache 类技术文档
|
||
|
||
## 概述
|
||
|
||
`ObjectCache` 是一个基于字典的内存对象缓存类,用于管理具有大小属性的对象集合。它继承自 Python 内置的 `dict` 类,并扩展了容量限制、自动淘汰和访问时间追踪功能。
|
||
|
||
该缓存适用于需要控制内存使用量、并能通过 `.get_size()` 方法获取对象大小的场景。当缓存总大小接近预设上限时,会触发基于最近最少使用(LRU)策略的半数淘汰机制。
|
||
|
||
---
|
||
|
||
## 特性
|
||
|
||
- 支持类似字典的键值存储操作。
|
||
- 自动跟踪每个对象的大小(需实现 `get_size()` 方法)。
|
||
- 设置最大缓存容量(以字节或其他单位计),防止内存溢出。
|
||
- 使用 LRU(最近最少使用)策略进行对象淘汰。
|
||
- 记录对象最后访问时间以支持淘汰逻辑。
|
||
|
||
---
|
||
|
||
## 类定义
|
||
|
||
```python
|
||
class ObjectCache(dict)
|
||
```
|
||
|
||
继承自 `dict`,因此支持所有标准字典操作(如 `in`, `len()`, 迭代等)。
|
||
|
||
---
|
||
|
||
## 初始化方法
|
||
|
||
### `__init__(self, maxsize=10000000, *args)`
|
||
|
||
#### 参数说明:
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|-----------|--------|------|
|
||
| `maxsize` | int | 缓存允许的最大总大小,默认为 10,000,000 单位(例如字节)。 |
|
||
| `*args` | tuple | 传递给父类 `dict` 的初始化参数(可选)。 |
|
||
|
||
#### 属性初始化:
|
||
|
||
| 属性 | 类型 | 说明 |
|
||
|------------|--------|------|
|
||
| `self.maxsize` | int | 最大缓存容量限制。 |
|
||
| `self.size` | int | 当前缓存中所有对象的总大小,初始为 0。 |
|
||
| `self._shadow` | dict | 私有字典,记录每个键对应的 `[最后访问时间, 对象大小]`。 |
|
||
|
||
> ⚠️ 注意:必须导入 `time` 模块,否则在设置项时会抛出 `NameError`。
|
||
|
||
---
|
||
|
||
## 核心方法
|
||
|
||
### `__setitem__(key, item)`
|
||
|
||
将对象插入或更新到缓存中。
|
||
|
||
#### 行为流程:
|
||
|
||
1. 调用 `item.get_size()` 获取对象大小。
|
||
2. 若失败(无此方法或异常),则直接返回,不缓存该对象。
|
||
3. 成功获取大小后,将其加到 `self.size`。
|
||
4. 如果当前总大小超过 `maxsize`:
|
||
- 从 `self._shadow` 中提取所有 `(访问时间, key)` 元组。
|
||
- 按访问时间排序(越早访问的排在前面)。
|
||
- 删除前一半最久未访问的条目及其对应的数据。
|
||
5. 调用父类 `__setitem__` 存储新对象。
|
||
6. 在 `_shadow` 中记录其访问时间和大小。
|
||
|
||
#### 注意事项:
|
||
|
||
- 存在 bug:`tmp[i][i]` 应为 `tmp[i][1]`(即 key),原代码会导致索引错误。
|
||
- 正确写法应为:
|
||
```python
|
||
for i in xrange(len(tmp)//2):
|
||
del self[tmp[i][1]] # tmp[i] 是 (time, key),所以取 [1]
|
||
```
|
||
|
||
---
|
||
|
||
### `__getitem__(key)`
|
||
|
||
获取指定键对应的对象。
|
||
|
||
#### 行为流程:
|
||
|
||
1. 尝试调用父类 `__getitem__` 获取对象。
|
||
2. 若存在,则更新该键在 `_shadow` 中的访问时间为当前时间(`time.time()`)。
|
||
3. 返回对象。
|
||
|
||
#### 异常处理:
|
||
|
||
- 若键不存在,重新抛出 `KeyError`。
|
||
|
||
---
|
||
|
||
### `get(key, default=None)`
|
||
|
||
安全获取指定键的对象,若不存在返回默认值。
|
||
|
||
#### 参数:
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|----------|------|------|
|
||
| `key` | any | 要查找的键。 |
|
||
| `default` | any | 键不存在时返回的默认值,默认为 `None`。 |
|
||
|
||
#### 实现逻辑:
|
||
|
||
- 调用 `self.has_key(key)` 判断是否存在。
|
||
- 存在则返回 `self[key]`(触发 `__getitem__` 并更新时间戳)。
|
||
- 否则返回 `default`。
|
||
|
||
> ✅ 推荐使用方式:比直接捕获异常更安全。
|
||
|
||
---
|
||
|
||
### `__delitem__(key)`
|
||
|
||
删除指定键的对象。
|
||
|
||
#### 行为流程:
|
||
|
||
1. 调用父类 `__delitem__` 删除主缓存中的对象。
|
||
2. 若成功,从 `self.size` 中减去该对象的大小。
|
||
3. 从 `self._shadow` 中删除对应的元数据。
|
||
|
||
#### 异常处理:
|
||
|
||
- 删除失败时重新抛出异常。
|
||
|
||
---
|
||
|
||
## 使用示例
|
||
|
||
```python
|
||
import time
|
||
|
||
class MyData:
|
||
def __init__(self, data):
|
||
self.data = data
|
||
|
||
def get_size(self):
|
||
return len(self.data)
|
||
|
||
# 创建缓存实例,最大容量为 1MB
|
||
cache = ObjectCache(maxsize=1024*1024)
|
||
|
||
# 添加对象
|
||
obj = MyData("Hello World" * 100)
|
||
cache['key1'] = obj
|
||
|
||
# 获取对象
|
||
data = cache.get('key1')
|
||
print(data)
|
||
|
||
# 删除对象
|
||
del cache['key1']
|
||
```
|
||
|
||
---
|
||
|
||
## 已知问题与改进建议
|
||
|
||
### ❌ Bug 修复建议
|
||
|
||
在 `__setitem__` 方法中:
|
||
|
||
```python
|
||
for i in xrange(len(tmp)//2) :
|
||
del self[tmp[i][i]]
|
||
```
|
||
|
||
应改为:
|
||
|
||
```python
|
||
for _, key in tmp[:len(tmp)//2]:
|
||
del self[key]
|
||
```
|
||
|
||
或者修正索引错误:
|
||
|
||
```python
|
||
for i in range(len(tmp)//2):
|
||
del self[tmp[i][1]] # 取出 key
|
||
```
|
||
|
||
此外,Python 2 风格的 `xrange` 和 `.iteritems()` 建议升级为 Python 3 兼容语法。
|
||
|
||
---
|
||
|
||
### ⚙️ 性能优化建议
|
||
|
||
- 当前淘汰策略每次都要排序全部元素,复杂度为 O(n log n),效率较低。
|
||
- 可替换为优先队列或双向链表实现真正的 LRU 缓存,提升性能。
|
||
- `_shadow` 结构可以考虑合并进主字典,减少维护成本。
|
||
|
||
---
|
||
|
||
### 💡 扩展功能建议
|
||
|
||
- 提供 `clear()` 方法重写以同步清理 `_shadow` 和 `size`。
|
||
- 添加 `__contains__`、`keys()` 等方法的行为说明。
|
||
- 增加线程安全锁(多线程环境下使用时)。
|
||
|
||
---
|
||
|
||
## 依赖要求
|
||
|
||
- Python 2.7 或兼容版本(当前代码为 Python 2 风格)
|
||
- 必须导入模块:
|
||
```python
|
||
import time
|
||
```
|
||
否则运行时报错。
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
`ObjectCache` 是一个轻量级的对象缓存工具,适合对内存敏感的应用场景。尽管存在一些实现缺陷,但其设计思路清晰,易于理解和扩展。建议在生产环境中结合更成熟的缓存库(如 `functools.lru_cache` 或第三方库 `cachetools`)使用,或在此基础上修复 bug 后封装成稳定组件。
|
||
``` |