Feed 流滚动分页与附近商户搜索

社交内容和附近商户看似是两个功能,背后都涉及“如何从不断变化的数据集合中取一页结果”。Feed 流按时间读取动态,附近搜索按距离读取地理位置;选择合适的索引和分页方式,才能减少重复和漏项。

1. Feed 流的推和拉

关注关系建立后,用户可以查看关注对象发布的动态。推模式在发布时把动态写入粉丝的收件箱,读取快但写放大明显;拉模式在用户打开页面时查询关注对象的新动态,写入简单但读取可能变慢。混合模式可按账号规模或活跃度选择策略,但会增加维护复杂度。

在项目练习中,可以先将动态 ID 写入收件箱有序集合:成员是动态 ID,分数表示发布时间。动态正文和作者信息仍保存在数据库或其他数据存储中,Feed 里只保存索引所需内容。

2. 为什么滚动分页比页码分页合适

页码分页常用 LIMIT offset, size。新动态插入后,前面的行发生移动,用户翻页时可能看到重复或漏掉内容。滚动分页让客户端带上上一页末尾的游标,继续读取游标之后的记录,更适合持续变化的时间线。

如果只用时间戳作为游标,同一毫秒发布的多条动态可能拥有相同分数。简单地记住时间戳再用 offset,遇到同分值记录新增或删除时仍可能重复或漏数据。更稳妥的游标需要包含稳定的次序信息(例如时间加唯一 ID),并在数据库查询条件中实现严格的“在上一条之后”。Redis ZSET 设计也要处理同分值的排序和分页边界。

3. 拉取一页 Feed 的基本步骤

  1. 根据当前用户找到收件箱中的动态索引。
  2. 按时间倒序取一批动态 ID 和游标信息。
  3. 批量查询动态详情,避免每个 ID 单独查询造成 N+1 次访问。
  4. 过滤已删除或不可见的动态。
  5. 返回详情以及下一页游标。

注意:动态详情的数据库顺序可能与 Redis 返回的 ID 顺序不同,需要按原始 ID 顺序组装结果。

4. 附近商户与 GEO

Redis GEO 命令可保存经纬度,并按指定中心和半径查询附近成员。它适用于一定规模下的附近检索;复杂的地理围栏、路线规划或精确空间分析应评估专门的空间数据库和索引。

经纬度顺序、单位、坐标系和边界都要处理正确。查询得到候选商户后,通常还要按距离、营业状态和业务排序规则整理结果。不要把 Redis GEO 结果当成完整的地理业务模型。

5. 实践时的检查点

  • 一个用户发布多条动态时,如何避免向同一收件箱重复写入?
  • 取消关注或删除动态后,收件箱中的旧索引怎样处理?
  • 热门账号的粉丝量很大时,推模式会产生多大写放大?
  • 分页游标怎样处理同分值、多条记录和动态新增?
  • 附近搜索是否需要过滤用户不可见或已下架的商户?

6. 小结

Feed 流的关键是读写路径与稳定游标,附近搜索的关键是地理索引与业务过滤。项目中的滚动分页应使用复合游标或可证明不重复的边界条件,而不是只靠页码或一个时间戳。