同一个人用 Web、App、小程序各操作一次,DAU 却常被算成三个人。建议你先把各端上报的 user_id、device_id、union_id 三个字段拉出来对一遍覆盖率,再谈怎么去重——因为根因就是埋点默认按设备号上报,而设备号天然分端、互不相认。要把他们合并成一个人,唯一可靠的钥匙是登录后的账号 ID。本文我按“为什么会重复 → 三层方案 → 什么时候不能去重 → 口径变化 → 治理与踩坑”展开。

为什么同一个人会被算成三个人?

结论:各端默认的用户标识是“设备级”的,不是“人级”的,所以同一个人在不同端天然就是三个互不认识的 ID。

我在做活跃口径评审时经常发现,团队以为埋点里已经有“用户 ID”,其实那个 ID 只是这一端自己生成的匿名标识:

  • Web 端:默认是 Cookie 里的 ClientID,绑定的是这台浏览器,换浏览器、清缓存就变;
  • App 端:默认是 App 安装实例 ID(如 Firebase 的 app_instance_id),绑定这一次安装,卸载重装就变;
  • 小程序端:默认拿到的是 OpenID,而按微信开放文档(2026 年更新)的说明,同一用户在不同小程序、公众号下的 OpenID 并不相同。

这三类 ID 分属三个体系,没有公共键,各自 distinct 再相加,一个人就被算成三个人。GA4 同样如此:据其官方《用 User-ID 衡量跨平台活动》文档(2026 年更新),它默认用 device ID(Cookie 或移动应用 ID)识别用户,要跨设备、跨端合并,必须由业务方主动上报自己的 User-ID,Analytics 才会把每个 user ID 当作独立用户,给出更准确的计数。

跨端去重的三层方案,分别解决什么问题?

结论:按身份可信度从高到低分三层——登录账号 ID、平台级身份打通、匿名设备 ID;越往上越准,覆盖越窄,必须组合使用。

L1 登录账号 ID:跨端合并的唯一硬钥匙

用户一旦在某一端登录,就会带上业务自己的 user_id。据 Google 官方迁移参考文档(2026 年更新),GA4 会对 user_id 与 device ID 做身份解析回退,其 blended 报告身份按 User-ID → Google signals → device-ID → 建模逐级兜底。这一层能跨设备、跨端强合并同一人,可信度最高。

L2 平台级身份打通:同一平台主体内的多端合并

产品往往不只有一个 App,而是 App、网站、小程序、公众号一堆。按微信开放文档(2026 年更新),只要这些应用绑定在同一个微信开放平台账号下,同一用户的 UnionID 就唯一——哪怕他在每个应用下的 OpenID 各不相同。UnionID 让你在同一平台主体内把多端行为串起来,但出了这个主体,仍要靠账号 ID 再打通一次。

L3 匿名设备 ID:未登录态的底线方案

未登录拿不到账号,只能退回设备级标识:Cookie/ClientID、设备 ID 和广告 ID。据华为 HarmonyOS 官方开发者文档(2026 年更新),OAID 是设备级匿名标识符,同一台设备上不同 App 拿到的值相同,但用户可重置,且受跟踪开关影响,关闭时拿到的是全 0 值;苹果 iOS 侧对应的是同样可重置的 IDFA。所以这一层只能在单设备、单 App 内识别“这台机器”,既不稳,也跨不了设备。

身份层级典型标识来源 / 覆盖跨端能力主要限制
L1 登录账号 IDuser_id / 账号系统 ID自家账号体系,仅登录用户跨设备、跨端强合并未登录用户拿不到
L2 平台级身份UnionID / 开放平台 ID同一平台主体下多应用同平台内多端合并跨平台仍需账号 ID 再打通
L3 匿名设备 IDCookie、设备实例 ID、OAID/IDFA单端单设备,未登录态仅单设备、单 App用户可清缓存/重置广告 ID,跨设备无效
图 1 匿名 ID → 登录 ID → 账号 ID 的三层身份打通与统一 one_id 归并
图 2 三层去重方案:能去重到哪一层,取决于你拿到了哪一级身份

什么时候能去重,什么时候绝对不能?

结论:只要用户在某个端完成过登录或授权,就能把他此前在该设备上的匿名行为回挂到账号;但“未登录 + 不同设备”时,系统没有任何证据证明是同一人,禁止硬合。

  • 能去重:用户在 A 设备匿名浏览后登录,可把这段匿名会话回挂到 user_id;同一平台主体内用 UnionID 打通多端;同一台手机上的多个 App 可用广告 ID 近似识别。
  • 不能去重:仅凭 IP、UA、设备指纹把两台从未登录的设备判为同一人。这种合并会把不同人错杀成一个人,既压低真实人数,又触碰合规红线。

一句话原则:有证据才合并,没证据就按设备分别计数,并在口径里写明这是“设备口径”而不是“人数口径”。

换了去重口径,DAU/MAU/留存会怎么变?

结论:同一批行为数据,换一个去重键,数字可以差很多;口径必须写死并对外统一。

按行业通行定义,DAU 是一个自然日内活跃的去重用户数,MAU 是一个自然月内至少活跃一次的去重用户数,同一用户一天用多次也只计 1 次——真正决定数字大小的,是这个“去重用户”按什么 ID 去重。

  • 按匿名设备 ID 去重:一人在 Web、App、小程序各活跃一次,会被记成 3 个“独立用户”,DAU 虚高,跨端行为被割裂;
  • 按统一 one_id 去重:三条记录归并到同一账号,只记 1 人,DAU/MAU 才反映真实人数;
  • 次日留存必须用同一 one_id 回看,否则用户只是换了台设备,就被当成“流失后又来的新用户”,留存率被系统性低估。
图 3 去重口径决定 DAU/MAU 算成几个人,并联动影响留存计算

多端统一 ID 怎么治理?一段归并伪代码

结论:核心是一张“身份映射表”加一套稳定的归并优先级:有账号用账号,没账号用平台身份,再不行才退回设备,且各层命名空间绝不互相合并。

# 示意伪代码:把每条埋点事件归并到统一 one_id(非真实运行结果)
def resolve_one_id(event):
    # L1 登录账号 ID:可信度最高,跨设备跨端强合并
    if event.user_id:
        return "UID:" + event.user_id
    # L2 平台级身份打通(如微信 UnionID):同平台主体内合并
    if event.union_id:
        return "UNION:" + event.union_id
    # L3 匿名设备 ID:只能按设备,跨设备、跨命名空间绝不强行合并
    return "DEV:" + event.device_id

# DAU = 当天活跃的 one_id 去重计数
dau = count_distinct(resolve_one_id(e) for e in events_of_today())

# MAU = 近一个自然月内至少活跃一次的 one_id 去重计数
mau = count_distinct(resolve_one_id(e) for e in events_in_month())

注意 UID、UNION、DEV 来自不同命名空间,哪怕字符串相似也不允许跨前缀归并——这是防止“误把两个人合成一个人”的最后一道防线。

踩坑记录:MAU 莫名上涨,其实是匿名 ID 在漂移

现象

一次版本迭代后,未登录用户的 MAU 环比莫名上涨一截,运营一开始以为是拉新效果。

根因

新版 App 的本地匿名 ID 持久化出了问题,每次冷启动都重新生成一个设备实例 ID;同时 Web 与 App 各自一套去重,换设备/重装的用户被反复当成“新设备”重复计数。

排查过程

我取那几天的 device_id 重复率、新老用户占比,和登录 user_id 重合度对比,发现大量“新增用户”其实是同一账号带着一堆不同匿名 ID,锁定为匿名 ID 漂移,不是真拉新。

修复方式

把匿名 ID 可靠持久化、卸载后尽量保留,并用系统级广告 ID 兜底;报表上区分“登录按 user_id 去重”与“全量按设备去重”两套口径,避免混在一个 MAU 里。

经验教训

跨端去重不是一句 count(distinct user_id) 能解决的事。先想清楚“当前这个指标用哪一级身份”,再谈数字大小;匿名 ID 一旦不稳,所有未登录口径都会失真。

合规上要守住哪几条?

结论:识别越准越涉及个人信息,告知同意与最小必要必须前置。

  • 未登录态用 Cookie、设备指纹、广告 ID 识别,属于个人信息处理,必须在隐私政策中告知并取得同意;
  • 广告 ID(OAID/IDFA)本就设计为用户可重置、可关闭跟踪,不得当成永久身份强绑定;
  • 未授权不要用 IP+设备指纹做长期跨设备隐性追踪;统计要的是“去重人数”,不是“把人画像拼出来”。

全端采集工具能帮上什么,又帮不上什么?

跨端去重的前提,是三端事件先进到同一套数据模型。像 456数据 这类覆盖网站、App、小程序的全端平台,用一套 SDK 多端统一实时采集行为数据并提供可视化看板、约 5 分钟接入,省掉了分别接三端再汇合的工作量。但工具只负责“把数据收齐”,用哪一级 ID 去重、登录态怎么回填、口径怎么统一,仍要业务侧自己治理——这也是我反复强调三层方案的原因。

常见问题 FAQ

Q1:DAU 和 MAU 到底按什么去重?

按你定义的“一个人”的标识去重。推荐登录用户用 user_id、未登录用户退回设备 ID,并在口径里写明用的是哪一层,不要让 DAU 一会按设备、一会按账号。

Q2:没登录的用户,能跨端识别成同一个人吗?

不能可靠识别。未登录且跨设备时系统没有公共键,最多在单设备/单 App 内用设备 ID 近似,跨设备强行合并就是猜,风险大。

Q3:UnionID 能代替 user_id 做全量去重吗?

不能完全代替。UnionID 只在同一平台主体(如同一微信开放平台账号)内唯一,跨到自家 App 账号体系时,仍要靠登录后的 user_id 再打通一次。

Q4:为什么用户换了手机,留存就掉得很难看?

很可能是你按设备 ID 算留存。换新设备后匿名 ID 变了,系统把他当成新用户,老用户的“回访”就丢了;改成按统一 one_id 回看才能跨设备连续计算。

Q5:广告 ID(OAID/IDFA)能当稳定的跨端 ID 长期用吗?

不能。它是设备级、可重置的,且受跟踪开关影响(关闭时拿到全 0 值),只适合做未登录态的辅助兜底,不应当作永久身份。

Q6:一套 SDK 多端采集,是不是就自动完成跨端去重了?

不是。多端采集只是把三端事件收进同一数据模型,解决“数据到齐”;用哪一级身份去重、如何回挂历史匿名行为,仍要业务在映射表和口径层面治理。

小结:跨端去重的本质不是技术难题,而是“用哪一级身份代表一个人”的口径选择。登录账号 ID 最准但覆盖窄,平台身份居中,匿名设备 ID 是未登录态的底线;把这三层和它们的边界写清楚,DAU/MAU/留存才不会越算越糊涂。

Logo

一站式 AI 云服务平台

更多推荐