如何在算法驱动的动态无限滚动场景中高效规避内容重复问题
动态推荐流无限滚动的通用去重实现方案
目前主流社交、短视频类平台处理动态推荐场景下的滚动去重,核心是三层低开销的配合逻辑,彻底解决传统分页、全量预加载、全量回传方案的固有痛点,没有要求极高存储或带宽成本的设计。
1. 会话绑定的快照游标机制(核心实现)
这是生产环境真正落地的核心逻辑,没有花哨设计:
- 用户首次打开推荐流页面时,服务端不会顺着后台实时变动的全局推荐池逐次返回内容,而是即时生成一份固定排序的推荐内容快照,一般包含300-500条匹配用户偏好的内容,给这份快照分配一个唯一的、无实际业务含义的游标ID,把快照内容列表、当前已展示的位置、已展示的内容ID集合一起绑定存在低延迟缓存中(一般是内存节点或Redis,设置10-30分钟的短过期时间)。
- 客户端后续所有滚动拉取请求,只需要传递这个几字节大小的游标ID,服务端就会定位到对应绑定的快照存储项,按照固定的位置偏移返回下一页内容,完全不受后台推荐算法实时迭代模型、调整全局推荐池排序的影响,从根源上避免动态排序变动导致的内容重复。举个实际的请求例子,这类接口的滚动拉取请求形式大概是:
相比客户端回传全量已加载ID的方案,整个请求的额外开销只有10个左右的字符,根本不会随着滚动深度增加变大。GET /api/recommend?cursor=7f2k9x - 当用户滚动到当前快照的内容末尾时,服务端才会重新触发新的推荐计算,生成新的快照和新的游标ID;计算过程中会自动读取旧快照里存储的已展示内容ID列表,直接把这些内容从新的推荐结果中排除,完全不需要客户端回传任何已加载的内容标识。
2. 轻量化滚动窗口过滤
针对跨快照、跨会话场景的兜底去重,服务端也不会存储用户全生命周期的观看历史,避免存储成本膨胀:
- 服务端针对每个用户的当前浏览会话,只维护一个最近100-200条已展示内容的ID哈希集合,总存储开销不到2KB,在每次生成新的推荐快照时,直接把这个集合对应的内容从推荐结果中过滤掉。
- 这个短窗口的设计是算过体验收益和成本的:用户滚动到几十、上百条内容之后,早期浏览过的内容就算偶发重复,对体验的影响微乎其微,短窗口过滤已经能覆盖99%以上的可感知重复问题,完全不需要为了极小概率的极端情况,付出全量存储、全量传输的高成本。
3. 客户端本地兜底校验
最后一层拦防线是放在客户端做的本地校验:
- 客户端本地维护一个最近加载过的内容ID集合,一般设置500条的存储上限,内存开销不到1MB,每次接收到服务端返回的新内容,先在本地过滤掉集合里已经存在的重复ID,再插入列表展示。
- 就算服务端因为缓存失效、快照边界计算错误返回了重复内容,客户端也能在不依赖服务端的情况下拦截掉,用户完全感知不到问题。
对之前梳理的两类方案痛点的针对性解决:
- 不需要像WebSocket预加载方案那样存储无限多的全量推荐内容,只需要按会话存几百条短时效的快照,开销极低;就算快照到期重新计算推荐池,也会自动排除之前已展示的内容,不会回到重复内容的问题上。
- 不需要像客户端回传全量已加载ID的方案那样,随着滚动深度增加不断增大请求体大小,所有去重状态都存在服务端的会话缓存里,客户端每次滚动请求只需要携带固定长度的游标参数,不会随着滚动内容变多出现请求变慢的问题。
内容的提问来源于stack exchange,提问作者northmaybe
相关产品推荐
相关产品推荐

