You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Web Push Subscription对象数据库存储数量最优方案咨询(MongoDB Atlas有限存储场景)

处理Web推送订阅存储:平衡空间限制与用户体验

嘿,作为刚踏入Web推送领域的开发者,你的这个问题真的很贴合实际——尤其是在MongoDB Atlas免费版只有512MB存储空间的情况下。咱们来拆解一下你的两个方案,再给出更优的平衡思路:

先聊聊两种场景的优缺点

场景1:保留所有设备的订阅记录

  • ✅ 优点:用户在任何已授权的设备/浏览器上都能收到通知,体验完整,不会出现“换了旧设备收不到消息”的困惑;
  • ❌ 缺点:无效订阅(用户弃用设备、更换浏览器、清除缓存导致的订阅失效)会持续占用空间,而且推送时给失效的endpoint发请求也是浪费资源。

场景2:只保留最新的设备/浏览器订阅

  • ✅ 优点:存储空间占用极小,不会积累无效记录;
  • ❌ 缺点:用户切换回旧设备时,完全收不到通知,体验割裂——比如用户在家用PC订阅,出门用手机,后来又换回PC,这时候PC的订阅已经被删了,就收不到推送了。

最优方案:保留所有订阅,但主动清理无效/长期未使用的记录

显然,两种场景都有明显的短板,最合理的做法是结合两者的优势:既保留用户的多设备订阅以保证体验,又通过主动清理机制控制存储空间。具体可以这么做:

1. 实时清理推送失败的无效订阅

当你向某个订阅的endpoint发送推送时,如果收到410 Gone(订阅已失效)或者持续的4xx/5xx错误,立刻从数据库中删除这个订阅对象。这是最直接有效的清理方式,能及时移除已确认失效的记录。

2. 定期清理长期未使用的订阅

给你的Schema新增一个lastUsed字段,记录该订阅最后一次成功接收推送的时间:

const pushNotificationsSchema = mongoose.Schema({
  _id: { type: mongoose.Types.ObjectId, required: true, ref: 'User' },
  subscriptions: [{
    endpoint: { type: String, required: true },
    expirationTime: { type: Date, default: null },
    keys: {
      p256dh: { type: String, required: true },
      auth: { type: String, required: true }
    },
    lastUsed: { type: Date, default: Date.now } // 记录最后使用时间
  }],
  updatedAt: { type: Date, default: Date.now } // 替换原有的time字段,自动维护更新时间
});

然后每周/每月跑一个定时脚本,删除lastUsed超过3个月(或根据你的业务调整时长)的订阅——这类订阅大概率是用户已经弃用的设备,清理后不会影响大多数用户的体验。

3. 优化Schema结构减少冗余

你原来的Schema里subobjects用了[Object]类型,改成明确的嵌套结构(如上面的示例),不仅能让数据更规范,还能避免MongoDB存储不必要的元数据,间接节省空间。另外,把原有的time字段换成自动维护的updatedAt,也能减少手动维护的麻烦。

额外的小建议

  • 如果你的用户量很大,还可以考虑把活跃订阅(最近30天内使用过的)缓存到内存中,推送时优先从缓存读取,减少MongoDB的查询压力,同时也能降低无效查询的概率;
  • 避免存储不必要的字段:比如如果expirationTime大部分时候是null,可以考虑不存这个字段(MongoDB会自动忽略不存在的字段,节省空间)。

这样一来,你既能保证用户在多设备上的推送体验,又能有效控制MongoDB的存储空间,完美适配免费版的限制~

内容的提问来源于stack exchange,提问作者Pratik Agarwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 06:38:15