基于用户活跃时段通过FCM每日推送高赞帖的高效架构问询
方案评估与优化建议
1. 你提出的方案是否高效?
整体思路可行,但存在几个细节需要优化,避免效率问题或逻辑漏洞:
- 2小时粒度的cron任务:如果
active_times表建有基于活跃时段起始/结束时间的联合索引,查询当前窗口内的用户会很高效;但如果用户的活跃时段跨多个2小时窗口(比如13:00-15:00),会被多个cron任务命中,此时Redis的校验逻辑必须可靠,否则会出现重复推送。 - 12小时的Redis过期时间:不符合「每日推送一次」的需求——若用户活跃时段在当天的两个12小时窗口内(比如上午10点和晚上10点),Redis过期后会导致同一天内重复推送,应调整为24小时或按天维度维护过期规则。
- 批量处理优化:若cron任务查询到大量用户,需批量调用FCM接口,避免单条请求循环调用拖慢任务执行效率。
2. Redis是否适用于此类场景?
非常适用,核心原因如下:
- Redis内存读写速度极快,做「是否已推送」的高频校验,相比每次查询Postgres能大幅降低数据库压力。
- 内置的过期机制可自动清理旧推送记录,无需手动维护数据生命周期。
- 支持多种数据结构:哈希表可存储用户推送时间等额外信息;集合(Set)能更高效判断用户是否已在当天被推送;字符串(String)可通过
user:{user_id}:pushed:{date}的键名直接存储推送状态,可按需选择。
3. 更优实现方案建议
针对你的需求,可从以下维度优化:
(1)精准控制每日推送次数
将Redis过期策略调整为按天维度:
- 用集合类型存储每日已推送用户,键名格式为
pushed_users:20240520(日期为当天),用户ID作为集合元素。 - 每日0点自动创建新集合,旧集合设置24小时过期或直接删除,避免占用内存。
- 推送前先判断用户是否在当天集合中,不在则执行推送并将用户ID加入集合。
(2)优化cron任务与数据库查询
- 给
active_times表添加联合索引(active_start, active_end),确保查询当前窗口活跃用户的SQL能快速过滤数据,示例SQL:SELECT user_id FROM active_times WHERE active_start <= CURRENT_TIMESTAMP AND active_end >= CURRENT_TIMESTAMP - 新增
push_logs表到Postgres,持久化推送记录(存储user_id、push_date、post_id等),作为Redis缓存失效后的兜底校验,避免极端情况下的重复推送。
(3)降低Postgres依赖,提升推送效率
- 预缓存高赞帖子:每小时执行一次任务,从Postgres查询Top N高赞帖子,缓存到Redis的列表或哈希表(比如
top_posts:daily),cron任务直接从Redis取帖子,减少对Postgres的查询压力。 - 批量获取FCM Token:将用户的FCM Token存储在Redis(或Postgres带索引),查询用户后批量获取Token,再调用FCM的批量推送接口,减少HTTP请求次数。
(4)避免多实例任务冲突
若Gin服务为多实例部署,需给cron任务加分布式锁:
- 用Redis的
SETNX命令实现锁,比如SET lock:push_task:20240520_10-12 1 EX 3600 NX,确保同一时间只有一个实例处理当前2小时窗口的推送任务,避免重复处理用户。
内容的提问来源于stack exchange,提问作者Matthew Trent
相关产品推荐
相关产品推荐

