<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>流处理 - tag - 沐木</title><link>https://oldletter.cn/tags/%E6%B5%81%E5%A4%84%E7%90%86/</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/%E6%B5%81%E5%A4%84%E7%90%86/" rel="self" type="application/rss+xml"/><item><title>千万级数据同步与 ETL 实战（六）：CDC、事件时间与真正的流式处理</title><link>https://oldletter.cn/posts/data-sync-etl-06-true-streaming/</link><pubDate>Sun, 19 Jul 2026 01:00:00 +0800</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/data-sync-etl-06-true-streaming/</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>前五篇使用高频 Micro Batch 处理权限受限的数据源。它能做到分钟级同步和故障重放，但事件仍由调度器分批查询。真正的流式链路持续消费无界事件，数据一旦进入日志或消息通道便可推动计算，系统还要处理分区顺序、事件时间、乱序、长时间状态、Barrier 快照和下游事务。</p>
<p>这套架构适合秒级或亚秒级延迟、持续高变化率、窗口聚合、复杂乱序以及需要按日志位置精确恢复的场景。源端只有分页查询、文件投递或低频 API 时，部署 Flink 并不会凭空生成事件流，输入端仍受轮询边界约束。</p>
<h2 id="一流式输入需要可持续的位置">一、流式输入需要可持续的位置</h2>
<p>数据库 CDC 读取提交日志。MySQL 以 Binlog 文件与位置或 GTID 标识进度，PostgreSQL 通过逻辑复制槽和 LSN 暴露变化，Oracle 常使用 SCN 关联日志顺序。连接器将底层记录转换为统一事件，同时保留源端位置：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-json">
        <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-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;source&#34;</span><span class="p">:</span> <span class="s2">&#34;orders&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;partition&#34;</span><span class="p">:</span> <span class="s2">&#34;mysql-cluster-a&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;position&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;gtid&#34;</span><span class="p">:</span> <span class="s2">&#34;synthetic:1-84321&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;transactionId&#34;</span><span class="p">:</span> <span class="s2">&#34;tx-1057&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;operation&#34;</span><span class="p">:</span> <span class="s2">&#34;UPDATE&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;key&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;orderId&#34;</span><span class="p">:</span> <span class="mi">10086</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;before&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;PAID&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;after&#34;</span><span class="p">:</span> <span class="p">{</span><span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;SHIPPED&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;sourceTime&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-07-19T10:01:03.126Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;schemaId&#34;</span><span class="p">:</span> <span class="s2">&#34;orders-v9&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>日志位置承担恢复游标，事务 ID 和事务边界承担一致性语义。数据库事务修改 100 行时，连接器需要保留它们的相对顺序，并明确下游是否按整笔事务可见。将每行直接发往不同分区，吞吐会提高，却可能让下游在短时间内观察到部分事务结果。</p>
<div class="mermaid" id="id-5" data-mermaid-definition="Zmxvd2NoYXJ0IExSCiAgICBEQlsi5pWw5o2u5bqT5o&#43;Q5Lqk5pel5b&#43;XIl0gLS0&#43;IENEQ1siQ0RDIENvbm5lY3RvciJdCiAgICBDREMgLS0&#43;IFRYWyLkuovliqHovrnnlYzkuI4gU2NoZW1hIOino&#43;aekCJdCiAgICBUWCAtLT4gTVFbIuWIhuWMuuS6i&#43;S7tuaXpeW/lyJdCiAgICBNUSAtLT4gU1RbIuacieeKtuaAgea1geiuoeeulyJdCiAgICBTVCAtLT4gU05LWyLkuovliqHmiJbluYLnrYkgU2luayJdCiAgICBTVCA8LS0&#43;IENQWyJDaGVja3BvaW50IENvb3JkaW5hdG9yIl0KICAgIENQIC0tPiBPRkZbIlNvdXJjZSBQb3NpdGlvbiArIFN0YXRlICsgU2luayBDb21taXQiXQ==">flowchart LR
    DB[&#34;数据库提交日志&#34;] --&gt; CDC[&#34;CDC Connector&#34;]
    CDC --&gt; TX[&#34;事务边界与 Schema 解析&#34;]
    TX --&gt; MQ[&#34;分区事件日志&#34;]
    MQ --&gt; ST[&#34;有状态流计算&#34;]
    ST --&gt; SNK[&#34;事务或幂等 Sink&#34;]
    ST &lt;--&gt; CP[&#34;Checkpoint Coordinator&#34;]
    CP --&gt; OFF[&#34;Source Position &#43; State &#43; Sink Commit&#34;]</div><p>日志保留时间必须覆盖停机和恢复窗口。消费任务离线 12 小时，而源端日志只保留 6 小时，旧位置已不可用，只能重新做快照并建立新起点。复制槽也不是免费保险：消费者停滞时，PostgreSQL 可能长期保留 WAL，造成磁盘增长。监控应同时展示消费延迟和源端日志保留压力。</p>]]></description></item></channel></rss>