<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Data on Tarragon</title><link>https://tarrragon.github.io/blog/tags/data/</link><description>Recent content in Data on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 06 Oct 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/data/index.xml" rel="self" type="application/rss+xml"/><item><title>Lambda Architecture（批次層與即時層）</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/lambda-architecture/</link><pubDate>Tue, 06 Oct 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/lambda-architecture/</guid><description>&lt;p>Lambda architecture 是把同一份統計分成兩條路徑計算的做法：&lt;strong>批次層&lt;/strong>（batch layer）定期從完整的原始事件重算出正確的數字，&lt;strong>即時層&lt;/strong>（speed layer）在事件發生的當下累加，補上批次層還沒算到的最近那一段。查詢時由合併層（原始定義稱為 serving layer）合併兩者：較早的時段讀批次層，最近的時段讀即時層。批次層的結果是權威，即時層的數字遺失或偏差時，等批次層算到那一段就被取代。它和 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/materialized-view/" data-link-title="Materialized View" data-link-desc="說明預先計算並儲存查詢結果以加速讀取的資料結構">Materialized View&lt;/a> 同樣是預先算好的結果，差別在於 materialized view 只有一份、刷新前讀到的是舊值；兩條路徑的做法用即時層填補刷新之間的空檔。其他中文資料常寫作 Lambda 架構。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>路徑&lt;/th>
 &lt;th>輸入&lt;/th>
 &lt;th>結果的性質&lt;/th>
 &lt;th>常見載體&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>批次層&lt;/td>
 &lt;td>保存期限內的全部原始事件&lt;/td>
 &lt;td>可重算、規則改了可以全部重新算&lt;/td>
 &lt;td>資料庫裡的彙總表、資料倉儲的排程查詢&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>即時層&lt;/td>
 &lt;td>事件發生當下的那一筆&lt;/td>
 &lt;td>只能累加、不能修正，遺失就是少算了&lt;/td>
 &lt;td>Redis 的計數與 HyperLogLog、串流處理的視窗聚合&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>兩條路徑寫兩份計算邏輯，是這個做法主要的維護成本。Kappa architecture 只留一條串流路徑，要重算時從事件記錄的開頭重播；適合事件本身長期保存在可重播的事件記錄裡（例如 Kafka）的系統。把事件長期保存、以重播重建狀態的設計見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/event-sourcing/" data-link-title="Event Sourcing" data-link-desc="說明用 append-only 事件流取代 mutable state 作為正式紀錄的設計模式、需求判斷標準與代價">Event Sourcing&lt;/a>。&lt;/p>
&lt;h2 id="可觀察訊號與例子">可觀察訊號與例子&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>同一個時段兩份數字不一樣&lt;/strong>：即時層只累加原始的點擊，批次層依規則排除機器人之後才計數，同一個小時在即時層看到的數字比較大。介面上要標示即時層的數字是「未分類、尚未定案」，或兩者的口徑一致。&lt;/li>
&lt;li>&lt;strong>最近幾分鐘的數字突然變小&lt;/strong>：即時層的資料庫重啟或切換節點，遺失了最近的累加。等批次層算到那一段就補回來；若批次層也偏低，問題出在原始事件的寫入，不在即時層。&lt;/li>
&lt;li>&lt;strong>切換點的重複或缺漏&lt;/strong>：合併時以「批次層已經完成到哪一個時段」為界，界線以前讀批次層、以後讀即時層。界線用「現在減一小時」這類固定值時，批次層落後的那一段會兩邊都沒有。&lt;/li>
&lt;/ul>
&lt;p>實例：短網址的點擊在轉址時以 Redis 的 &lt;code>HINCRBY&lt;/code> 依連結與小時累加，後台看目前與前一個小時的數字時讀它；更早的時段讀資料庫裡依原始事件重算的小時表。詳見 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/high-frequency-counting/" data-link-title="1.18 高頻計數的寫入與彙總：熱點列的鎖、只新增的事件、可以重跑的彙總與重送的事件" data-link-desc="熱點列更新的吞吐量上限與它的成因、拆成多列／只新增事件／先在記憶體裡合併的差別、只新增事件搬去哪裡的成本、累加與重算在什麼條件下漏算或重複計算、讓彙總排程互斥的建議鎖與連線池的交互，以及重送事件的冪等寫入、推導事實的存放位置，以及 Redis 承擔即時計數、跨小時不重複數、去重與互斥時各自的邊界">1.18 高頻計數的寫入與彙總：熱點列的鎖、只新增的事件、可以重跑的彙總與重送的事件&lt;/a>〈Redis 在計數、去重與互斥裡的位置〉。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>先確認需求方真的需要比批次層更新得更快的數字：批次層每分鐘重算一次就夠用時，即時層只增加一份要維護的邏輯。採用時寫下三件事：哪一層是權威、兩層的界線由什麼決定、兩層的計數口徑有沒有差別以及介面怎麼標示。即時層的資料不需要備份，它的遺失由批次層吸收。&lt;/p></description><content:encoded><![CDATA[<p>Lambda architecture 是把同一份統計分成兩條路徑計算的做法：<strong>批次層</strong>（batch layer）定期從完整的原始事件重算出正確的數字，<strong>即時層</strong>（speed layer）在事件發生的當下累加，補上批次層還沒算到的最近那一段。查詢時由合併層（原始定義稱為 serving layer）合併兩者：較早的時段讀批次層，最近的時段讀即時層。批次層的結果是權威，即時層的數字遺失或偏差時，等批次層算到那一段就被取代。它和 <a href="/blog/backend/knowledge-cards/materialized-view/" data-link-title="Materialized View" data-link-desc="說明預先計算並儲存查詢結果以加速讀取的資料結構">Materialized View</a> 同樣是預先算好的結果，差別在於 materialized view 只有一份、刷新前讀到的是舊值；兩條路徑的做法用即時層填補刷新之間的空檔。其他中文資料常寫作 Lambda 架構。</p>
<h2 id="概念位置">概念位置</h2>
<table>
  <thead>
      <tr>
          <th>路徑</th>
          <th>輸入</th>
          <th>結果的性質</th>
          <th>常見載體</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>批次層</td>
          <td>保存期限內的全部原始事件</td>
          <td>可重算、規則改了可以全部重新算</td>
          <td>資料庫裡的彙總表、資料倉儲的排程查詢</td>
      </tr>
      <tr>
          <td>即時層</td>
          <td>事件發生當下的那一筆</td>
          <td>只能累加、不能修正，遺失就是少算了</td>
          <td>Redis 的計數與 HyperLogLog、串流處理的視窗聚合</td>
      </tr>
  </tbody>
</table>
<p>兩條路徑寫兩份計算邏輯，是這個做法主要的維護成本。Kappa architecture 只留一條串流路徑，要重算時從事件記錄的開頭重播；適合事件本身長期保存在可重播的事件記錄裡（例如 Kafka）的系統。把事件長期保存、以重播重建狀態的設計見 <a href="/blog/backend/knowledge-cards/event-sourcing/" data-link-title="Event Sourcing" data-link-desc="說明用 append-only 事件流取代 mutable state 作為正式紀錄的設計模式、需求判斷標準與代價">Event Sourcing</a>。</p>
<h2 id="可觀察訊號與例子">可觀察訊號與例子</h2>
<ul>
<li><strong>同一個時段兩份數字不一樣</strong>：即時層只累加原始的點擊，批次層依規則排除機器人之後才計數，同一個小時在即時層看到的數字比較大。介面上要標示即時層的數字是「未分類、尚未定案」，或兩者的口徑一致。</li>
<li><strong>最近幾分鐘的數字突然變小</strong>：即時層的資料庫重啟或切換節點，遺失了最近的累加。等批次層算到那一段就補回來；若批次層也偏低，問題出在原始事件的寫入，不在即時層。</li>
<li><strong>切換點的重複或缺漏</strong>：合併時以「批次層已經完成到哪一個時段」為界，界線以前讀批次層、以後讀即時層。界線用「現在減一小時」這類固定值時，批次層落後的那一段會兩邊都沒有。</li>
</ul>
<p>實例：短網址的點擊在轉址時以 Redis 的 <code>HINCRBY</code> 依連結與小時累加，後台看目前與前一個小時的數字時讀它；更早的時段讀資料庫裡依原始事件重算的小時表。詳見 <a href="/blog/backend/01-database/high-frequency-counting/" data-link-title="1.18 高頻計數的寫入與彙總：熱點列的鎖、只新增的事件、可以重跑的彙總與重送的事件" data-link-desc="熱點列更新的吞吐量上限與它的成因、拆成多列／只新增事件／先在記憶體裡合併的差別、只新增事件搬去哪裡的成本、累加與重算在什麼條件下漏算或重複計算、讓彙總排程互斥的建議鎖與連線池的交互，以及重送事件的冪等寫入、推導事實的存放位置，以及 Redis 承擔即時計數、跨小時不重複數、去重與互斥時各自的邊界">1.18 高頻計數的寫入與彙總：熱點列的鎖、只新增的事件、可以重跑的彙總與重送的事件</a>〈Redis 在計數、去重與互斥裡的位置〉。</p>
<h2 id="設計責任">設計責任</h2>
<p>先確認需求方真的需要比批次層更新得更快的數字：批次層每分鐘重算一次就夠用時，即時層只增加一份要維護的邏輯。採用時寫下三件事：哪一層是權威、兩層的界線由什麼決定、兩層的計數口徑有沒有差別以及介面怎麼標示。即時層的資料不需要備份，它的遺失由批次層吸收。</p>
]]></content:encoded></item></channel></rss>