本文作者:V5IfhMOK8g

我把数据复盘了一遍:91网页版为什么你总刷到同一类内容?多半是多端适配没弄明白(别被误导)

V5IfhMOK8g 今天 79
我把数据复盘了一遍:91网页版为什么你总刷到同一类内容?多半是多端适配没弄明白(别被误导)摘要: 我把数据复盘了一遍:91网页版为什么你总刷到同一类内容?多半是多端适配没弄明白(别被误导)简介 很多产品经理、工程师和运营同学常抱怨:网页版的流量一旦上来,用户就不断看到...

我把数据复盘了一遍:91网页版为什么你总刷到同一类内容?多半是多端适配没弄明白(别被误导)

我把数据复盘了一遍:91网页版为什么你总刷到同一类内容?多半是多端适配没弄明白(别被误导)

简介 很多产品经理、工程师和运营同学常抱怨:网页版的流量一旦上来,用户就不断看到相似内容,体验变差。经过对一次真实流量样本的复盘,我发现一个最常被忽视的罪魁祸首:多端适配(multi-end adaptation)没做好。换句话说,服务端、缓存、个性化引擎和前端在不同端口之间没把“同一用户/同一会话/同一历史”对齐,导致重复曝光、推荐去重失效或策略冲突。

这篇文章把复盘过程、关键发现和可落地的修复建议都整理出来,既适合技术实现,也方便产品/数据同学对问题做验收。

我怎么做的(数据与方法)

  • 数据来源:过去 7 天的线上曝光日志(impressions)、点击日志(clicks)、会话表(sessions)、用户标识映射(loginid / cookieid / device_id)以及推荐引擎的请求/响应日志(含 rank score、source)。
  • 样本范围:每天高峰时段的 500k 次曝光抽样,覆盖 PC 网页、移动网页、Android、iOS。
  • 关键指标:
  • Repeat Rate(重复曝光率):同一用户/会话在短时间窗内再次看到同一 content_id 的比例。
  • Same-Category Share:会话内前 10 条中,同类别(category)内容占比。
  • Unique Impressions per Session:每次会话中不同 content_id 的数量。
  • 核心分析手段:按端(web/mobile/app)拆分指标、对比缓存命中与非命中请求、检查个性化引擎是否收到正确的历史/特征、审查 cache key 与 CDN 配置。

一个常用的 SQL 示例(计算会话内不同内容占比): SELECT sessionid, COUNT(DISTINCT contentid) AS uniqcnt, COUNT(*) AS totalimpressions, COUNT(DISTINCT contentid) * 1.0 / COUNT(*) AS uniqratio FROM impressions WHERE date BETWEEN '2026-02-01' AND '2026-02-07' GROUP BY session_id;

主要发现(问题清单) 1) 同一用户在不同端没有统一的识别链路

  • 未登录用户依赖 cookie 或 localStorage,在不同端(PC 浏览器、手机浏览器、App 内嵌 webview)时往往拿不到同一标识,导致服务端无法拼接历史,个性化引擎被当成“新用户”或“冷启动”统一下发默认推荐,结果看起来像“同一类热门内容被一直推”。

2) 缓存策略打平了个性化差异

  • 服务端或 CDN 使用过大的 cache key 粒度(例如只按 URL 分缓存或忽略 User-ID),导致多个用户/端命中同一缓存页面或同一静态响应,个性化片段被缓存成“模板化热榜”,不同用户看到高度相似内容。

3) 推荐去重在多端合并时失效

  • 去重逻辑仅在单端执行(例如 app-side 去重),但后端在合并来自不同内容源(feed、专题、广告)时没有统一去重,或去重基于不同的 content_id 命名空间,导致重复逻辑漏掉跨源重复。

4) 个性化权重/策略在多端不一致

  • 各端使用了不同版本的 ranker 或不同的默认参数(favor recency vs. popularity),且没有记录版本信息。流量路由有概率把网页版流量打到某一默认策略,造成网页版“更倾向于热门/同类内容”的表象。

5) 展示侧(前端)重试/无序导致重复

  • 前端在请求失败时会重试并接收相同响应;又或者懒加载和分页实现问题导致同一批数据被插入多次到 DOM 中,用户视觉上感知为重复内容。

6) 跨端曝光记录不同步,去重窗口错位

  • 去重窗口(例如 24 小时内不重复推荐)在 server A 上以 UTC 生效,但在 B 上按本地时间生效,或者去重 key 用了不同哈希算法,造成同一条 content 在不同端并行曝光。

可落地解决方案(工程 + 算法 + 产品) 下面把可实施的修复按优先级排序,从“低成本快速起效”到“深度改造”。

A. 低成本、快速验证的改动(1-2 周) 1) 前端展示去重复校验

  • 在前端维护一个短期 sessionseenset(基于 content_id 哈希),插入新数据前先做一次去重。注意与后端去重保持一致的 key。
  • 对非登录用户使用 cookie/localStorage 的轻量 fingerprint(不用于跨域跟踪,仅用于会话内去重)。

2) 针对热门区做小范围随机化

  • 在 top N 的热门推荐中加入小幅随机化(例如 top10 中打乱 10%-20% 的位置或用 epsilon-greedy 的小概率探索),减少看到相邻近似内容的概率,同时监控 CTR。

3) 修正 CDN/缓存 key(短期)

  • 把缓存 key 加上端信息(devicetype、ismobileweb、usersegment),或者让个性化片段 bypass 公共缓存,避免把高度个性化的 html/body 缓存成全量响应。

B. 中期改造(2-8 周) 4) 统一用户识别策略(登录优先,匿名优先策略)

  • 识别优先级:loginid > crossdevicecookie (持久 cookie) > sessioncookie > ephemeral_fingerprint。
  • 在后端的请求日志里记录每条请求的实际 usedid 和 usedid_source,方便后续审计和实验分流。
  • 对未登录设备推行“轻度联邦化”历史拼接:用 hash(device attributes + consent) 做短期合并,能在不侵害隐私的前提下改善个性化效果。

5) 去重与合并的统一层(server-side)

  • 建立一层统一的 de-duplication 服务:输入多个源(feed、专题、广告),输出按规则去重与分配的最终列表。去重规则支持同 content_id、相似 title、相似 url 的 fuzzy 去重。
  • 去重窗口参数化(例如 lastN, lastT),能够按用户、按端配置。

6) 个性化服务能力对齐

  • 把 ranker 的版本和配置下沉到日志,每次请求都记录 rankerversion、featureset、weight_config,便于回溯。
  • 统一多个端的默认 hyperparameter(如探索率、冷启动曝光数),可通过配置中心动态下发。

C. 深度优化(8 周以上) 7) 强化跨端用户画像合并

  • 对已登录用户,建立统一 profile(历史曝光、点击、收藏)并在各端实时同步(或近实时)。对匿名用户,建立可待升级的半持久 profile,登录后用于归并。
  • 解决跨端时间序列一致性:采用 append-only event store 并用流式处理同步到在线 feature store。

8) 多端缓存与个性化的协调机制

  • 对个性化响应采用分层缓存:公共热点区(边界)、用户无感差异化区(小键策略)、用户级个性化区(bypass CDN 或私有缓存)。
  • 引入 Cache Vary 策略(例如 Vary: User-Agent; Vary: X-User-Segment)并限制粒度,以平衡命中率和个性化。

算法类改进(推荐侧)

  • 去重策略:主键去重(content_id),补以内容相似度阈值(例如基于标题向量 cosine > 0.9 即视为重复)。
  • 再排序(re-ranking)中加入多样性惩罚项:score' = score - lambda * recencypenalty - mu * seenpenalty,其中 seen_penalty 基于用户历史/会话内短期曝光。
  • 探索/利用平衡:实现多臂老虎机(MAB)或 epsilon-greedy 来动态控制热门 vs 个性化内容的占比。
  • 分层推荐框架:头部插入一定比例的“非个性化但高新鲜度”条目(editorial / 新品),以提升内容多样性。

监控与验收(KPI & SQL) 关键监控指标建议放到日报/实时告警:

  • Repeat Rate(24 小时内重复曝光率) = 重复曝光次数 / 总曝光次数
  • Session Unique Ratio(前 10 条的 uniq_ratio)
  • CTR、Dwell Time(会话长度)
  • Cache hit rate by segment(按端口/用户分)
  • Ranker version skew(不同端使用的 ranker 版本分布)

示例 SQL(计算 24 小时重复曝光率): WITH last24 AS ( SELECT userid, contentid, MIN(timestamp) AS firstts FROM impressions WHERE timestamp >= now() - interval '24 hours' GROUP BY userid, contentid ) SELECT COUNT(*) FILTER (WHERE cnt > 1) * 1.0 / COUNT() FROM ( SELECT userid, contentid, COUNT() AS cnt FROM impressions WHERE timestamp >= now() - interval '24 hours' GROUP BY userid, content_id ) t;

验收实验设计(A/B)

  • 指标:primary 用 Repeat Rate 与 Session Uniq Ratio,secondary 用 CTR、留存、会话时长。
  • 样本量与时长:首轮小流量 5%-10% 流量,观察 3-7 天;若显著改善则扩大到 20%-50% 再观测统计显著性。
  • 失败条件:Repeat Rate 无改善但 CTR/留存下降,应回滚并排查带来的副作用。

用户与产品层面的缓解(体验优化)

  • 给出推荐原因:对重复感知高的用户,在重复内容位置增加“你可能喜欢类似内容”或“基于你最近的浏览”标识,降低认知厌倦。
  • 提供“换一批”显式交互,让用户主动刷新推荐策略。
  • 对登录用户鼓励“合并设备”或“同步历史”的 UX 引导,提升跨端识别率。

结论(简要) 网页版频繁看到同一类内容,背后通常不是单一 bug,而是多端之间的识别、缓存、去重与个性化策略没对齐。通过三步路线能快速缓解并最终固化体验: 1) 立刻做前端会话去重 + 增加热门区随机化; 2) 修正缓存 key 与记录请求使用的用户标识来源; 3) 中长期落地统一的去重层与跨端画像合并,并把 ranker 版本与配置纳入可观测指标。

如果你愿意,我可以:

  • 帮你把你们现有日志字段映射(示例)做成一份检查清单;
  • 根据你们的栈(比如用 Varnish/CloudFront + Lua、或用 Nginx + Redis cache)提供具体的 cache key 与 Vary 头配置建议;
  • 给出一份 A/B 实验的样本量估算脚本和分析模板。

你想先看哪一部分?是直接开始做缓存 key 修正的 checklist,还是把你们的曝光日志字段贴出来让我帮你做一次问题定位?