Feed流笔记及项目心得 这份笔记用于后续学习和优化friend_zone项目:https://github.com/CuSO41108/feed_example。当前项目已经完成了朋友圈系统里最核心的 Timeline Feed 主链路发布动态、写 MySQL、写 event outbox、Kafka 异步分发、Redis 热 inbox、MySQL 兜底、推拉结合查询和游标分页。后续学习时不需要刻意避开 Feed相反要把 Feed 当作主干再把发布审核、隐私权限、互动、缓存一致性、高可用和异常边界都接到这条主干上。0. 总体架构主线朋友圈系统的核心目标是在海量用户和高并发查询下支撑动态发布、好友动态聚合查看、点赞评论互动同时保证隐私安全、内容安全和体验流畅。推荐总体架构MySQL 做持久化兜底保存用户、好友关系、动态、媒体、互动、通知、审核记录等核心数据。Redis 做高频读写缓存保存用户 Feed 列表、动态详情、互动计数、点赞去重集合、评论列表等热点数据。Kafka 做异步削峰承接发布动态后的 Feed 分发、缓存清理、通知生成、审核回调、统计更新等异步任务。API 服务保持无状态方便水平扩容worker 服务按 Topic 消费消息独立扩展。核心链路可以理解为用户发布动态后端校验内容、隐私、频率和媒体状态。MySQL 事务写动态主表、作者 outbox、事件 outbox。outbox relay 将事件投递到 Kafka避免“数据库写成功但消息丢失”。worker 消费发布事件按普通用户推、热点用户拉的策略更新 Feed。Redis 保存活跃用户的 Feed 热列表MySQL 的 inbox/outbox 作为兜底。用户刷新朋友圈时优先查 Redis未命中再查 MySQL并按时间游标分页。读路径要二次校验好友关系、动态状态、审核状态和隐私权限防止缓存脏数据越权展示。一句话总结以 MySQL 保证持久化以 Redis 扛高并发读写以 Kafka 做异步削峰以 Feed 推拉结合做动态分发再用权限校验和补偿任务兜住一致性。1. 动态发布与审核流程核心目标实时性普通用户发动态后尽快返回成功不让审核、媒体处理拖慢主流程。内容安全文字、图片、视频都需要审核违规内容不能进入可见流。数据一致性动态主表、媒体表、审核记录、消息事件要能对齐失败可重试。推荐流程前端提交动态内容文本、图片或视频、隐私级别、指定可见好友等。媒体先上传对象存储例如 OSS、S3、MinIO得到 media URL 或 object key。后端校验隐私配置例如指定可见好友是否存在、是否互相关注、数量是否超限。内容审核强管控场景同步审核通过后才发布。轻量场景先发布为audit_pending或正常可见再异步审核失败后下架并通知用户。生成动态 ID建议继续使用雪花 ID保证趋势递增和分布式唯一。MySQL 事务写入动态主表、媒体表、可见范围表、event outbox。outbox relay 将核心事件发送到 Kafka触发后续分发、通知、统计等异步任务。前端收到发布结果刷新朋友圈或插入本地最新动态。建议补充的数据模型posts或dynamics增加字段visibility_type、audit_status、media_count、like_count、comment_count。post_media保存媒体类型、object key、URL、宽高、时长、转码状态、审核状态。post_visibility保存指定可见或不可见用户列表也可以按分组扩展。content_audit_records保存审核请求、审核结果、失败原因、供应商回调信息。publish_rate_limits或 Redis 限流键限制用户单位时间发布次数。进阶优化内容审核异步化普通文字可先发后审图片和视频建议至少先拿到媒体审核结果。媒体文件预处理上传后异步压缩、转码、生成封面、提取宽高和时长。发布频率限制例如每分钟最多 5 条避免刷屏和垃圾内容。状态机设计draft - pending_audit - published - rejected - deleted状态变化必须可追踪。2. 隐私权限设计隐私模块决定“谁能看见这条动态”是朋友圈系统里最容易出现边界问题的地方。常见可见范围仅自己可见。好友可见。指定好友可见。指定好友不可见。分组可见或标签可见。发布时要做的校验指定可见用户必须存在。指定用户是否必须是好友需要按产品规则决定。可见范围数量不能无限大否则会把发布接口拖成重操作。可见配置要和动态一起持久化避免后续查不到当时发布意图。查询时要做的校验用户是否仍然有权查看该动态。动态是否删除或审核失败。作者账号是否正常。当前用户是否被作者屏蔽。好友关系变化的边界删除好友后是否还能看删除前发布的动态。新增好友后是否能看添加前的历史动态。拉黑或屏蔽后缓存里的旧动态是否需要清理。两种常见策略查询时实时校验好友关系权限最准确但查询成本更高缓存命中后也不能完全跳过校验。发布时快照可见范围读路径更快但关系变化后需要额外规则处理历史可见性。建议项目先采用简单且安全的方案读路径查出候选动态后再做一次轻量权限过滤等数据量上来后再考虑关系版本号、可见范围快照和异步缓存清理。3. Feed 推拉结合与动态分发Feed 是朋友圈系统的主干。它解决的是“我打开朋友圈时如何快速看到我有权限查看的好友动态并按发布时间稳定排序”。推模式普通用户发布动态后系统异步把动态 ID 写入所有可见好友的 Feed 列表。优点是读路径快用户刷新时直接拿自己的 Feed 列表即可。缺点是写放大明显作者好友很多时会产生大量写入。拉模式热点用户或大 V 发布动态后只写自己的作者动态列表不主动推给所有好友。好友刷新朋友圈时再从热点作者 outbox 拉取动态与自己的 inbox 合并。优点是避免推送风暴缺点是读路径需要做聚合。推拉结合普通用户走推模式保障实时性。热点用户走拉模式控制写放大。对热点用户的活跃好友可以局部推送对非活跃好友读时再拉。Redis 保存最近 Feed 热数据MySQL 保存完整索引作为兜底。兜底机制推模式推送失败时自动降级为拉模式好友查询时补拉该动态。Redis Feed 缓存过期后从 MySQL inbox/outbox 重新聚合。历史动态超过保留窗口后从 MySQL 归档表或历史表拉取减少 Redis 压力。项目当前已经完成的部分posts保存动态主表。author_outbox保存作者动态列表。user_feed_inbox保存用户 Feed 索引。event_outbox配合 Kafka relay 保证发布事件可靠投递。worker 分块 fanout并把活跃用户最新 Feed 写入 Redis ZSET。Feed 查询支持 Redis 优先、MySQL 兜底、热点作者 outbox 合并和游标分页。后续可优化在posts或author_outbox记录发布时的 fanout 策略避免作者粉丝数变化后历史动态策略漂移。增加 fanout 任务进度表支持失败后从上次 follower_id 继续推。增加 Feed 最大翻页深度、历史归档和冷数据查询路径。增加缓存命中率、fanout 延迟、Kafka 堆积、MySQL 回源次数等监控。4. 实时互动功能互动模块主要包括点赞、取消点赞、评论、回复、互动通知。它不属于 Feed 主链路但会反向影响动态详情、列表计数和消息中心。点赞推荐流程用户点击点赞。Redis 判断是否已点赞可用 Set、Bitmap、Bloom Filter项目初期用 Set 更直观。未点赞时执行 Redis 原子操作添加点赞记录。INCR动态点赞数。异步写 MySQLpost_likes表。更新posts.like_count或独立统计表。返回点赞成功并可异步发送点赞通知。取消点赞类似Redis 判断是否已点赞。已点赞则移除点赞记录。DECR点赞数但要避免减成负数。异步更新 MySQL。需要注意点赞必须幂等重复请求不能重复加计数。Redis 和 MySQL 最终一致即可但要有补偿任务校准计数。热门动态的点赞用户集合可能很大可只缓存最近点赞用户和计数全量记录落 MySQL。建议表post_likes(content_id, user_id, status, created_at, updated_at)唯一键(content_id, user_id)。建议 Redis keypost:likes:{content_id}Set记录已点赞用户。post:stats:{content_id}Hash记录like_count、comment_count。评论推荐流程用户提交评论。内容审核至少对文本做敏感词或第三方审核。生成评论 ID。写 MySQLpost_comments表。Redis 增加评论数并按评论时间缓存评论列表。返回评论成功并异步推送评论通知。评论列表查询优先查 Redis 缓存按评论时间排序。未命中查 MySQL并回填缓存。支持分页避免一次返回过多评论。回复评论可用parent_id关联主评论和子评论。建议表post_comments(comment_id, content_id, user_id, parent_id, content_text, audit_status, status, created_at, updated_at)。建议 Redis keypost:comments:{content_id}ZSETscore 为评论时间member 为评论 ID。comment:detail:{comment_id}Hash 或 JSON缓存评论详情。5. 消息中心与通知消息中心承接点赞、评论、回复、审核失败、系统提醒等事件。推荐事件来源点赞成功事件。评论成功事件。回复评论事件。动态审核失败事件。动态被删除或被系统处理事件。建议流程互动或审核模块写业务表。同事务写 notification outbox。Kafka 消费者异步生成通知记录。在线用户可以通过 WebSocket、SSE 或轮询获得未读消息。建议表notifications(notification_id, receiver_id, actor_id, type, content_id, comment_id, read_status, created_at)。项目初期可以先做站内未读列表不急着接实时长连接。6. 聚合查询与缓存优化图片里提到的多级缓存和聚合优化是当前项目后续可以强化的方向。当前项目已经有 Redis ZSET inbox但还没有完整的多级缓存、热点动态详情缓存和历史数据分层。多级缓存L1应用本地缓存例如 Caffeine 思路缓存当前用户最近几次聚合结果过期时间很短例如 5 分钟。L2Redis保存用户动态 ID 列表、热点动态详情、点赞评论计数过期时间较短例如 30 分钟。L3MySQL作为最终兜底查询源后续可分库分表。分页优化Redis Sorted Set 使用发布时间作为 score。用ZRANGEBYSCORE或反向范围查询实现时间游标分页。只传输动态 ID详情按批量接口补齐减少网络传输。动态聚合策略预聚合更新结合推模式用户发布动态后异步更新好友的 Redis Feed 列表把写压力放到后台处理提升读路径速度。分页查询优化Feed 列表只保存动态 ID 和时间分数分页时先取 ID再批量查询动态详情。热点用户优化热点作者单独维护作者动态缓存好友查询时拉取热点作者动态避免每次重新聚合。历史懒加载近期数据优先查 Redis历史数据按时间游标查 MySQL 或归档表再短期回填 Redis。热点用户优化对好友多、发布频繁的用户单独维护作者动态缓存。好友查询时直接拉取热点作者动态避免重复聚合。热点用户动态不要无脑推送给所有人否则写放大会很明显。历史动态懒加载最近数据走 Redis。超过保留窗口的数据走 MySQL。查到历史数据后可短期回填 Redis提高连续翻页体验。项目可补点给动态详情增加 Redis Hash 缓存。给点赞数、评论数增加 Redis 统计缓存。给 Feed 查询增加缓存命中率、MySQL 回源次数等指标。给历史 Feed 增加最大翻页深度或归档策略。7. 媒体与大文件加载优化朋友圈动态不只有文字高清图片和视频会显著影响发布耗时、审核耗时和 Feed 加载体验。媒体能力建议独立建模不要把所有媒体信息塞进动态主表。发布侧图片和视频先上传对象存储动态主表只保存媒体引用。上传完成后异步做压缩、转码、生成封面、提取宽高、时长和文件大小。同一媒体生成多种规格例如缩略图、标清图、高清图、视频封面、短视频转 MP4。媒体状态独立维护例如uploading、processing、ready、failed、rejected。加载侧按需加载根据设备、网络状态和页面位置选择合适分辨率移动网络优先加载低清WiFi 再加载高清。CDN 加速媒体文件放到 CDN利用边缘节点减少延迟。预加载用户滑动 Feed 时提前加载下一页动态的封面和缩略图。懒加载首屏只加载可见区域媒体未进入视口的图片和视频延迟加载。异常处理媒体处理失败时动态可以展示文本和可用媒体同时标记失败媒体。视频转码未完成时可以先展示封面和“处理中”状态。审核失败的媒体要触发动态状态更新和缓存清理避免违规内容继续展示。项目可补点新增post_media表保存 object key、媒体类型、宽高、时长、转码状态和审核状态。前端 Feed 卡片支持图片缩略图和视频封面。引入媒体处理 worker独立消费media.uploaded或post.published事件。8. 高可用与容灾设计服务层API 服务保持无状态方便多实例部署。使用 Nginx、SLB 或其他负载均衡分发请求。单实例故障时请求自动转发到其他健康实例。多机房多个机房部署应用实例、Redis 集群、MySQL 主从或分片。通过全局负载均衡 GSLB 做跨机房切换。单机房故障时流量切到健康机房。降级与熔断Redis 故障时Feed 和互动计数可降级查 MySQL或者返回缓存过期提示。Kafka 堆积时优先保证发布和查询延迟非核心通知。审核服务不可用时按风险等级决定是阻断发布还是进入待审核状态。评论回复、实时推送、消息提醒可以作为非核心功能优先降级。推荐保留的核心能力登录鉴权。动态发布。动态查询。点赞基础操作。可优先降级的能力评论回复。实时通知。热门推荐。媒体高级处理。9. 异常与边界场景好友关系变化导致动态可见性问题场景用户删除好友后仍能看到该好友删除前发布的动态或者新增好友后看不到该好友添加前的历史动态。影响违反隐私规则用户体验不一致严重时会造成越权查看。实时同步缓存删除好友时异步删除当前用户 Redis Feed 列表中该好友的动态 ID同时删除相关动态详情缓存添加好友时异步把该好友最近一段时间的历史动态聚合到当前用户 Feed 缓存。查询时二次校验即使缓存中存在动态读路径仍要校验当前好友关系、动态状态、审核状态和可见范围无权限则过滤。历史动态批量处理添加好友后的历史动态同步不要阻塞关注接口可通过后台任务批量补齐最近 30 天或产品允许范围内的数据。发布成功但消息未分发问题动态主表已写入但 Kafka 发送失败。方案继续使用 outbox 表relay 定时重试事件消费端保持幂等。重复消费或重复写入问题Kafka 消息重复导致 inbox、点赞、通知重复。方案唯一键、INSERT IGNORE、业务幂等 key、消费记录表。审核失败后的回收问题先发后审的内容被判违规但已经进入部分用户缓存。方案动态状态改为 rejected/deleted发送删除事件清理 Redis 和 Feed inbox通知作者。动态删除或修改后的缓存一致性问题场景用户删除或修改动态后MySQL 已更新但 Redis Feed 列表或动态详情缓存未及时更新其他用户仍看到旧内容或已删除内容。影响数据不一致影响信息准确性和用户信任。同步更新缓存删除或修改动态时先更新 MySQL再发布删除/修改事件由消费者删除 Redis 动态详情、Feed 列表中的动态 ID、评论缓存和通知引用。延迟双删删除缓存后延迟一小段时间再删一次降低并发读导致旧缓存回填的概率。缓存过期兜底动态详情、Feed 列表、评论列表都设置合理 TTL即使删除事件失败缓存过期后也会回源加载最新数据。读路径兜底无论缓存是否命中关键状态仍以动态状态为准例如deleted、rejected、audit_pending不应展示。缓存与数据库计数不一致问题Redis 点赞数、评论数和 MySQL 明细不一致。方案以 MySQL 明细为准周期性校准 Redis 统计热点数据允许短期最终一致点赞和取消点赞要用 Redis 原子操作避免并发计数错误。评论过多问题热门动态评论列表过长查询和缓存压力大。方案只缓存前几页后续分页查 MySQL评论详情按需加载。媒体处理失败或加载慢问题高清图片、1080P 视频等大文件加载慢用户等待时间长可能放弃查看。方案上传后异步压缩、转码、生成多分辨率版本CDN 加速分发Feed 滚动时预加载下一页媒体客户端按网络状态选择清晰度。删除动态后的清理问题动态删除后仍出现在 Feed、评论列表、通知入口。方案发布删除事件消费者清理 Redis、inbox、评论缓存和通知引用读路径始终过滤status ! published。历史动态查询性能下降场景用户查询很早以前的动态例如 1 年前的朋友圈数据分布在历史分表或归档表中查询和聚合耗时明显增加。影响用户体验差可能放弃查询历史动态。历史数据归档超过一定时间的数据迁移到低成本存储或历史表减少主表压力。历史动态预聚合按用户和月份预聚合历史动态索引查询时直接查归档索引。懒加载与小分页历史查询采用懒加载单页数量适当减小例如 10 条减少单次查询数据量。缓存回填用户连续翻页历史动态时将查询结果短期写入 Redis提高后续翻页速度。高并发发布压力问题热点时段大量用户发布动态审核、写库、分发和通知都可能形成峰值。方案发布接口只做必要校验和主表写入审核、Feed 分发、通知、统计全部走队列异步化对单用户发布频率做限流对非核心任务做降级。10. 总结与面试回答要点面试官最想听到的核心点需求痛点识别朋友圈的难点不是简单发帖而是动态聚合复杂、高并发查询、海量数据存储、隐私权限控制和缓存一致性。架构选型逻辑选择“消息队列 Redis MySQL”是为了平衡实时性、吞吐量、成本和可靠性。Feed 推拉理解普通用户推热点用户拉活跃用户可局部推非活跃用户读时拉避免写放大和推送风暴。缓存设计细节Redis Sorted Set 适合按时间排序的 FeedHash 适合动态详情Set 适合点赞去重多级缓存提升吞吐。一致性思维通过 outbox、异步双写、延迟双删、定时校验和读路径兜底处理缓存和数据库不一致。权限与安全发布时保存可见范围查询时二次校验审核失败后实时下架并清缓存。异常处理意识好友关系变化、动态删改、Kafka 重复消费、Redis 故障、媒体处理失败、历史查询变慢都要有方案。结构化表达按“需求 - 目标 - 架构 - 核心链路 - 缓存与一致性 - 异常 - 总结”的顺序回答。口述总结示例首先朋友圈系统的核心价值是在海量用户和高并发场景下高效支撑动态发布、好友动态聚合查看和实时互动同时保证隐私安全和体验流畅。传统单库单表无法支撑高查询 QPS单纯 Redis 发布订阅又存在消息不可靠问题所以我会采用“消息队列 Redis MySQL”的混合架构。核心模块上Feed 使用推拉结合。普通用户发布动态后走推模式异步把动态 ID 写入好友 Feed 列表保障读路径速度热点用户或好友数很多的用户走拉模式只维护作者 outbox好友刷新时再拉取并合并避免推送风暴。Redis 用 Sorted Set 维护按时间排序的 Feed用 Hash 缓存动态详情用 Set 做点赞去重MySQL 保存动态、好友关系、inbox/outbox 和互动明细作为最终兜底。一致性方面发布动态时先写 MySQL 和 event outbox再由 relay 投递 Kafkaworker 消费后分发 Feed消费端通过唯一键和幂等逻辑避免重复写入。动态删除或审核失败时发送删除事件清理 Redis、Feed inbox、评论和通知缓存同时查询时二次校验好友关系、动态状态和可见范围防止缓存脏数据导致越权展示。异常场景上好友关系变化通过缓存清理和查询时校验保证可见性正确缓存不一致通过延迟双删、TTL 和定时校验兜底大文件通过对象存储、CDN、转码压缩和预加载优化体验历史动态通过归档表、按月预聚合和小分页懒加载降低查询压力。总结来说这套设计以 Redis 扛高并发以 Feed 推拉结合做动态分发以 MySQL 做持久化兜底再用工程化手段保证一致性、隐私安全和高可用。11. 建议项目优化路线第零阶段夯实现有 Feed 主链路记录发布时的 fanout 策略避免热点用户身份变化影响历史动态分发判断。增加 fanout 任务进度表支持失败任务继续推送。增加 Feed 查询、Redis 命中、MySQL 回源、Kafka 堆积和 worker 处理耗时指标。给 Feed 服务补充单元测试和集成测试覆盖游标分页、Redis 未命中、热点作者合并和删除动态过滤。第一阶段发布安全与隐私给posts增加审核状态和可见范围字段。增加隐私校验模块例如internal/module/privacy。增加发布频率限制。增加内容审核记录表先做 mock 审核服务也可以。第二阶段互动功能增加点赞接口、取消点赞接口。增加评论接口、评论列表接口。增加点赞和评论计数缓存。增加互动事件 outbox为消息中心做准备。第三阶段消息中心增加通知表。点赞、评论、回复后异步生成通知。增加未读数和已读接口。第四阶段媒体能力增加post_media表。支持图片和视频元数据。增加媒体上传后的压缩、转码、封面、审核状态。第五阶段工程化可靠性增加 Kafka 消费指标、DLQ 查看和重放机制。增加缓存命中率、MySQL 回源次数、发布延迟、fanout 延迟监控。增加降级开关例如关闭实时通知、评论回复或媒体高级处理。为核心模块补单元测试和集成测试。