Feed流分类推荐Feed流关注Feed流附近Feed流Feed流的几种模式拉模式介绍写只写发件箱读拉所有关注者发件箱 → 合并排序优点实现简单存储空间小。缺点 读延迟高读扩散一次用户请求会放大N倍读请求。推模式写推送到所有粉丝收件箱读直接读收件箱优点性能好。不需要获取关注列表对象的发件箱内容。缺点存储压力大写扩散发布一条内容产生N个写请求。推拉结合模式普通用户推模式。写扩散和存储压力可控延时低大V分活跃用户与非活跃用户。活跃用户采取推模式。刷新频率高推模式延迟低并且基数小非活跃用户采取拉模式。实现TimeLine Feed流定义Timeline Feed流数据要求内容按照发布时间从近到远排序对于发布时间相同的内容按照内容ID由大到小。内容与用户收件箱的交互背景在推模式下当内容发布的时候我们需要把这些内容推送到粉丝的收件箱当中。解耦与异步化问题内容发布服务与Feed分发逻辑强耦合导致发布接口可用性降低。方案引入消息队列。流程发布服务仅发送“内容变更事件” - 独立的Timeline消费者异步消费消息并执行推送。可靠性保障问题消费者故障可能导致数据丢失。方案有序遍历 断点续传。流程消费者将待推送粉丝ID排序每推送一个,就更新数据库中的“进度游标”。发生故障后消息队列的重试机制会触发消费者重新消费通过读取游标跳过已处理用户实现精确续传。并行化优化问题海量粉丝场景下单线程串行遍历推送导致消息积压和高延迟。方案分发器-执行器模式。流程分发器将庞大的粉丝列表按粒度如每1000人拆分为多个子任务消息。执行器多个实例并行消费子任务消息。效果化整为零将串行阻塞转变为并行处理大幅提升吞吐量。收件箱作用保存被推送来的数据。数据库实现表结构字段名类型含义idBIGINT自增主键无特殊含义user_idBIGINT用户 IDcontent_idBIGINT内容 IDpublish_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条。多路归并对这 M1个列表进行排序合并排序规则发布时间TS从大到小若 TS 相同内容 ID 从大到小。截断取前 N 条作为最终 Feed 流展示。上滑核心逻辑带游标偏移多取一删精准衔接。注设已读最后一条内容的发布时间为ts内容 ID 为last_id。数据拉取推模式收件箱从 Redis 或数据库中拉取(ts last_ts)或(ts last_ts 且 id last_id)的前 N 条内容。拉模式发件箱针对 M 个大 V 关注者分别从其发件箱拉取ts last_ts的前 N1 条内容。针对拉取列表的“清洗”对拉模式得到的 M 个内容列表分别执行以下过滤逻辑遍历列表只要当前元素的(ts, id)大于或等于(last_ts, last_id)就直接剔除。多路归并对这 M1个列表进行排序合并排序规则发布时间TS从大到小若 TS 相同内容 ID 从大到小。截断取前 N 条作为最终 Feed 流展示。本文灵感来自《亿级流量系统架构与设计》