mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-09-28 16:20:56 +08:00
10 KiB
10 KiB
权限检查系统优化方案
项目概述
优化 zhenxun_bot 的权限检查系统,将每条消息的数据库/缓存查询次数从 6-10次 降低到 1-2次。
当前问题分析
现有查询流程
每条消息进入时,权限检查系统执行以下查询:
| 阶段 | 查询内容 | 次数 |
|---|---|---|
_load_context |
PluginInfo, UserConsole, GroupConsole | 3次 |
auth_ban |
BanConsole | 1-2次 |
auth_bot |
BotConsole | 1次 |
auth_admin |
LevelUser (全局+群组) | 1-2次 |
auth_limit |
PluginLimit (如果不在内存) | 0-1次 |
总计:6-10次查询
问题根源
- 数据分散在多个表:
user_console,group_console,ban_console,bot_console,level_user,plugin_info - 每个检查模块独立查询,缺乏数据共享
- 即使有 Redis 缓存,也需要多次网络往返
优化方案:预聚合权限快照 (Permission Snapshot)
核心思想
用一个 Hash 结构存储权限检查所需的所有数据,消息到达时只需 1-2 次查询。
数据结构设计
1. 权限快照 (AuthSnapshot)
缓存键格式: AUTH_SNAPSHOT:{user_id}:{group_id}:{bot_id}
Hash 结构:
{
# === 用户信息 ===
"user_gold": 100, # 用户金币
"user_banned": 0, # 0=未ban, -1=永久ban, >0=ban结束时间戳
"user_ban_duration": 0, # ban时长(用于计算剩余时间)
# === 用户权限等级 ===
"user_level_global": 0, # 全局权限等级
"user_level_group": 0, # 群组权限等级
# === 群组信息 ===
"group_status": 1, # 群组状态 (1=开启, 0=休眠)
"group_level": 5, # 群组等级
"group_is_super": 0, # 是否超级群组
"group_block_plugins": "", # 禁用插件列表 "<plugin1,<plugin2,"
"group_superuser_block_plugins": "", # 超级用户禁用插件列表
"group_banned": 0, # 群组是否被ban
# === Bot信息 ===
"bot_status": 1, # Bot状态
"bot_block_plugins": "", # Bot禁用插件列表
# === 元数据 ===
"version": 1, # 快照版本(用于失效判断)
"created_at": 1703859600 # 创建时间戳
}
2. 插件信息缓存 (PluginSnapshot)
插件是全局的,变化较少,可以使用本地内存缓存 + Redis 双层缓存:
缓存键格式: PLUGIN_SNAPSHOT:{module}
结构:
{
"status": true, # 全局开关状态
"block_type": null, # 禁用类型 (PRIVATE/GROUP/ALL/null)
"admin_level": 0, # 调用所需权限等级
"cost_gold": 0, # 调用所需金币
"level": 5, # 所需群权限等级
"limit_superuser": false, # 是否限制超级用户
"plugin_type": "NORMAL", # 插件类型
"ignore_prompt": false # 是否忽略阻断提示
}
工作流程
消息到达
│
▼
┌──────────────────────────────────────────────────────┐
│ 1. 第一次查询:获取权限快照 │
│ AUTH_SNAPSHOT:{user_id}:{group_id}:{bot_id} │
│ │
│ - 如果存在且未过期 → 直接使用 │
│ - 如果不存在 → 触发快照构建(异步) │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 2. 第二次查询:获取插件信息 │
│ PLUGIN_SNAPSHOT:{module} │
│ │
│ - 优先从本地内存缓存获取 │
│ - 未命中时从 Redis 获取 │
│ - 仍未命中时从 DB 加载并缓存 │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 3. 执行权限检查(纯内存计算,无 I/O) │
│ │
│ - ban 检查 │
│ - bot 状态检查 │
│ - 插件状态检查 │
│ - 群组状态检查 │
│ - 权限等级检查 │
│ - 金币检查 │
└──────────────────────────────────────────────────────┘
│
▼
权限检查完成
缓存失效策略
主动失效(事件驱动)
| 事件 | 失效范围 |
|---|---|
| 用户金币变化 | AUTH_SNAPSHOT:{user_id}:*:* |
| 用户被 ban/unban | AUTH_SNAPSHOT:{user_id}:*:* |
| 群组设置变更 | AUTH_SNAPSHOT:*:{group_id}:* |
| Bot 配置变更 | AUTH_SNAPSHOT:*:*:{bot_id} |
| 插件配置变更 | PLUGIN_SNAPSHOT:{module} + 本地内存缓存 |
| 用户权限变更 | AUTH_SNAPSHOT:{user_id}:{group_id}:* |
被动失效(TTL)
- 权限快照 TTL:60秒(权衡实时性和性能)
- 插件快照 TTL:300秒(插件配置变化较少)
- 本地内存缓存 TTL:30秒
实现计划
Phase 1: 基础设施 ✅ [已完成]
- 新增
CacheType.AUTH_SNAPSHOT和CacheType.PLUGIN_SNAPSHOT - 创建
AuthSnapshotPydantic 模型 - 创建
PluginSnapshotPydantic 模型 - 实现快照构建器
SnapshotBuilder
Phase 2: 快照服务 ✅ [已完成]
-
创建
AuthSnapshotService类get_snapshot(user_id, group_id, bot_id)- 获取权限快照build_snapshot(user_id, group_id, bot_id)- 构建权限快照invalidate_user(user_id)- 失效用户相关快照invalidate_group(group_id)- 失效群组相关快照invalidate_bot(bot_id)- 失效Bot相关快照
-
创建
PluginSnapshotService类get_plugin(module)- 获取插件信息(本地缓存优先)invalidate_plugin(module)- 失效插件缓存warmup()- 预热所有插件缓存
Phase 3: 权限检查器重构 ✅ [已完成]
- 创建新的
OptimizedAuthChecker类 - 基于快照数据的权限检查逻辑
- 无 I/O 的纯内存计算
- 保持与现有系统的兼容性
Phase 4: 缓存失效集成 ⏳ [可选优化]
注:当前实现使用 TTL 自动过期机制,以下为可选的主动失效优化
- 在
UserConsole的写操作中添加失效逻辑 - 在
GroupConsole的写操作中添加失效逻辑 - 在
BanConsole的写操作中添加失效逻辑 - 在
BotConsole的写操作中添加失效逻辑 - 在
LevelUser的写操作中添加失效逻辑 - 在
PluginInfo的写操作中添加失效逻辑
Phase 5: 测试与验证 ⏳ [待测试]
- 单元测试
- 性能对比测试
- 边界情况测试
文件结构
zhenxun/
├── services/
│ └── auth_snapshot/
│ ├── __init__.py
│ ├── models.py # AuthSnapshot, PluginSnapshot 模型
│ ├── builder.py # 快照构建器
│ ├── service.py # 快照服务
│ └── checker.py # 优化后的权限检查器
└── builtin_plugins/
└── hooks/
└── auth_checker_v2.py # 新版权限检查入口
性能预期
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 查询次数 | 6-10次 | 1-2次 | 80%↓ |
| 平均延迟 | ~50ms | ~10ms | 80%↓ |
| Redis 连接压力 | 高 | 低 | 显著降低 |
风险与缓解
| 风险 | 缓解措施 |
|---|---|
| 快照数据过期 | 合理的 TTL + 主动失效机制 |
| 快照构建延迟 | 异步构建 + 首次访问降级到旧流程 |
| 内存占用增加 | 监控内存使用 + 合理的缓存清理 |
| 数据一致性 | 写操作后立即失效缓存 |
使用方式
方式一:替换原有权限检查器(推荐)
修改 zhenxun/builtin_plugins/hooks/__init__.py,将 auth_checker 替换为 auth_checker_v2:
# 原来的导入
# from . import auth_checker
# 替换为
from . import auth_checker_v2
方式二:并行测试
同时加载两个版本,通过日志对比性能:
from . import auth_checker # 原版本
from . import auth_checker_v2 # 优化版本(会覆盖原版本的 run_preprocessor)
API 使用示例
from zhenxun.services.auth_snapshot import (
AuthSnapshotService,
PluginSnapshotService,
AuthSnapshot,
PluginSnapshot,
)
# 获取权限快照
snapshot = await AuthSnapshotService.get_snapshot(
user_id="123456",
group_id="789012",
bot_id="bot_001"
)
# 检查用户是否被ban
if snapshot.is_user_banned():
print(f"用户被ban,剩余时间: {snapshot.get_user_ban_remaining()}秒")
# 获取插件快照
plugin = await PluginSnapshotService.get_plugin("example_plugin")
if plugin and plugin.cost_gold > 0:
print(f"此插件需要 {plugin.cost_gold} 金币")
# 手动失效缓存(数据更新时调用)
await AuthSnapshotService.invalidate_user("123456")
await PluginSnapshotService.invalidate_plugin("example_plugin")
进度追踪
- 开始日期:2025-12-29
- 当前阶段:核心功能已完成
- 状态:✅ 基础功能完成,待测试验证