<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>集合对账 - tag - 沐木</title><link>https://oldletter.cn/tags/%E9%9B%86%E5%90%88%E5%AF%B9%E8%B4%A6/</link><description>集合对账 - tag - 沐木</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sun, 19 Jul 2026 01:00:00 +0800</lastBuildDate><atom:link href="https://oldletter.cn/tags/%E9%9B%86%E5%90%88%E5%AF%B9%E8%B4%A6/" rel="self" type="application/rss+xml"/><item><title>千万级数据同步与 ETL 实战（五）：桶摘要、Merkle Tree 与 IBLT 对账</title><link>https://oldletter.cn/posts/data-sync-etl-05-merkle-iblt/</link><pubDate>Sun, 19 Jul 2026 01:00:00 +0800</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/data-sync-etl-05-merkle-iblt/</guid><description><![CDATA[<blockquote>
<p>系列导航：<a href="/posts/data-sync-etl-01-boundaries-and-architecture/" rel="">1</a> | <a href="/posts/data-sync-etl-02-snapshot-watermark/" rel="">2</a> | <a href="/posts/data-sync-etl-03-hash-wheel-fingerprint/" rel="">3</a> | <a href="/posts/data-sync-etl-04-micro-batch-correctness/" rel="">4</a> | <a href="/posts/data-sync-etl-05-merkle-iblt/" rel="">5</a> | <a href="/posts/data-sync-etl-06-true-streaming/" rel="">6</a></p></blockquote>
<p>两个节点各保存 1000 万条记录，直接交换全部身份键和行指纹可以得到精确差异，但通信量和排序成本很高。若双方已经维护稳定分桶和本地指纹状态，可以先交换小摘要，只对不一致区域继续下钻。差异条目很少时，还可以用 IBLT 直接恢复差集。</p>
<p>这些结构优化的是对账和差异定位。源端没有日志、版本或预存摘要时，首次生成全量状态仍需读取 $N$ 条记录。任何摘要结构都无法从未读取的数据中推断变化。</p>
<h2 id="一对账对象先固定">一、对账对象先固定</h2>
<p>对账不能直接对任意行对象求 Hash。双方先约定：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">datasetId
</span></span><span class="line"><span class="cl">schemaId
</span></span><span class="line"><span class="cl">canonicalizationVersion
</span></span><span class="line"><span class="cl">identityKeyEncoding
</span></span><span class="line"><span class="cl">rowFingerprintAlgorithm
</span></span><span class="line"><span class="cl">partitionAlgorithmVersion
</span></span><span class="line"><span class="cl">snapshotEpoch 或 comparisonBoundary</span></span></code></pre></div></div>
<p>每条记录映射为集合元素：</p>
<p>$$
x=encode(IdentityKey, RowFingerprint)
$$</p>
<p>如果只比较身份键，只能发现新增和删除，无法发现内容更新。把身份和内容指纹共同编码后，同一键内容变化会表现为删除旧元素并新增新元素。结果解码后再按身份键合并成 UPDATE。</p>
<p>对账边界也要一致。节点 A 对应 10:00 的状态，节点 B 已处理到 10:05，摘要不同并不代表数据损坏。双方应冻结一个可比较 Epoch，或记录各分区的日志位置和 Watermark，在共同边界上生成摘要。</p>
<h2 id="二单个-hash-无法解释差异">二、单个 Hash 无法解释差异</h2>
<p>把所有元素排序后计算一个 SHA-256，可以判断“集合大概率相同”，但摘要不一致时无法定位哪条记录有问题。重新传输全量集合会失去摘要的工程价值。</p>
<p>只使用 XOR 也不够。定义：</p>
<p>$$
X(S)=\bigoplus_{x\in S}h(x)
$$</p>
<p>XOR 可交换、可结合，相同元素能抵消，适合合并分片。不过它丢失了计数信息，重复元素会相互抵消，多组不同输入也可能得到相同结果。对账摘要可组合多个统计量：</p>]]></description></item></channel></rss>