Nuxt 3 博客计数越刷越少:Redis 增量合并与定时落库的修复记录
个人站浏览量、点赞在高并发下容易丢。把高频写入先放进 Redis 做增量,再由 Node.js 定时批量同步到 MongoDB,接口变快,计数也更稳。

作者
ZHOUYI
前段时间给博客补浏览量,发现数字越刷越少:明明自己点了几次,刷新后又回到很小。起初以为是前端重复请求,后来发现是计数写入在并发下被吞掉。我的站点是 Nuxt 3 + Node.js + MongoDB,文章页既要渲染又要接收点赞/浏览上报,量不大但请求集中,一旦页面里有几个组件同时触发,数据库写入就开始互相挤。
先把问题拆开
- 浏览接口直接更新 MongoDB,高并发时写锁和慢查询让请求排队。
- 前端重试或用户连点,会让同一次行为重复提交。
- 为了实时数字好看,每次都写库,反而把写库拖慢。
我的改法:Redis 增量 + 定时落库
思路很简单:高频请求先写 Redis,只做增量,不立刻碰 MongoDB。浏览量用 INCR,点赞用 SET 记录用户或设备标识,避免重复。Node.js 定时任务每隔几十秒把增量批量合并进 MongoDB。这样接口响应快,数据库压力也变成低频批量写。对个人站来说,展示数字晚几秒并不致命,丢数才致命。
// 伪代码:用 SCAN,不要 KEYS
const postId = req.params.id;
await redis.incr(`post:views:${postId}`);
// Node.js 定时任务
for (const key of await scanByPattern('post:views:*')) {
const delta = await redis.getDel(key);
if (delta > 0) {
await Post.updateOne({ _id: idFromKey(key) }, { $inc: { views: Number(delta) } });
}
}这里我没有追求绝对实时,而是把『接收行为』和『持久化』拆开。Nuxt 页面里展示的数字可以先读 Redis 合并值,也可以直接读 MongoDB 的旧值加当前增量。热度榜、内链推荐这些依赖浏览量的功能,也因此少了很多抖动。
踩坑与兜底
- SCAN 代替 KEYS,避免 Redis 阻塞;具体客户端封装以官方文档为准。
- 取增量时用 getDel 或脚本/事务,防止任务执行期间新增量丢失。
- 落库用 $inc,保持幂等;失败就保留增量下轮重试。
- 点赞去重别只靠前端节流,服务端要有唯一标识。
- 定时任务要加锁,避免多实例重复消费。
我还加了很土的监控:每轮记录待同步键数量、失败键数量和平均延迟。如果失败连续出现,就把增量重新写回 Redis,并打日志。计数系统不怕慢,怕的是悄悄丢。只要能看到差值,就能慢慢把边界情况补齐。
“这类小站计数不需要每次都强一致。把热路径变轻,把冷路径做稳,数字反而更可信。”