跨域匹配的本质不是同步数据。

它是在两个边界之间建立一条有条件的、可解释的、可撤销的对应关系。广告 Cookie Sync、OAuth 登录、支付回调、转化归因——这些看起来毫无关系的场景,底层都在处理同一件事:确认"我这里的这个对象"和"你那里的那个对象"是不是同一个东西。

这篇文章从四个场景展开,抽象出一个通用模型,以及什么情况下你根本不应该做匹配。

跨域匹配到底在匹配什么

先看"域"是什么。

这里的"域"不只是浏览器里的 domain。它可以是:

  • 一个业务系统
  • 一个组织
  • 一个数据源
  • 一个账号体系
  • 一个设备空间
  • 一个第三方平台
  • 一个广告交易平台
  • 一个支付渠道
  • 一个分析系统

每个域都有自己的 ID 体系。

比如:

系统 A 中的用户:user_id = A123
系统 B 中的用户:user_id = B789

这两个 ID 本身没有任何天然关系。A123 不会因为长得朴素一点就自动等于 B789。工程里最危险的错误之一,就是看到两个字段都叫 user_id,就假装它们语义一样。

跨域匹配真正要建立的是一条映射关系:

A123 <-> B789

这条关系通常还不够,还要知道:

A123 <-> B789
source = cookie_sync
partner = some_exchange
created_at = 2026-05-21
expire_at = 2026-07-20
consent_scope = ...
confidence = ...

也就是说,匹配不是简单地“把两个 ID 存起来”。

它至少包含四层含义:

  1. 谁和谁匹配
  2. 为什么认为它们能匹配
  3. 这个匹配关系从哪里来
  4. 这个关系什么时候应该失效

缺少后面三点,前面的 ID 映射迟早会变成数据垃圾场。垃圾场的问题不是不能用,而是你用的时候永远不知道踩到的是数据还是雷。

为什么不能直接共享 ID

最直觉的想法是:既然两个系统都要识别用户,为什么不大家都用同一个 ID?

这想法很美好,像很多架构图一样美好。

现实里很难。

第一,系统边界不同。
一个广告交易平台不可能直接使用 DSP 内部用户 ID 作为自己的主键。一个支付平台也不可能把商户系统里的用户 ID 当成自己的用户 ID。

第二,权限边界不同。
你能识别用户,不代表你能把这个识别能力无条件交给别人。尤其是广告、金融、医疗、内容推荐这些领域,ID 本身就是敏感资产。

第三,生命周期不同。
A 系统里的 ID 可能长期稳定,B 系统里的 ID 可能会轮换、过期、清理、重置。强行统一 ID,最后会把两个系统的生命周期耦合在一起。

第四,合规边界不同。
有些 ID 在某个场景可以使用,在另一个场景不能使用。有些数据可以用于统计,不能用于个性化。有些用户同意了 A,不代表同意了 B。

所以更现实的做法不是共享一个全局 ID,而是建立一个受控的映射关系。

local_id + partner + scope -> external_id

这比"大家共用一个 ID"麻烦,但它保留了边界。

当然,统一 ID 并非在所有场景都是错的。同一个组织的内部微服务之间、同一信任域内的 SSO、同一数据平台内的用户画像——这些场景下统一 ID 是合理的,也是更简单的。问题不是统一 ID 本身,而是把统一 ID 的做法不假思索地推到跨信任域、跨组织、跨合规体系的场景里。判断标准不是"能不能统一",而是"这是不是一个信任域"。

这类架构选择看起来是自找麻烦,本质上是为了保留边界。边界平时碍事,出事故时就是防火墙。

在广告投放链路里,Cookie Sync 是跨域匹配非常典型的一种实现。它不是竞价逻辑本身,但会影响后面的频控、受众匹配、重定向、归因和出价判断。

熟悉 OpenRTB 的话会很容易理解这个问题:SSP 或 Exchange 有自己的用户 ID,DSP 也有自己的用户 ID。两边都能识别“自己域名下的用户”,但不能直接读取对方域名下的 cookie。

假设用户访问一个媒体网站。页面里加载了广告请求,SSP 或 Exchange 能在自己的域名下识别这个浏览器,比如:

exchange.com -> ex_uid = E123

DSP 也可能在自己的域名下识别过这个浏览器,比如:

dsp.com -> dsp_uid = D789

问题是浏览器的 cookie 按域隔离:

exchange.com 不能直接读取 dsp.com 的 cookie
dsp.com 也不能直接读取 exchange.com 的 cookie

所以双方不能直接说:

E123 就是 D789

它们需要通过浏览器跳转、像素请求、302 redirect 之类的方式,让浏览器分别访问双方的域名。这样每一方都只能读取自己域名下的 cookie,但通过参数传递,最终建立映射表:

exchange_user_id = E123
dsp_user_id      = D789

之后 Exchange 给 DSP 发 OpenRTB bid request 时,就可以在合适的情况下带上 buyer 侧 ID,比如:

{
  "user": {
    "id": "E123",
    "buyeruid": "D789"
  }
}

这里有一个很重要的点:

user.id 通常是供应侧、Exchange 或 SSP 的用户 ID。
buyeruid 才更接近 DSP 自己的用户 ID。

如果 DSP 把 user.id 直接当成自己的用户 ID 用,那就是典型的字段名驱动开发。字段名看着像,语义完全不是一回事。后面频控、归因、人群匹配全都会乱。

不要先写 match table,再说后面可以删。
在隐私相关系统里,"先污染,后治理"通常会把工程债升级成合规风险。技术债影响的是维护成本,合规风险影响的是业务能否继续使用这套数据。

账号绑定也是跨域匹配

把视角从广告拿出来,账号绑定其实也是同一个问题。

比如一个用户用 GitHub 登录你的系统:

你的系统 user_id = U1001
GitHub user_id = G7788

用户点击“使用 GitHub 登录”之后,你通过 OAuth 拿到 GitHub 的用户身份,然后在本地建立绑定关系:

provider = github
provider_user_id = G7788
local_user_id = U1001

这就是跨域匹配。

它和 Cookie Sync 的区别只是流程更“文明”一点:

  • 用户显式点击登录
  • OAuth 提供标准授权流程
  • provider 返回身份信息
  • 本地系统落绑定关系

但本质没变。

你不能把 GitHub 的用户 ID 当成本地用户 ID。
你也不能假设同一个邮箱就一定是同一个人。
你更不能在用户没有确认的情况下,把多个身份来源随便合并。

账号绑定里最容易出问题的是“合并策略”。

比如:

Google 登录邮箱 = a@example.com
GitHub 登录邮箱 = a@example.com

能不能自动合并?

默认不建议。

因为邮箱是否已验证、provider 是否可信、用户是否实际控制这个邮箱、历史账号里是否已有数据,这些都会影响安全边界。账号体系不是做字符串 join。把两个账号合错,比不合并严重得多。

不合并只是用户体验差。
合错了就是数据越权。

这也是跨域匹配里非常关键的一条原则:

匹配关系一旦会影响权限、资产、身份,就必须保守。

广告里的匹配错了,可能导致频控和投放效果变差。
账号里的匹配错了,可能直接把一个人的数据给另一个人看。

两个都糟糕,但糟糕程度不是一个量级。

支付回调也是跨域匹配

支付系统里也有类似问题。

你的订单系统里有:

order_id = O123
user_id = U1001

支付渠道里可能有:

payment_id = P999
transaction_id = T888

你创建支付单时,需要把本地订单和支付渠道订单关联起来:

local_order_id = O123
provider_payment_id = P999

支付成功后,支付平台通过 webhook 回调你:

{
  "payment_id": "P999",
  "status": "success",
  "amount": 10000
}

你不能看到 P999 就直接给用户加余额。你要做的是:

  1. 找到本地映射关系
  2. 校验签名
  3. 校验金额
  4. 校验币种
  5. 校验订单状态
  6. 做幂等处理
  7. 再更新本地订单

这里的匹配不是用户匹配,而是订单匹配。
但底层模式还是一样:

external_id -> local_id

并且这类场景比广告更不能随便。

广告系统里一个 ID 匹配错了,可能导致投放和归因数据变脏。支付系统里一个 ID 匹配错了,影响的是资金、对账和审计,风险级别完全不同。

转化归因里的匹配更微妙

转化归因也是跨域匹配。

广告曝光发生在一个系统:

impression_id = I123
user_id = U1
campaign_id = C1

用户后来在广告主网站转化:

conversion_id = CV999
order_id = O888

归因系统要判断:

这次 conversion 是否应该归因到之前某次 impression/click?

这里的匹配不再只是 ID 等值匹配。它还会涉及:

  • 用户 ID 是否一致
  • 点击 ID 是否存在
  • 时间窗口是否满足
  • campaign 是否一致
  • 设备是否一致
  • 是否跨设备
  • 是否 view-through
  • 是否 click-through
  • 是否有多个广告触点
  • 归因模型是 last click 还是其他规则

这说明跨域匹配不是只有一种强度。

可以粗略分成三类:

类型 例子 特点
确定性匹配 OAuth 账号绑定、支付订单映射 有明确授权或强校验
半确定性匹配 Cookie Sync、点击 ID 归因 依赖 ID、时间窗口、上下文
概率性匹配 跨设备推断、相似行为匹配 有置信度,不能当事实用

概率性匹配尤其要小心。跨设备推断、相似行为匹配这类手段,本质上是"猜测",不是"确认"。猜测在广告投放和增长分析里有用,但不能被下游当作事实来消费——比如不能因为概率匹配认为两个设备属于同一用户,就把一方的个人数据展示给另一方。概率性匹配的结果,下游每多传一层,置信度就应该衰减一层。最危险的做法是把置信度 0.6 的匹配当成确定性匹配塞进同一张表、同一个字段,让后续所有消费者都默认它是事实。

我个人更倾向于把确定性匹配和概率性匹配在数据结构上明确分开。不要都塞进一个 matched_user_id 字段里。短期省字段,长期毁排查。

一个比较清楚的结构应该包含:

match_type      = deterministic / probabilistic
match_source    = oauth / cookie_sync / click_id / device_graph
confidence      = 1.0 / 0.82 / ...
matched_at      = ...
expires_at      = ...

工程上最怕的是"字段看起来简单,语义被塞爆"。
一个字段承担五种含义,最后所有人都说"历史原因"。这种历史原因通常意味着当年的数据建模没有把来源、强度和生命周期表达清楚。

跨域匹配的核心模型

不管场景怎么变化,跨域匹配都可以抽象成这个模型:

subject in domain A
        |
        |  evidence / protocol / consent / verification
        v
subject in domain B

落到数据结构上,大概是:

CREATE TABLE identity_match (
    domain_a          VARCHAR(64)  NOT NULL,
    id_a              VARCHAR(256) NOT NULL,
    domain_b          VARCHAR(64)  NOT NULL,
    id_b              VARCHAR(256) NOT NULL,

    match_type        VARCHAR(32)  NOT NULL,
    match_source      VARCHAR(64)  NOT NULL,
    confidence        DECIMAL(5,4) NULL,

    consent_scope     VARCHAR(128) NULL,
    evidence_id       VARCHAR(256) NULL,

    created_at        TIMESTAMP    NOT NULL,
    updated_at        TIMESTAMP    NOT NULL,
    expires_at        TIMESTAMP    NULL,

    PRIMARY KEY (domain_a, id_a, domain_b, id_b)
);

这不是说所有系统都应该建这么一张通用表。
通用 identity graph 很容易变成过度设计,尤其是在业务还没复杂到那个程度时。什么时候值得考虑升级?几个信号:你发现自己同时在维护三套以上的专用匹配表;跨场景的匹配查询开始需要多表 join;或者不同业务线开始各自建匹配逻辑、彼此不一致。出现这些信号之前,专用映射表就够了。

但这个模型能提醒自己:匹配关系不是一个裸 ID,它至少有来源、范围、强度和生命周期。

在具体系统里,可以根据场景缩小:

广告匹配:

partner_id
partner_user_id
dsp_user_id
source
expires_at

OAuth 绑定:

provider
provider_user_id
local_user_id
verified_email
bound_at

支付订单:

provider
provider_payment_id
local_order_id
status
signature_verified

转化归因:

conversion_id
touchpoint_id
match_type
attribution_window
attributed_at

重点不是表怎么设计得漂亮,而是不要丢掉语义。

什么时候不应该匹配

不是所有能匹配的东西都应该匹配。

至少有几种情况要明确拒绝:

1. 没有合法授权或用户同意

涉及用户身份、行为、广告、设备标识时,如果没有对应授权,不应该做匹配。

尤其是广告场景,不能因为技术上可以发 sync pixel,就默认可以同步用户。Cookie Sync 本质上处理的是在线标识符,它不应该绕过 privacy gate。

2. 匹配结果会扩大权限

如果一次匹配会让用户看到更多数据、获得更多权限、访问更多资产,那必须走更强校验。

比如账号自动合并、企业账号绑定、支付账户绑定,都不能只靠邮箱或者昵称这种弱信号。

3. ID 来源不可信

第三方传来的 ID,如果没有签名、没有来源校验、没有白名单,不要直接写入核心映射表。

开放式回调、开放式 redirect、随便接受 partner 参数,这些都是事故入口。第三方 ID 进入核心映射表之前,必须先通过来源校验和权限边界。

4. 无法解释匹配依据

如果线上出了问题,你回答不了:

这个 A 为什么会匹配到这个 B?
什么时候匹配的?
谁触发的?
依据是什么?
还能不能撤销?

那这套匹配系统就是不可运维的。

不可运维的系统,迟早会把"偶发问题"升级成"全员排查"。

匹配系统的几个工程原则

第一,ID 要带命名空间

不要只存:

user_id = 123

要知道它是谁的 ID:

domain = exchange_a
user_id = 123

否则两个系统都生成了 123,你就获得了一次免费的数据串线体验。

第二,映射关系要有方向感

有些映射是对称的:

A <-> B

有些映射不是:

external_id -> local_id

比如支付回调里,外部 payment ID 只能映射到本地订单,不代表本地订单可以无条件反推出所有外部状态。

广告里也类似。Exchange 的 user.id 映射到 DSP user ID,不代表这个 ID 可以被拿去其他 partner 场景复用。

第三,匹配要有生命周期

Cookie 会过期,token 会过期,consent 会变化,设备会重置,用户会解绑账号。

所以映射关系也应该过期。

永不过期的匹配表很诱人,因为省事。但省事通常只是把问题存在未来。未来不会感谢你,它只会把那张表膨胀到没人敢动。

第四,匹配要可撤销

用户解绑 GitHub,映射关系要删除或失效。
用户撤回 consent,广告 ID 映射要停止使用。
支付订单关闭,不能继续接受后续成功回调直接改状态。

跨域匹配不是只负责建立关系,还要负责解除关系。

第五,主链路不能依赖慢匹配

在 RTB 这种低延迟场景里,bid request 进来之后再跨库、跨区、跨服务慢慢找 ID,不现实。

匹配关系应该提前建立,主链路只做低延迟查询。

这点不只适用于广告。支付回调、登录鉴权、权限判断也是一样。核心链路里每增加一个远程依赖,系统就多一个不可用来源。低延迟主链路应该尽量依赖本地索引、缓存或已预计算的映射结果。

第六,必须有观测能力

至少要知道:

match request 数量
match 成功率
match 失败原因
privacy block 比例
映射写入成功率
映射查询命中率
过期清理数量
partner 维度异常

没有这些指标,跨域匹配系统出问题时只能靠猜。靠猜不是工程方法。

我现在对跨域匹配的理解

这个领域不是停滞的。隐私增强匹配技术——Private Set Intersection (PSI)、Google PAIR、数据洁净室(data clean room)——正在改变匹配的技术手段,从"把 ID 传来传去"转向"在各自域内计算交集而不暴露原始 ID"。这些方案不是银弹,但它们指向同一个方向:匹配和隐私不是对立的,前提是系统设计必须正视授权、边界和可审计性。

跨域匹配本质上不是"同步数据"。

它更像是在两个边界之间建立一条有条件的、可解释的、可撤销的对应关系。

这里面最重要的不是 ID,而是边界。

没有边界,匹配就会变成数据混用。
没有来源,匹配就会变成黑盒。
没有过期,匹配就会变成污染。
没有合规,匹配就会变成风险。
没有观测,匹配就会变成玄学。

Cookie Sync 只是这个问题在广告系统里的一个典型表现。它把浏览器同源策略、广告交易、用户识别、隐私合规、低延迟系统和数据建模揉在了一起,所以看起来复杂。

但把它拆开之后,它和很多工程问题是相通的:

我是谁?
你是谁?
我们怎么证明这两个身份有关?
这个关系能用在哪里?
什么时候失效?
出了问题怎么解释?

能回答这些问题,才算真的理解了跨域匹配。

否则只是会拼几个 redirect URL。那只能说明你知道流程形态,还不代表理解了这套机制的边界和风险。

这篇文章讨论的是工程设计原则,不替代具体法域下的法律、隐私或合规判断。涉及用户身份、广告标识、支付资产和跨组织数据合作时,最终方案仍然需要结合业务场景、合同约束和当地法规单独评估。

参考资料