<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>系统架构 - tag - 沐木</title><link>https://oldletter.cn/tags/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/</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/%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84/" rel="self" type="application/rss+xml"/><item><title>千万级数据同步与 ETL 实战（一）：边界、降级策略与整体架构</title><link>https://oldletter.cn/posts/data-sync-etl-01-boundaries-and-architecture/</link><pubDate>Sun, 19 Jul 2026 01:00:00 +0800</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/data-sync-etl-01-boundaries-and-architecture/</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>数据同步项目开工时，团队常先讨论 Kafka、Flink 或 Debezium。这个顺序容易忽略一个基础问题：源系统究竟能提供什么。很多企业数据源只有只读账号、分页接口或定期投递的文件。数据库日志权限拿不到，表里也未必有可靠的更新时间。此时直接套用 CDC 架构，实施阶段会在权限、版本、网络和运维能力上不断返工。</p>
<p>本文先给系统划定边界。目标数据集以单表或单集合不超过 1000 万行为基准，变更速率不高，业务接受几十秒到数小时的延迟。系统需要发现新增、修改和删除，支持断点恢复，并限制对源库的影响。每秒数十万事件、复杂事件时间计算、强实时交易链路不在这套微批架构的处理范围内，第六篇会单独讨论真正的流式系统。</p>
<h2 id="一先盘点数据源能力">一、先盘点数据源能力</h2>
<p>同一个“查询数据”接口，能提供的增量能力差别很大。建任务前要探测并固化以下信息：</p>
<table>
  <thead>
      <tr>
          <th>能力</th>
          <th>需要确认的内容</th>
          <th>缺失后的影响</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>变更日志</td>
          <td>Binlog、WAL、LSN、SCN、日志保留时间</td>
          <td>无法按日志位置持续消费</td>
      </tr>
      <tr>
          <td>版本字段</td>
          <td>单调版本号、精度、是否覆盖删除</td>
          <td>只能依赖时间或扫描</td>
      </tr>
      <tr>
          <td>更新时间</td>
          <td>是否有索引、是否随每次修改更新</td>
          <td>历史回改可能漏读</td>
      </tr>
      <tr>
          <td>稳定键</td>
          <td>单主键、复合业务键、键是否会变化</td>
          <td>无法可靠识别同一条记录</td>
      </tr>
      <tr>
          <td>一致性快照</td>
          <td>隔离级别、快照时长、日志衔接点</td>
          <td>首次全量与增量可能断层</td>
      </tr>
      <tr>
          <td>分页方式</td>
          <td>游标、Keyset、页码、排序稳定性</td>
          <td>并发写入时可能漏读或重复</td>
      </tr>
      <tr>
          <td>删除信息</td>
          <td>删除日志、软删字段、墓碑记录</td>
          <td>需要周期扫描才能识别删除</td>
      </tr>
      <tr>
          <td>源端改造</td>
          <td>能否加索引、生成列、触发器</td>
          <td>分桶扫描可能退化成全表计算</td>
      </tr>
  </tbody>
</table>
<p>能力探测结果进入数据集版本，不能只留在部署人员的经验里。任务发布后如果主键、时区或更新时间精度发生变化，旧 Checkpoint 可能失效。调度器需要暂停任务，完成迁移验证后再恢复。</p>
<p>建议把增量策略设计成一条降级链：</p>
<div class="mermaid" id="id-4" data-mermaid-definition="Zmxvd2NoYXJ0IExSCiAgICBBWyLor7vlj5bmupDnq6/og73lipsiXSAtLT4gQnsi5Y&#43;v5raI6LS55Y&#43;Y5pu05pel5b&#43;XIn0KICAgIEIgLS0&#43;fOaYr3wgQ1siQ0RDIC8g5pel5b&#43;X5aKe6YePIl0KICAgIEIgLS0&#43;fOWQpnwgRHsi5pyJ54mI5pys5Y&#43;35oiW5pu05paw5pe26Ze0In0KICAgIEQgLS0&#43;fOaYr3wgRVsiVmVyc2lvbiAvIFdhdGVybWFyayJdCiAgICBEIC0tPnzlkKZ8IEZ7IuaVsOaNruWPqui/veWKoCJ9CiAgICBGIC0tPnzmmK98IEdbIuiHquWinumUruWinumHjyJdCiAgICBGIC0tPnzlkKZ8IEhbIkhhc2ggV2hlZWwg5YiG5qG25omr5o&#43;PIl0KICAgIEggLS0&#43;IElbIuWFqOmHj&#43;aMh&#43;e6ueagoemqjOWFnOW6lSJd">flowchart LR
    A[&#34;读取源端能力&#34;] --&gt; B{&#34;可消费变更日志&#34;}
    B --&gt;|是| C[&#34;CDC / 日志增量&#34;]
    B --&gt;|否| D{&#34;有版本号或更新时间&#34;}
    D --&gt;|是| E[&#34;Version / Watermark&#34;]
    D --&gt;|否| F{&#34;数据只追加&#34;}
    F --&gt;|是| G[&#34;自增键增量&#34;]
    F --&gt;|否| H[&#34;Hash Wheel 分桶扫描&#34;]
    H --&gt; I[&#34;全量指纹校验兜底&#34;]</div><p>降级需要记录原因。例如数据库支持 Binlog，但账号缺少复制权限，任务可以暂时使用 Watermark，同时持续告警“当前链路无法直接识别物理删除”。若系统只把模式保存为一个枚举，维护人员看不到能力缺口，也无法判断将权限补齐后能获得什么收益。</p>]]></description></item></channel></rss>