refactor: align auth checker optimization with DataAccess caching

This commit is contained in:
HibiKier
2025-12-17 15:03:44 +08:00
parent 0939013a89
commit 26150c2924
4 changed files with 1237 additions and 0 deletions
@@ -0,0 +1,249 @@
# 权限检查系统 - 缓存优化指南
## 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