引言
最近在打磨我的轻量级论坛系统(FlintHub),遇到一个很经典的问题:页面加载速度。我的系统是纯 PHP + SQLite,还自研了分片引擎(SplitDB),但页面查询次数一直降不下来,首页 28 次查询,虽然不算多,但总觉得还能再优化。
后来跟朋友聊天,随口说了一句:“把缓存放用户浏览器里,页面加载是不是更快?”
结果聊着聊着,居然聊出了一个双层缓存架构。今天把这个思路分享出来,看看大家怎么看。
一、问题:缓存放哪?
传统的论坛系统,缓存一般只有一层:
- 服务器缓存:数据存服务器(文件 / Redis / Memcached),所有用户共享,首次访问快,二次访问更快。
但服务器缓存有个天然瓶颈:每次页面渲染,都要从服务器读一次缓存。虽然比查数据库快,但还是要走网络,还是有延迟。
那能不能让用户浏览器也存一份缓存?
二、思路:双层缓存,各司其职
第一层:用户浏览器缓存(localStorage)
把变化慢的数据(版块列表、插件列表、热门标签、友情链接)存在用户浏览器里。用户二次访问时,直接从本地读取,零网络请求,毫秒级加载。
第二层:服务器缓存(托底)
如果用户浏览器没有缓存(首次访问 / 换设备 / 清缓存),就从服务器缓存读。服务器缓存保证数据一致性和首次访问兜底。
三、技术实现
1. 哪些数据适合放用户浏览器?
| 数据 | 变化频率 | 全站展示 | 适合放 localStorage? |
|---|---|---|---|
| 版块列表 | 极低 | ✅ | ✅ 非常适合 |
| 插件列表 | 低 | ✅ | ✅ 非常适合 |
| 热门标签 | 中 | ✅ | ✅ 适合(TTL 短一点) |
| 友情链接 | 极低 | ✅ | ✅ 非常适合 |
2. 两层缓存的职责
| 层级 | 数据来源 | 速度 | 职责 |
|---|---|---|---|
| 用户浏览器缓存 | localStorage | 毫秒级(零网络) | 加速用户二次访问 |
| 服务器缓存 | 文件 / SQLite | 快(不走数据库) | 保证跨用户一致 + 首次访问兜底 |
| 数据库 | SplitDB 分片 | 正常 | 数据最终来源 |
3. 数据读取流程
用户访问页面 ↓ ① 检查用户浏览器缓存(localStorage) ├─ 有缓存且未过期 → 直接从浏览器读(零网络请求,毫秒级) └─ 没缓存或已过期 → 走服务器 ↓ ② 检查服务器缓存 ├─ 有缓存 → 返回数据(不走数据库) └─ 没缓存 → 查数据库 → 生成缓存 → 返回数据
四、效果预估
| 场景 | 只有服务器缓存 | 双层缓存 |
|---|---|---|
| 首次访问 | 查数据库 → 生成缓存 | 查数据库 → 生成缓存(一样) |
| 二次访问(同一浏览器) | 从服务器缓存读(走网络) | 从 localStorage 读(零网络) |
| 二次访问(不同浏览器) | 从服务器缓存读(走网络) | 从服务器缓存读(一样) |
| 页面加载 | 快(但要走网络) | 极快(本地读取) |
五、这个方案的优点
- 零依赖:纯 PHP + JS 实现,不需要 Redis / Memcached,符合极简哲学
- 数据主权:数据留在用户浏览器,用户隐私性更强
- 页面加载更快:二次访问数据从本地读取,不走网络
- 减少服务器压力:服务器不用每次给用户返回同一份数据
- 与 SplitDB 契合:服务器缓存走文件 / SQLite,与分片架构天然兼容
六、这个方案的挑战
- 数据一致性:用户浏览器缓存可能和服务器不一致(比如新增了版块,用户浏览器还是旧的)
- 对策:TTL 过期时间 + 内容变更时清空缓存
- localStorage 容量:只有 5MB,但这四类数据很小,完全够用
- 用户换设备:换浏览器/设备后,缓存就没了,需要重新从服务器拉取
- 对策:服务器缓存托底,首次访问快速返回
七、讨论
这个“双层缓存架构”的思路,大家怎么看?
- 你们觉得 localStorage 缓存低频数据,可行吗?
- 数据一致性问题,有更好的解决方案吗?
- 这个架构适合其他论坛系统吗?
欢迎在评论区讨论,一起交流学习。
(完)