当前位置: 首页 > news >正文

Timeline Feed服务

Feed流分类

  • 推荐Feed流

  • 关注Feed流

  • 附近Feed流

Feed流的几种模式

拉模式

介绍:

写:只写发件箱

读:拉所有关注者发件箱 → 合并排序

优点:实现简单,存储空间小

缺点: 读延迟高,读扩散(一次用户请求会放大N倍读请求)。

推模式

写:推送到所有粉丝收件箱

读:直接读收件箱

优点:性能好。(不需要获取关注列表对象的发件箱内容)。

缺点:存储压力大,写扩散(发布一条内容,产生N个写请求)。

推拉结合模式

  • 普通用户:推模式。(写扩散和存储压力可控,延时低)

  • 大V:分活跃用户与非活跃用户。

    • 活跃用户:采取模式。(刷新频率高,推模式延迟低,并且基数小)

    • 非活跃用户:采取模式。

实现TimeLine Feed流

定义:

Timeline Feed流数据要求内容按照发布时间从近到远排序,对于发布时间相同的内容,按照内容ID由大到小。

内容与用户收件箱的交互:

背景:

推模式下,当内容发布的时候,我们需要把这些内容推送到粉丝的收件箱当中。

解耦与异步化

  • 问题:内容发布服务与Feed分发逻辑强耦合,导致发布接口可用性降低。
  • 方案:引入消息队列
  • 流程:发布服务仅发送“内容变更事件” -> 独立的Timeline消费者异步消费消息并执行推送。

可靠性保障

  • 问题:消费者故障可能导致数据丢失。
  • 方案有序遍历 + 断点续传
  • 流程:消费者将待推送粉丝ID排序,每推送一个,就更新数据库中的“进度游标”。发生故障后,消息队列的重试机制会触发消费者重新消费,通过读取游标跳过已处理用户,实现精确续传。

并行化优化

  • 问题:海量粉丝场景下,单线程串行遍历推送导致消息积压和高延迟。
  • 方案分发器-执行器模式
  • 流程
  1. 分发器:将庞大的粉丝列表按粒度(如每1000人)拆分为多个子任务消息。
  2. 执行器:多个实例并行消费子任务消息。
  • 效果:化整为零,将串行阻塞转变为并行处理,大幅提升吞吐量。

收件箱

作用:保存被推送来的数据。

数据库实现:

表结构:

字段名类型含义
idBIGINT自增主键,无特殊含义
user_idBIGINT用户 ID
content_idBIGINT内容 ID
publish_timeDATE内容发布时间

写入:即新增一条记录

读取:

  • 下拉:

定义:下拉用于获取当前时间最新的N条内容。

SELECT content_id, publish_time FROM inbox WHERE user_id = 111 ORDER BY publish_time DESC LIMIT N
  • 上滑:

定义:获取已加载内容的发布时间之后的N条内容。

SELECT content_id, publish_time FROM inbox WHERE user_id = 111 AND (publish_time < ts OR (publish_time = ts AND content_id < last_content_id)) ORDER BY publish__time DE SC LIMIT N

优点:用廉价的磁盘空间assz

缺点:数据库应对高并发的能力有限

Redis的ZSET实现:

结构:

  • Key为“inbox_{用户ID}”,表示一个ZSET对象是哪个用户的收件箱。

  • Member 为内容 ID。

  • Score为内容发布时间。ZSET可以按照内容发布时间从小到大排列内容ID

写入:

ZADD inbox_lll 1688659398 999

读取:

  • 下拉:

ZREVRANGE inbox_lll 0 N-l WITHSCORES

  • 上滑:

    原理:先查排名,再按排名偏移

    优点:较直接,不需要依赖发布时间,可以天然覆盖多条内容在同一时间发布的场景。

    缺点:

    • 只适合推模式。不适合推拉结合模式。

    • ZREVRANK依赖实时相对位置,当插入或删除数据的时候,位置变化,容易造成消息重复和丢失。

    -先获取最后内容的排名 local rank = redis.call(* ZREVRANK1, KEYS[1], ARGV[1]) --如果找不到成员则返回空 if rank == false then return {} end —从最后内容的下~~位开始拉取N条内容 local res = redis . call (1 ZREVRANGE' f KEYS [1] , rank + 1, rank + ARGV[2], 1 WITHSCORES 1) return res

    原理:以‘时间戳’为第一排序键,‘内容ID’为第二排序键。查询时,定位所有满足‘时间戳小于上次’或‘时间戳相等但内容ID小于上次’的数据。”

    做法:“ZSET 的排序机制天然对应数据库的‘联合索引’。我们将‘发布时间’作为 Score,‘内容ID’作为 Member,利用 ZSET‘先比分数、后比内容ID’的特性,自动实现了与数据库完全一致的‘时间优先、ID 兜底’的全局有序性。”

    问题:ZSET将Member值视为字符串类型,所以使用了字典序排列,而不是数字顺序。导致:虽然这4个成员的Score相同,但是Member 的排列结果却是10833、1673、19671、627。

    解决:

    String formatMember = String.format("%020d", contentId);

    // %020d 含义:输出十进制整数,宽度 20,不足部分用 0 填充

    • 方法1.使用上次内容ID获取

    • 2.使用时间戳和内容ID获取。

推拉结合模式构建Timeline

构建Feed流

下拉

区分推拉对象:获取关注列表。收件箱中已有其内容的关注者走“推模式”;收件箱中没有的走拉模式。

并行拉取数据:

  • 用户收件箱取最近的N条。

  • 从 M个关注者的发件箱各取最近的 N条。

多路归并:对这 M+1个列表进行排序合并:

  • 排序规则:发布时间(TS)从大到小;若 TS 相同,内容 ID 从大到小。

  • 截断:取前 N 条作为最终 Feed 流展示。

上滑

核心逻辑:带游标偏移,多取一删,精准衔接。

注:设已读最后一条内容的发布时间为ts,内容 ID 为last_id

数据拉取

  1. 推模式(收件箱):从 Redis 或数据库中拉取(ts < last_ts)(ts == last_ts 且 id < last_id)的前 N 条内容。

  2. 拉模式(发件箱):针对 M 个大 V 关注者,分别从其发件箱拉取ts <= last_ts的前 N+1 条内容。

针对拉取列表的“清洗”

对拉模式得到的 M 个内容列表,分别执行以下过滤:

  • 逻辑:遍历列表,只要当前元素的(ts, id)大于或等于(last_ts, last_id),就直接剔除。

多路归并:对这 M+1个列表进行排序合并:

  • 排序规则:发布时间(TS)从大到小;若 TS 相同,内容 ID 从大到小。

  • 截断:取前 N 条作为最终 Feed 流展示。

本文灵感来自《亿级流量系统架构与设计》

http://www.jsqmd.com/news/558275/

相关文章:

  • Arduino UNO Q 板载 Nanobot 自动化编程指南之七
  • OpenClaw安全加固:nanobot镜像的防火墙配置要点
  • 从GESP真题看二进制趣味数学:这些奇妙的数字性质你知道吗?
  • 从零构建词法引擎:Java源码解析如何绕过正则库实现精准分词(核心算法篇)
  • OpenClaw+QwQ-32B翻译助手:多语言文档批量处理
  • Unity 2022 LTS 实战:用NavMesh Agent和OffMesh Link,5分钟搞定一个会‘跳’会‘绕’的智能敌人AI
  • Vue3 + wangEditor 实战:从封装可复用的富文本组件到图片上传(附完整代码)
  • OpenRocket火箭设计与仿真全攻略
  • MATLAB实战:手把手教你实现Gardner环路位同步(附完整代码)
  • EcomGPT-7B开源大模型部署案例:企业级电商AI工具链搭建全流程
  • FLUX.1-devAI应用:与Stable Diffusion ControlNet联动实现精准构图控制
  • 春联生成模型-中文-base应用:个人家庭、企业商家春节装饰方案
  • 颠覆性智能科学探索:AI-Scientist-v2引领自动化科研新纪元
  • OpenClaw自动化监控:GLM-4.7-Flash驱动的系统异常检测与报警
  • 2026新会陈皮优质品牌推荐榜:鹿茸品牌排行榜、鹿茸哪个牌子最好、鹿茸哪个牌子最正宗、鹿茸排名、鹿茸排行榜、鹿茸牌子排名选择指南 - 优质品牌商家
  • 别再直接升glibc 2.25了!CentOS7下从2.17平滑升级到2.31的保姆级排雷手册
  • TensorFlow-v2.15快速体验:无需担心依赖冲突,纯净环境随用随弃
  • Alist挂载云盘翻车实录:我在Termux里踩过的3个坑及完美解决方案
  • 黑金AX301开发板+HS-04模块:手把手教你用FPGA实现超声波测距(附完整Verilog代码)
  • 如何用MOOTDX实现Python量化分析:3个关键应用场景深度解析
  • 解决ModelScope与datasets版本兼容性问题的最佳实践
  • 2026四川茶歇服务优质品牌推荐榜安全定制双保障:订制茶歇、BBQ烧烤、公司茶歇定制、冷餐会公司、冷餐会宴会、冷餐会承接选择指南 - 优质品牌商家
  • WeChatExtension-ForMac突破微信功能壁垒:全方位提升macOS微信效率实战指南
  • Flutter打包APK/AAB保姆级教程:从签名文件生成到避坑指南
  • 百川2-13B-4bits量化版实测:OpenClaw连续执行8小时稳定性报告
  • 长沙旧房改造专业服务商排行及价格参考:长沙二手房翻新预算/长沙旧房厨卫改造/长沙旧房墙面改造/长沙旧房局部改造/选择指南 - 优质品牌商家
  • 高等数学零点定理实战:3个典型例题解析与常见误区避坑
  • 告别混乱数据:LAMMPS后处理中compute chunk/atom命令的深度解读与避坑指南
  • Redis未授权访问的隐藏风险:Momentum靶机渗透中的密码泄露案例分析
  • Emu3.5:vision、text 的vocab id 体系