[开发]SoSite 数据库查询优化计划

👑Lv.11 元老 🌞 骨干会员
2026-10-23 23:57:30

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:7月5日复制的资料/数据库查询优化计划.md。

SoSite 数据库查询优化计划

目标:将典型页面的 DB 查询次数从 16~18 次降到 6~8 次

原则:不改业务逻辑、不影响数据实时性、改动量小


当前每次请求的固定开销(用户已登录)

查询次数用途
Session 读1MySQL Session
SELECT * FROM users1查当前用户
SELECT theme FROM users1查主题设置
SELECT COUNT FROM messages1查未读消息数
SELECT + INSERT online_users2在线状态
Session 写1请求结束写入
固定小计7

优化方案

🏆 第一刀:用户数据存 Session(省 2 次查询)

改动文件:app/Helpers/Auth.php、app/Controllers/AuthController.php

当前:每次请求都 SELECT * FROM users WHERE id = :id,即使同一个人 1 秒前刚查过。

改后:登录成功时,把用户信息写进 $_SESSION:

// AuthController::login() 登录成功后
$_SESSION['user_id'] = (int)$user['id'];
$_SESSION['user_data'] = $user;  // 新增:用户信息直接存 Session

// Auth::getCurrentUser()
// 优先从 Session 读,不再每次都查 DB
if (isset($_SESSION['user_data'])) {
    return $_SESSION['user_data'];
}
// Session 中没有才查 DB(第一次或缓存被清除)

```php

// 当用户修改资料时需要刷新 Session

Auth::clearUserCache() 改为同时清除 $_SESSION['user_data']

```

效果:每次请求省 1 次 SELECT * FROM users


🏆 第二刀:主题设置并入用户数据(省 1 次查询)

改动文件:app/Controllers/ThemeController.php、app/Helpers/Theme.php

当前:Theme::getCurrent() 对已登录用户执行 SELECT theme FROM users。

改后:$currentUser['theme'] 已经在用户数据里了(SELECT * FROM users 本来就包含 theme 字段),直接从用户数据读,不再单独查。

// Theme::getCurrent() 中
if (\app\Helpers\Auth::isLoggedIn()) {
    $user = \app\Helpers\Auth::getCurrentUser();  // 已从 Session 读取,0 次查询
    if ($user && !empty($user['theme'])) return $user['theme'];
}

效果:省 1 次 SELECT theme FROM users


🏆 第三刀:未读消息数不进布局(省 1 次查询)

改动文件:app/Core/Controller.php

当前:每个页面加载都 SELECT COUNT(*) FROM messages WHERE receiver_id = :uid AND is_read = 0,仅为了在导航栏显示一个红点。

改后:布局中去掉未读消息数的自动查询,改为:

方案 A(推荐):用户点开「消息」时才查

方案 B:在 Session 中存一个每分钟过期的时间戳,1 分钟内不重复查

// Controller::view() 中去掉这一行
'unreadCount' => $isLoggedIn ? Settings::getUnreadMessageCount() : 0,

// 改为固定值,或只对有消息的用户显示
'unreadCount' => 0,

效果:省 1 次 SELECT COUNT(*),代价是导航栏不显示未读红点(或者在消息页单独查)


🏆 第四刀:在线状态去掉 SELECT 检查(省 1 次查询)

改动文件:app/Helpers/Settings.php

当前:每次请求先 SELECT last_activity 判断是否 60 秒内已更新,没更新才写入。

改后:去掉 SELECT 预检,改成用 Session 记时间,60 秒内不执行任何在线状态操作:

public static function updateOnlineStatus(): void
{
    if (!Auth::isLoggedIn()) return;

    // 用 Session 时间戳判断,不查 DB
    $lastUpdate = $_SESSION['_online_last_update'] ?? 0;
    if (time() - $lastUpdate < 60) return;
    $_SESSION['_online_last_update'] = time();

    $now = date('Y-m-d H:i:s');
    $db = \app\Core\Database::getInstance();
    $db->query(
        "INSERT INTO online_users (user_id, last_activity) VALUES (:uid, :now)
         ON DUPLICATE KEY UPDATE last_activity = :now2",
        [':uid' => $_SESSION['user_id'], ':now' => $now, ':now2' => $now]
    );
}

效果:省 1 次 SELECT,每 60 秒才写 1 次 DB


🏆 第五刀:搜索页去掉重复查询(省 4 次查询)

改动文件:app/Controllers/SearchController.php

当前:搜索时 Search::search() 被调用了两次——一次拿计数(perPage=1),一次拿数据(perPage=10)。每次内部都是 4 条 SQL(帖子 COUNT + SELECT + 博客 COUNT + SELECT)。

改后:一次调用同时返回计数和数据:

// 一次查询同时拿到计数和当前页数据
$searchResults = Search::search($keyword, $type === 'all' ? 'all' : $searchType, $page, $perPage, $catFilter);
$threadTotal = $searchResults['thread_count'] ?? 0;
$blogTotal = $searchResults['blog_count'] ?? 0;
$threadResults = $searchResults['threads'] ?? [];
$blogResults = $searchResults['blogs'] ?? [];

效果:搜索时有结果时省 4 次(从 8 次降到 4 次)


效果汇总

优化省查询改动量风险
用户数据存 Session-1~10 行低(clearUserCache 已存在)
主题从用户数据读-1~3 行低(theme 已在 users 表中)
未读消息不从布局查-1~1 行低(仅导航红点消失)
在线状态去 SELECT-1~5 行低(Session 同样可靠)
搜索去重-4~5 行低(逻辑不变)
合计-8~25 行

改完后各页面的预期查询数:

页面当前优化后
首页(已登录)~15~7
论坛(已登录)~16~8
搜索有结果~18~10
搜索无关键词~7~3
主题设置(已登录)~7~4
主题设置(未登录)~2~2


不改的部分

查询原因
Session 读/写MySQL Session 不可避免
帖子/博客内容查询业务必须
权限检查安全必须
插件钩子插件系统必须


实施顺序

第一刀(用户存 Session)  →  效果最明显,改完立即生效
第二刀(主题合并)        →  顺势就改了
第三刀(未读消息)        →  看你能不能接受导航栏不显示红点
第四刀(在线状态)        →  独立改动,不影响其他
第五刀(搜索去重)        →  搜索页专用

要不要开始做?可以先做前两刀,十分钟搞定,效果立竿见影。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 瀏覽 4 次 | 回覆 0 次

全部回覆 (0)

暫無回覆

請 登入