mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-21 12:10:13 +08:00
250 lines
7.7 KiB
Markdown
250 lines
7.7 KiB
Markdown
# 权限检查系统 - 缓存优化指南
|
||||
|
|
|
|||
|
|
## DataAccess 缓存机制说明
|
|||
|
|
|
|||
|
|
### 已实现的缓存功能
|
|||
|
|
|
|||
|
|
`DataAccess` 类已经实现了完整的 Redis 缓存机制,包括:
|
|||
|
|
|
|||
|
|
1. **自动缓存查询结果**
|
|||
|
|
```python
|
|||
|
|
# 第一次查询:从数据库获取并缓存
|
|||
|
|
plugin = await plugin_dao.safe_get_or_none(module="example")
|
|||
|
|
|
|||
|
|
# 后续查询:直接从缓存获取(如果缓存未过期)
|
|||
|
|
plugin = await plugin_dao.safe_get_or_none(module="example")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **空结果缓存(5分钟TTL)**
|
|||
|
|
```python
|
|||
|
|
# 查询不存在的记录时,会缓存空结果标记
|
|||
|
|
# 避免频繁查询数据库确认记录不存在
|
|||
|
|
user = await user_dao.safe_get_or_none(user_id="nonexistent")
|
|||
|
|
# 5分钟内再次查询相同记录,直接从缓存返回 None
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **缓存统计**
|
|||
|
|
```python
|
|||
|
|
from zhenxun.services.data_access import DataAccess
|
|||
|
|
|
|||
|
|
# 获取缓存统计信息
|
|||
|
|
stats = DataAccess.get_cache_stats()
|
|||
|
|
# 返回格式:
|
|||
|
|
# [
|
|||
|
|
# {
|
|||
|
|
# "cache_type": "GLOBAL_ALL_PLUGINS",
|
|||
|
|
# "hits": 1000, # 缓存命中次数
|
|||
|
|
# "null_hits": 50, # 空结果缓存命中次数
|
|||
|
|
# "misses": 200, # 缓存未命中次数
|
|||
|
|
# "hit_rate": "84.00%" # 缓存命中率
|
|||
|
|
# },
|
|||
|
|
# ...
|
|||
|
|
# ]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. **自动缓存失效**
|
|||
|
|
- 数据创建、更新、删除时自动清除相关缓存
|
|||
|
|
- 支持手动清除:`await dao.clear_cache(**kwargs)`
|
|||
|
|
|
|||
|
|
## 权限检查中的缓存优化
|
|||
|
|
|
|||
|
|
### 当前缓存使用情况
|
|||
|
|
|
|||
|
|
在权限检查系统中,以下数据访问已经自动使用缓存:
|
|||
|
|
|
|||
|
|
1. **PluginInfo(插件信息)**
|
|||
|
|
```python
|
|||
|
|
plugin_dao = DataAccess(PluginInfo)
|
|||
|
|
plugin = await plugin_dao.safe_get_or_none(module=module)
|
|||
|
|
# ✅ 自动缓存,基于 module 字段
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **UserConsole(用户信息)**
|
|||
|
|
```python
|
|||
|
|
user_dao = DataAccess(UserConsole)
|
|||
|
|
user = await user_dao.get_by_func_or_none(
|
|||
|
|
UserConsole.get_user, False, user_id=user_id
|
|||
|
|
)
|
|||
|
|
# ✅ 自动缓存,基于 user_id 字段
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **GroupConsole(群组信息)**
|
|||
|
|
```python
|
|||
|
|
group_dao = DataAccess(GroupConsole)
|
|||
|
|
group = await group_dao.safe_get_or_none(
|
|||
|
|
group_id=group_id, channel_id__isnull=True
|
|||
|
|
)
|
|||
|
|
# ✅ 自动缓存,基于 group_id 字段
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. **BanConsole(ban记录)**
|
|||
|
|
```python
|
|||
|
|
ban_dao = DataAccess(BanConsole)
|
|||
|
|
ban = await ban_dao.safe_get_or_none(
|
|||
|
|
user_id=user_id, group_id=group_id
|
|||
|
|
)
|
|||
|
|
# ✅ 自动缓存,基于 user_id 和 group_id
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
5. **BotConsole(Bot信息)**
|
|||
|
|
```python
|
|||
|
|
bot_dao = DataAccess(BotConsole)
|
|||
|
|
bot = await bot_dao.safe_get_or_none(bot_id=bot_id)
|
|||
|
|
# ✅ 自动缓存,基于 bot_id 字段
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
6. **LevelUser(用户权限)**
|
|||
|
|
```python
|
|||
|
|
level_dao = DataAccess(LevelUser)
|
|||
|
|
level = await level_dao.safe_get_or_none(
|
|||
|
|
user_id=user_id, group_id=group_id
|
|||
|
|
)
|
|||
|
|
# ✅ 自动缓存,基于 user_id 和 group_id
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 优化建议
|
|||
|
|
|
|||
|
|
#### 1. 并行获取数据,提高缓存命中率
|
|||
|
|
|
|||
|
|
**当前实现**:
|
|||
|
|
```python
|
|||
|
|
# 串行获取,可能错过缓存优势
|
|||
|
|
plugin = await plugin_dao.safe_get_or_none(module=module)
|
|||
|
|
user = await user_dao.get_by_func_or_none(...)
|
|||
|
|
group = await group_dao.safe_get_or_none(...)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优化后**:
|
|||
|
|
```python
|
|||
|
|
# 并行获取,DataAccess 会自动处理缓存
|
|||
|
|
plugin, user, group = await asyncio.gather(
|
|||
|
|
plugin_dao.safe_get_or_none(module=module),
|
|||
|
|
user_dao.get_by_func_or_none(UserConsole.get_user, False, user_id=user_id),
|
|||
|
|
group_dao.safe_get_or_none(group_id=group_id, channel_id__isnull=True)
|
|||
|
|
if group_id else asyncio.sleep(0)
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. 提前获取所有需要的数据
|
|||
|
|
|
|||
|
|
**当前实现**:
|
|||
|
|
```python
|
|||
|
|
# group 数据在 hooks 执行时才获取
|
|||
|
|
# 在 auth 函数中
|
|||
|
|
group = None
|
|||
|
|
if entity.group_id:
|
|||
|
|
group = await group_dao.safe_get_or_none(...)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**优化后**:
|
|||
|
|
```python
|
|||
|
|
# 在获取 plugin 和 user 时,同时获取 group
|
|||
|
|
# 统一数据获取,充分利用缓存
|
|||
|
|
async def get_all_auth_data(module, user_id, group_id):
|
|||
|
|
plugin_dao = DataAccess(PluginInfo)
|
|||
|
|
user_dao = DataAccess(UserConsole)
|
|||
|
|
group_dao = DataAccess(GroupConsole) if group_id else None
|
|||
|
|
|
|||
|
|
tasks = [
|
|||
|
|
plugin_dao.safe_get_or_none(module=module),
|
|||
|
|
user_dao.get_by_func_or_none(UserConsole.get_user, False, user_id=user_id),
|
|||
|
|
]
|
|||
|
|
if group_id and group_dao:
|
|||
|
|
tasks.append(
|
|||
|
|
group_dao.safe_get_or_none(group_id=group_id, channel_id__isnull=True)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
results = await asyncio.gather(*tasks)
|
|||
|
|
return results
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3. 监控缓存效果
|
|||
|
|
|
|||
|
|
**实现方案**:
|
|||
|
|
```python
|
|||
|
|
from zhenxun.services.data_access import DataAccess
|
|||
|
|
|
|||
|
|
async def auth(...):
|
|||
|
|
# 记录开始时的缓存统计
|
|||
|
|
cache_stats_before = DataAccess.get_cache_stats()
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 执行权限检查
|
|||
|
|
...
|
|||
|
|
finally:
|
|||
|
|
# 记录结束时的缓存统计
|
|||
|
|
cache_stats_after = DataAccess.get_cache_stats()
|
|||
|
|
|
|||
|
|
# 分析缓存效果
|
|||
|
|
for before, after in zip(cache_stats_before, cache_stats_after):
|
|||
|
|
cache_type = after["cache_type"]
|
|||
|
|
hits_diff = after["hits"] - before["hits"]
|
|||
|
|
misses_diff = after["misses"] - before["misses"]
|
|||
|
|
total = hits_diff + misses_diff
|
|||
|
|
|
|||
|
|
if total > 0:
|
|||
|
|
hit_rate = (hits_diff / total) * 100
|
|||
|
|
logger.debug(
|
|||
|
|
f"缓存统计 - {cache_type}: "
|
|||
|
|
f"命中率={hit_rate:.1f}%, "
|
|||
|
|
f"命中={hits_diff}, 未命中={misses_diff}",
|
|||
|
|
LOGGER_COMMAND
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 如果缓存命中率过低,记录警告
|
|||
|
|
if hit_rate < 30:
|
|||
|
|
logger.warning(
|
|||
|
|
f"缓存命中率过低: {cache_type} = {hit_rate:.1f}%",
|
|||
|
|
LOGGER_COMMAND
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 4. 调整空结果缓存时间
|
|||
|
|
|
|||
|
|
**根据实际需求调整**:
|
|||
|
|
```python
|
|||
|
|
from zhenxun.services.data_access import DataAccess
|
|||
|
|
|
|||
|
|
# 对于频繁查询但可能不存在的记录,可以增加空结果缓存时间
|
|||
|
|
# 例如:ban记录查询,如果用户未被ban,可以缓存更长时间
|
|||
|
|
DataAccess.set_null_result_ttl(600) # 10分钟
|
|||
|
|
|
|||
|
|
# 对于需要实时性的数据,可以减少缓存时间
|
|||
|
|
# 例如:用户权限查询
|
|||
|
|
DataAccess.set_null_result_ttl(60) # 1分钟
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 缓存性能优化检查清单
|
|||
|
|
|
|||
|
|
- [ ] 所有数据访问都通过 `DataAccess`,而不是直接使用模型类
|
|||
|
|
- [ ] 并行获取多个数据,而不是串行获取
|
|||
|
|
- [ ] 提前获取所有需要的数据,避免在检查过程中重复获取
|
|||
|
|
- [ ] 监控缓存命中率,确保缓存效果良好
|
|||
|
|
- [ ] 根据实际需求调整空结果缓存时间
|
|||
|
|
- [ ] 定期查看缓存统计,优化缓存策略
|
|||
|
|
|
|||
|
|
## 预期缓存效果
|
|||
|
|
|
|||
|
|
### 高并发场景下的缓存命中率预期
|
|||
|
|
|
|||
|
|
- **PluginInfo(插件信息)**:90-95%(插件信息变化不频繁)
|
|||
|
|
- **UserConsole(用户信息)**:80-90%(用户信息相对稳定)
|
|||
|
|
- **GroupConsole(群组信息)**:85-95%(群组信息变化不频繁)
|
|||
|
|
- **BanConsole(ban记录)**:70-85%(ban状态可能变化)
|
|||
|
|
- **BotConsole(Bot信息)**:95-99%(Bot信息很少变化)
|
|||
|
|
- **LevelUser(用户权限)**:75-85%(权限可能变化)
|
|||
|
|
|
|||
|
|
### 性能提升预期
|
|||
|
|
|
|||
|
|
- **缓存命中时**:查询时间从 10-50ms 降低到 1-5ms(减少 80-90%)
|
|||
|
|
- **总体性能**:在高并发场景下,总体执行时间减少 30-50%
|
|||
|
|
- **数据库压力**:减少数据库查询 40-60%
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. **缓存一致性**:DataAccess 在数据更新时会自动清除缓存,确保一致性
|
|||
|
|
2. **缓存失效**:如果需要强制刷新缓存,可以使用 `await dao.clear_cache(**kwargs)`
|
|||
|
|
3. **监控缓存**:定期查看缓存统计,确保缓存效果良好
|
|||
|
|
4. **空结果缓存**:空结果缓存有助于减少对不存在记录的查询,但需要根据实际需求调整TTL
|
|||
|
|
|