活力39282
在线时间13640 小时
阅读权限200
管理员
自由的灵魂
- 积分
- 107434
- 主题
- 5630
- 回帖
- 26661
- 注册时间
- 2003-4-10
- 最后登录
- 2026-10-2
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
1、问题缘起
现象:
用户反馈回帖后页面不自动跳转到新回复,必须手动清空Redis缓存后才能看到新内容,但问题会反复出现。
初步判断:
Redis缓存与数据库状态不一致。数据库已成功写入新回复,但缓存未失效,导致页面仍展示旧的缓存数据。
排查过程:
- 检查发帖/回帖核心文件 source/function/function_post.php。
- 发现 updatethreadcount() 和 updateforumcount() 函数在更新数据库后,没有任何清理Redis缓存的操作。
- 进一步发现 updateattach() 函数在更新附件状态后,同样缺失缓存清理逻辑。
- 确认这是Discuz! X3.5(20250901版本)在Redis缓存失效机制上的一个设计遗漏。
2、解决方案
(1)简要技术分析
采用Cache-Aside Pattern(旁路缓存)的精准失效策略:在数据库写入成功后,立即删除(而非全量清空)该操作对应的Redis键。下次读取数据时,系统会自动从数据库回填最新数据至缓存,以此保证数据一致性,且性能损耗极小。
(2)修改文件列表
| 文件路径 | 操作 | 修改位置 | | /www/wwwroot/www.dianbo.org/source/function/function_post.php | 修改 | updatethreadcount() 函数末尾 | | /www/wwwroot/www.dianbo.org/source/function/function_post.php | 修改 | updateforumcount() 函数末尾 | | /www/wwwroot/www.dianbo.org/source/function/function_post.php | 修改 | updateattach() 函数末尾 |
(3)关键作用代码段全文
修改点一:updatethreadcount() 函数(约第330行)
- function updatethreadcount($tid, $updateattach = 0) {
- // ... (省略原有数据库更新逻辑) ...
- C::t('forum_thread')->update($tid, $data);
- // [缓存修复] 回帖后精准清理该帖子的Redis缓存
- if(function_exists('memory') && memory('check')) {
- memory('rm', 'forum_thread_' . $tid); // 清理帖子主信息缓存
- memory('rm', 'forum_post_' . $tid); // 清理帖子回复内容缓存
- memory('rm', 'forum_threadlist_' . C::t('forum_thread')->fetch_thread($tid)['fid']); // 清理该版块帖子列表缓存
- }
- // [/缓存修复]
- }
复制代码
修改点二:updateforumcount() 函数(约第312行)
- function updateforumcount($fid) {
- // ... (省略原有数据库更新逻辑) ...
- C::t('forum_forum')->update($fid, $setarr);
- // [缓存修复] 版块统计更新后精准清理该版块的Redis缓存
- if(function_exists('memory') && memory('check')) {
- memory('rm', 'forum_forum_' . $fid); // 清理版块信息缓存
- memory('rm', 'forum_forumlist'); // 清理版块列表缓存
- }
- // [/缓存修复]
- }
复制代码
修改点三:updateattach() 函数(约第160行末尾)
- C::t('forum_thread')->update($tid, array('attachment'=>$attachment));
- C::t('forum_post')->update_post('tid:'.$tid, $pid, array('attachment' => $attachment), true);
- // [缓存修复] 附件状态变更后精准清理该帖子的Redis缓存
- if(function_exists('memory') && memory('check')) {
- memory('rm', 'forum_thread_' . $tid); // 帖子主信息缓存(含attachment字段)
- memory('rm', 'forum_post_' . $tid); // 帖子回复内容缓存
- }
- // [/缓存修复]
复制代码
3、最终实现效果
| 场景 | 修改前 | 修改后 | | 普通回帖 | 数据库更新,Redis缓存未失效,页面显示旧数据。 | 数据库更新后立即清理对应缓存,下次读取自动回填。 | | 带附件回帖 | attachment字段变更但缓存未更新,附件状态显示异常。 | 附件状态变更后缓存即时失效,状态同步更新。 | | 版块统计更新 | 版块最后回复/帖子数显示严重滞后。 | 版块缓存精准失效,统计数据实时同步。 | | 性能影响 | 无。 | 每次操作仅删除2-3个Redis键,O(1)操作,耗时在微秒级,可忽略。 | | 是否需要手动清理Redis | 需要,且问题会反复出现。 | 不需要,由系统自动完成精准失效。 |
4、重要技术沉淀(固化摘要)
- Discuz! X3.5 缓存缺陷: 核心函数 updatethreadcount()、updateforumcount()、updateattach() 均存在“写数据库但不清理缓存”的设计遗漏,这是官方版本的一个已知问题。
- Cache-Aside Pattern 实践: 在写操作后,必须主动失效(Invalidation)对应的缓存键,而不能单纯依赖TTL过期或全量清空。
- 精准失效优于全量清理: 使用 memory('rm', 'key') 仅影响单条记录,比执行 flushall 或批量删除键值要安全、高效得多。
- 核心文件定位: function_post.php 是发帖、回帖、附件处理的核心文件,未来任何涉及帖子状态变更的修改,都必须检查并补充相应的缓存失效逻辑。
5、进一步优化的可能性
| 优化方向 | 说明 | 优先级 | | 给Redis缓存加TTL兜底 | 在 config_global.php 中设置 $config['memory']['redis']['ttl'] = 3600,即使失效逻辑有遗漏,缓存也能自动过期,作为最终防线。 | 低(当前修复已足够) | | 增加监控脚本 | 编写一个检测“帖子回复数与缓存内容”是否一致的脚本,发现异常时自动清理对应的缓存键,作为兜底方案。 | 低 | | 检查其他核心表 | 排查 source/class/table/ 目录下,其他表的 update/insert 操作是否存在类似的缓存失效遗漏。 | 中 | | 升级到官方补丁版 | 持续关注Discuz!官方是否发布X3.5的Redis缓存修复补丁,以便后续跟进。 | 中 |
|
|