[已修复] update/18 改动说明 — 页面数据查询次数优化(请求级缓存复用与同 id 去重)

👑Lv.11 元老 🌛 见习会员
2026-09-17 19:07:55
版本 V1.0.18version.php,末位数字 18 = 目录号 18)
主题 收敛「同一请求内对 users / settings / notifications / messages / level_config 的重复查询」,降低每页数据查询次数
涉及静态资源 (不含 sw.jsCACHE_VERSION 无需递增;本包不含任何 CSS / JS)
数据库结构变更 (无表结构、无索引、无数据迁移;不改动任何业务数据
部署关键点 覆盖 10 个代码文件 + 根目录 version.php;纯文件覆盖,无配置项、无升级 URL。若站点已存在 protected/level_config_cache.php 且此前改过等级配置,建议删除该文件让其重建(见第六节)

一、变更文件清单

# 文件 变更类型
1 app/Helpers/UserPrefs.php 新增(请求级用户偏好缓存:一次查询取 theme / night_theme_auto / hide_right_sidebar / home_mode / list_excerpt / list_excerpt_len)
2 app/Helpers/Theme.php 修改(getCurrent() / getHomeMode() / hideRightSidebar() 改读 UserPrefs 缓存,删除 3 处 users 单查)
3 app/Controllers/ThemeController.php 修改(readPref() 改读 UserPrefs 缓存,消除逐 key 查询;.settings() 连续 5 次调用共享一份数据)
4 app/Helpers/Settings.php 修改(reconcileWithDb() 复用已加载的全量 KV + 60s 对账节流;新增 getUnreadBadgeCounts() / clearUnreadBadgeCache()getUnreadMessageCount() 改为委托;clearUnreadMessageCache() 同步清合并缓存)
5 app/Helpers/Notification.php 修改(getUnreadCount() 当前登录用户走合并缓存;markAllRead() 后清合并缓存)
6 app/Helpers/Permission.php 修改(groupIdOf() 当前登录用户直接复用 Auth::getCurrentUser()['group_id']
7 app/Helpers/Points.php 修改(新增 loadAllConfigs() / writeConfigCache() / clearConfigCache()getLevelConfig()getAllConfigs() 共用同一份数据源;ensureTable() / saveLevelConfig() / deleteLevelConfig() 写后失效)
8 app/Models/Thread.php 修改(decorate() 新增 $decorateUserCache / $decorateCatCache,同一请求同一 uid/cid 只查一次)
9 app/Models/User.php 修改(getStats()COUNT(*) blogs 按 uid 请求级缓存)
10 plugins/medal/Plugin.php 修改(自动发章评估:当前登录用户复用 Authpoints / created_at
11 version.php 修改(V1.0.13V1.0.18

未触碰:app/SplitDB/、列表页的 WHERE id IN (...) 批量补用户/分类、投票的 UNION ALL 合并、游客前置静态缓存。以上均为既有正确写法,本次刻意保持原样。


二、优化前现状与根因

页脚「数据查询 N 次」= Database::getQueryCount(),仅统计 business.sqlite 业务库的真实数据语句(不含 sessions 库、分片桶、PRAGMA/DDL)。优化前实测(登录态):首页 24 次、论坛 23 次、帖子详情 29 次、主题设置 21 次。根因共 4 条,全部属于「公共骨架重复查询」,而非业务查询本身:

  1. 用户偏好字段被 4 个方法各自查 users 表,且都没有请求级缓存 Theme::getCurrent()theme, night_theme_auto(同一请求 4~5 次)、Theme::getHomeMode()home_modeTheme::hideRightSidebar()hide_right_sidebarThemeController::readPref() 按 key 逐次查(一次 5 条)。而 Auth::getCurrentUser() 本就有请求级缓存,只是这 3 个字段不在其 SELECT 列表内,才各自回源。
  2. 同页两次读 settings 表 Settings::reconcileWithDb() 每请求执行 SELECT "key" FROM settings 做孤儿键对账,而 loadAllFromDb() 已读过全量 key/value,两者未复用。
  3. 导航未读数两条 COUNT 各查一次 Notification::getUnreadCount()(通知)与 Settings::getUnreadMessageCount()(私信)每页各执行一条 COUNT。
  4. 详情页单行装饰被重复调用 Thread::decorate() 内部逐条查 users + categories;帖子详情里同一主题被装饰两次(主题行 + 首帖),users / categories 各查 2 次。 另有 Permission::groupIdOf()、medal 插件评估、Points::getLevelConfig() 对同一用户的重复读取。

三、逐项改动细则

# 项目 做法 每页省
1 用户偏好缓存 新增 UserPrefs::all()(静态缓存 + 一次查询取 6 字段,游客直接返回默认值不查库);Theme 3 处 + ThemeController::readPref() 改读它 3~4(主题设置页 10)
2 settings 双查合一 reconcileWithDb():①已从 DB 全量加载时零成本跳过(缓存侧键集合 = DB 键集合,不存在孤儿键);②其余情况 60s 节流戳(protected/settings_reconcile.stamp),窗口内不再查键 1
3 未读数合并 新增 Settings::getUnreadBadgeCounts():一条 UNION ALL 取通知 + 私信未读数(两表过滤条件均为 is_read = 0,同库同连接);Notification::getUnreadCount()(当前用户)与 getUnreadMessageCount() 均委托它 1
4 当前用户元数据复用 Permission::groupIdOf() 当前登录用户取 Auth 缓存的 group_id(含 <=0 → 1 归一);medal 插件评估取 Authpoints / created_at(非当前用户仍回源) 1~2
5 level_config 缓存 新增 Points::loadAllConfigs():请求级静态 → protected/level_config_cache.php(含 1 小时 TTL 安全网)→ DB;两条读取路径(getLevelConfig / getAllConfigs)共用一份数据源;三个写路径写后立即失效 1
6 详情页去重 Thread::decorate() 的 users / categories 子查询按 uid/cid 请求级复用(行本身仍各自组装,因详情页两次装饰的 $content 不同,按行缓存会串内容);User::getStats()COUNT(*) blogs 按 uid 请求级缓存 1~3

正确性配套(必须一起发布,否则同请求内可能读到旧值)

  • Notification::markAllRead() 更新后调用 Settings::clearUnreadBadgeCache()
  • Settings::clearUnreadMessageCache() 同时清 $unreadBadgeCache(私信已读路径 MessageController 已调用该方法);
  • Points::ensureTable() / saveLevelConfig() / deleteLevelConfig() 写后调用 clearConfigCache()

四、优化效果(CLI 探针跑完整 index.php 管线实测)

4.1 登录态

页面 优化前 优化后 降幅
/ 首页 24 14 −10
/forum 论坛 23 14 −9
/thread/70 帖子详情 29 18 −11
/blog/16 博客详情 24 15 −9
/tags 标签 16 8 −8
/profile 个人主页 17 8 −9
/post/new 发帖 16 8 −8
/search 搜索 15 7 −8
/theme-settings 主题设置 21 7 −14

4.2 游客态

页面 优化前 优化后
/ 11 11(游客走 cookie,本包对游客渲染路径零改动)
/forum 16 11
/thread/70 17 13

五、验证方式与结果

  1. 语法php -l 全部变更文件通过(No syntax errors detected);
  2. 编码:所有文件 UTF-8 无 BOM + LF(新增 UserPrefs.php 与修改后的 Notification.php 行尾已校正为 LF);
  3. 渲染一致性:同批复测页面输出字节数与优化前基线逐字节比对——/ 75324、/forum 81165、/thread/70 164951、/blog/16 47367 完全一致;/tags/post/new/search/theme-settings 与上一轮完全一致;游客 / 70466、/forum 76064、/thread/70 107522 完全一致;
  4. 未读数语义等价:改造前后同一时刻逐值比对 → user 1:通知 2 / 私信 0(独立查询与 UNION ALL 完全一致);user 2:0 / 0(空表边界,messages 表 0 行同样正确);notifications 表 1640 行规模下亦一致;
  5. 缓存内容一致性level_config 文件缓存 11 行 vs DB 逐字段比对一致;settings 缓存 107 键 vs DB 107 键,无孤儿键、无缺失键(故 60s 对账节流不改变任何渲染结果);
  6. SQL 明细核对Theme.php 偏好查询 ×4、Theme.php home_mode / hide_right_sidebar、ThemeController::readPref() ×5、Settings.php 对账、Points.php 等级表、Permission group_id、medal 评估等重复项均已从明细中消失。

已知观察项(非缺陷):/profile 输出字节数在两次跨越时间点的测量间出现 ±1 字节(31030 → 31029),该页为唯一承载用户会话态/在线态动态显示的页面;同批其余页面逐字节一致,且上述第 3~5 项已排除代码原因。/blog/16 仍有 2 条 COUNT(*) FROM blogs WHERE user_id = :uid,经明细核对为两个不同 uid(博主 + 当前登录用户)的正常统计,按「去重仅针对同一请求同一 id」的约定不做合并。


六、部署与回滚

部署:按本目录结构覆盖到站点根目录即可:

app/Helpers/UserPrefs.php          ← 新增文件(必须上传)
app/Helpers/Theme.php
app/Helpers/Settings.php
app/Helpers/Notification.php
app/Helpers/Permission.php
app/Helpers/Points.php
app/Controllers/ThemeController.php
app/Models/Thread.php
app/Models/User.php
plugins/medal/Plugin.php
version.php
  • 无数据库迁移、无配置项、无需访问升级 URL;
  • 站点会在首次请求时自动生成两个运行时缓存文件:protected/level_config_cache.php(等级配置)与 protected/settings_reconcile.stamp(对账节流戳)。二者均非必需,删除后自动重建;
  • 若站点此前已存在 protected/level_config_cache.php 且其后改过等级配置,建议在覆盖后删除该文件一次,确保等级档位与数据库一致(此后由写路径自动失效,无需再手动处理)。

回滚:还原上述 11 个文件到旧版本即可;删除 protected/level_config_cache.phpprotected/settings_reconcile.stamp 以清除新增缓存产物。


七、未纳入本包的观察项(供后续评估)

  1. 博客详情页 SELECT b.*, u.username … FROM blogs WHERE id = ? 存在同参数重复 2 次,可加请求级缓存再省 1 次;
  2. Thread::decorateList()decorate() 可共享同一份 uid/cid 缓存,列表与详情混合渲染的页面可能再省 1 次;
  3. /blog/16 的两条 COUNT(*) blogs(不同 uid)如需合并为一条 IN (uid1, uid2),属业务语义变更,需单独确认后再做。
我喜欢在我的自留地里瞎逛,FlintHub!
| 浏览 0 | 回复 2

全部回复 (1)

🌳Lv.4 中级 🌛 见习会员
2026-09-17 19:21:10
那么这个更新后的文件在哪下载呢?
| 引用 #1 楼

登录

×