列表页缓存与版本戳:发帖后立即生效的缓存设计

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP管理员 黑卡会员
发布于 2026-09-18 23:39 ·12 浏览 ·0 回复

列表页缓存想做到"发帖后立刻可见",正解不是发帖后去挨个删缓存,而是给缓存键挂一个版本戳:发帖、回帖、置顶、删帖时只把对应作用域的版本号 +1,新请求自然算出一个全新的键,旧缓存没人再读、到点自动回收。删缓存是 O(n) 的扫荡,改版本戳是一次 O(1) 的自增。

为什么"发帖后删缓存"这条路走不通

结论:列表页缓存从来不是"一份缓存",而是一大片,靠主动删除永远删不干净。

一个帖子发出去,会同时影响首页列表、所属版块列表、版块内子分类筛选页、按分类排序页、每一页分页——十几二十个键起步。你既不可能精确枚举出该删哪些键,也不可能保证不漏。

再往下想更麻烦:删漏一个,用户看到"我刚发的帖子不见了";删多了,等于每次发帖都把整站列表缓存清零,缓存命中率直接崩盘。加上多键遍历本身有开销,还容易误删到别的模块的键,这条路在工程上基本是负收益。

版本戳具体怎么设计

结论:缓存键 = 作用域 + 版本号 + 参数,事件发生时只动版本号。

设计三句话就能说清:

  1. 一个作用域一个版本号。全局列表一个(`globalVer`),每个版块一个(`boardVer`)。
  2. 键的拼法固定:`list:v{globalVer}:home:p1`、`list:b{boardId}:v{boardVer}:cat3:p2`,分页、排序、分类筛选全部进键。
  3. 触发点只管自增:发帖/回帖/置顶/加精/关闭/删除 → 递增对应版本号,自增是 O(1) 的整数操作。

新帖发布 → 版块版本号 7 变 8 → 原来 `...:v7:...` 的所有键瞬间全部失效,请求直接算 `v8`。

版本号该存在哪里

结论:版本号不能只放缓存里,必须有一个持久、可原子自增的落脚点。

优先级从高到低:

  • 数据库一行(最稳):一张 `cache_versions` 表,`scope` 做唯一键,`UPDATE ... SET ver = ver + 1`。事务内自增,重启不丢,多进程并发也安全。
  • 文件:`flock` 写一个纯数字,够用但要处理权限和并发写。
  • 只放缓存:最危险。缓存一被清,版本号回退,旧键"复活",用户会读到过期列表。所以版本号必须有比缓存更持久的底座。

读取侧可以再加一层进程内缓存(比如同一请求周期内只查一次库),避免每个分页请求都去打数据库。

和 Clara BBS 这类轻量论坛的契合点

结论:版本戳的设计哲学和 Clara BBS「保存即生效、无需清缓存」的路子是一脉相承的。

Clara BBS 是无框架轻量级 PHP 论坛(PHP 7.4-8.5 + MySQL 5.7+,不需要 Composer、不需要命令行、没有编译缓存),后台改配置保存即生效,插件放进 `content/plugins` 也是保存即生效、无需编译——本质都是"不要让用户去手工清理,让旧状态自然失效"。

如果你要给这类系统的列表页加缓存,可以直接复用它的公共设施:`Cache::remember` 提供缓存能力,`Cron::register` 可以跑懒触发的清理任务。手工清理仍然保留了一个出口,在后台「系统工具 → 缓存清理」。另外像 GEO 问答工厂插件那样"发布问答后自动清缓存进问答池",就是按需失效的典型场景。

这几个坑一定要避开

结论:版本戳写错的地方,九成出在键的设计和 TTL 上。

  • 别用时间戳当版本号:同一秒内两个发帖会撞号,缓存不失效。用自增整数。
  • TTL 必须设:版本戳只负责"逻辑失效",旧键的物理空间还得靠 TTL 回收,建议 5–15 分钟,千万别设永久。
  • 分页、排序、筛选参数必须进键:少一个参数,翻到第 3 页给你第 1 页的内容。
  • 用户私有状态别进公共列表缓存:像"我是否已购买此帖""我是否点过赞"这类,要么单独走一份个人缓存,要么在渲染时独立取,否则会把 A 的状态穿给 B。
  • 并发递增是安全的:两个请求同时发帖,版本号 +2 无非多失效一次,不会读到脏数据。

一句话收尾:列表页缓存的正确姿势是"键里带版本、事件里加一、TTL 兜底",缓存只负责加速,版本戳负责正确性——两者分开,发帖后立即生效就是自然结果,而不是靠人去清。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-498.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~