<?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>Raw-Log on Tarragon</title><link>https://tarrragon.github.io/blog/tags/raw-log/</link><description>Recent content in Raw-Log on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 06 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/raw-log/index.xml" rel="self" type="application/rss+xml"/><item><title>資料模型與容量邊界</title><link>https://tarrragon.github.io/blog/automation/03-sheet-as-database/data-model-and-capacity/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/automation/03-sheet-as-database/data-model-and-capacity/</guid><description>&lt;p>Sheets 當資料庫的資料模型設計，核心原則是「一列一事件、欄位固定、只 append 不改」。這種形狀叫 raw log（原始紀錄）：每筆瀏覽就是一列、寫進去就不再動它，彙總與分析交給後續的排程另外算。先講為什麼這樣設計，再講這張表能長到多大。&lt;/p>
&lt;h2 id="raw-log-的欄位設計">raw log 的欄位設計&lt;/h2>
&lt;p>raw log 表每一欄對應一個固定的維度，每一列是一筆完整的事件。流量統計的 raw log 大致是：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>時間&lt;/th>
 &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>2026-07-06 20:11:03&lt;/td>
 &lt;td>/posts/foo/&lt;/td>
 &lt;td>google.com&lt;/td>
 &lt;td>zh-TW&lt;/td>
 &lt;td>mobile&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>2026-07-06 20:11:47&lt;/td>
 &lt;td>/automation/&lt;/td>
 &lt;td>&lt;/td>
 &lt;td>en-US&lt;/td>
 &lt;td>desktop&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>三個設計決定讓這張表好用又好彙總。&lt;strong>時間放第一欄、用接收端的伺服器時間&lt;/strong>，讓資料天然按寫入順序排列，也讓「某天的資料」用時間範圍就能篩。&lt;strong>每一欄都是原子維度、不把多個資訊塞進一欄&lt;/strong>（不要把「路徑+裝置」合併成一欄），因為彙總時要能單獨 group by 路徑、或單獨 group by 裝置，欄位分開才做得到。&lt;strong>只 append、不回頭改已寫的列&lt;/strong>，讓這張表成為不可變的事實紀錄——要改的是彙總結果、不是原始 log，原始資料保持乾淨才能隨時重算。&lt;/p>
&lt;p>raw log 直接看沒有意義（幾千列逐筆瀏覽），它的價值是當彙總的原料。把 raw log 依日期與路徑 group 成「每天每篇看幾次」的日報，是&lt;a href="https://tarrragon.github.io/blog/automation/04-triggers-automation/" data-link-title="模組四：觸發器與排程" data-link-desc="用 Apps Script 的時間觸發器把累積的原始瀏覽 log 定時彙總成看得懂的日報，以及觸發器的每日執行配額">模組四：觸發器與排程&lt;/a>的工作。&lt;/p>
&lt;h2 id="sheets-的容量邊界">Sheets 的容量邊界&lt;/h2>
&lt;p>Sheets 是為「人看的試算表」設計的，不是高吞吐資料庫，所以它有兩類上限要放在心上。&lt;/p>
&lt;p>&lt;strong>總 cell 數上限&lt;/strong>：單一試算表有一個總儲存格數量的硬上限（目前以千萬 cell 計，Google 會不定期調整，實際數字以官方為準）。raw log 每列的 cell 數等於欄數，5 欄的話，這個上限換算成列數是相當大的量——對個人 blog 是好幾年的資料，不會很快碰到。但它是有限的，不是無限成長。&lt;/p>
&lt;p>&lt;strong>讀寫效能隨列數下降&lt;/strong>：實務上先浮現的邊界是效能，比硬上限更早遇到。當 raw log 累積到數萬、數十萬列，任何「整表讀進來算」的操作（例如彙總 trigger 每次全表掃描）會越來越慢，可能逼近觸發器的&lt;a href="https://tarrragon.github.io/blog/automation/knowledge-cards/execution-quota/" data-link-title="Execution Quota（執行配額）" data-link-desc="Apps Script 個人帳號的執行時間、同時併發與觸發器每日總時間上限，決定免費膠水層能承受多大的量">單次 6 分鐘執行上限&lt;/a>。&lt;/p>
&lt;h2 id="撐不住的訊號與應對">撐不住的訊號與應對&lt;/h2>
&lt;p>判斷 Sheets 開始撐不住，看這幾個訊號：彙總 trigger 的執行時間逐月變長、逼近 6 分鐘；開啟試算表本身變慢、捲動卡頓；或 cell 數逼近上限的警告。出現這些訊號時，有兩條應對路徑，依情況選：&lt;/p>
&lt;p>&lt;strong>分表&lt;/strong>：把 raw log 按月（或按季）拆成多張工作表，例如 &lt;code>log-2026-07&lt;/code>、&lt;code>log-2026-08&lt;/code>。彙總 trigger 只讀當月表、不必掃全部歷史，執行時間就回到穩定。這是最小改動、繼續留在 Sheets 的做法，適合量成長但還在 Sheets 能力範圍內。&lt;/p>
&lt;p>&lt;strong>遷移&lt;/strong>：當量大到連分表都吃力、或需要更即時的查詢，訊號就指向「該換更重的儲存」——把接收端從 Apps Script 換成 Cloudflare Workers + D1（免費 SQLite），資料庫的查詢能力與吞吐遠高於 Sheets。遷移的完整訊號與路徑在&lt;a href="https://tarrragon.github.io/blog/automation/05-deploy-quota-security/" data-link-title="模組五：部署、配額與安全" data-link-desc="把匿名可存取的 beacon 接收端上線後，怎麼守住免費配額、擋掉濫用、保持資料乾淨、以及判斷何時該換更重的工具">模組五&lt;/a>，選型的取捨在&lt;a href="https://tarrragon.github.io/blog/automation/00-mental-model/free-tier-and-tool-choice/" data-link-title="免費額度的思考方式與工具選型" data-link-desc="判斷免費膠水層撐不撐得住自己流量時該看哪個限制、以及 Apps Script 與 Cloudflare Workers 各自適合什麼場景">模組零&lt;/a>。&lt;/p>
&lt;p>判準用一句話收斂：&lt;strong>Sheets 撐不住的第一訊號通常是彙總變慢、不是 cell 爆掉&lt;/strong>——先分表爭取空間，分表也吃力才談遷移。對絕大多數個人 blog，這些邊界很久才會碰到，先讓簡單版跑著、留意訊號即可。&lt;/p>
&lt;h2 id="事件模型會改變這裡的估算">事件模型會改變這裡的估算&lt;/h2>
&lt;p>上面的估算假設一次瀏覽產生一列。&lt;a href="https://tarrragon.github.io/blog/automation/06-reading-the-data/event-model/" data-link-title="事件模型與停留時間" data-link-desc="每次瀏覽只記一列時看不出讀者停留多久、有沒有真的在讀；補上離開事件之後，那個秒數的語意與既有報表公式的連帶影響">模組六的事件模型&lt;/a>把它改成一進一離兩列，並在 payload 上多加六個欄位——列數接近翻倍、單列的 cell 數從五增為十一，兩者相乘之後容量上限會比這裡算出來的早很多抵達。真要估的話，用那一章的欄位清單重算一次，而不是沿用這一節的數字。&lt;/p>
&lt;h2 id="下一步">下一步&lt;/h2>
&lt;p>資料進來、也知道表能長多大之後，把 raw log 變成看得懂的日報，見&lt;a href="https://tarrragon.github.io/blog/automation/04-triggers-automation/" data-link-title="模組四：觸發器與排程" data-link-desc="用 Apps Script 的時間觸發器把累積的原始瀏覽 log 定時彙總成看得懂的日報，以及觸發器的每日執行配額">模組四：觸發器與排程&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Sheets 當資料庫的資料模型設計，核心原則是「一列一事件、欄位固定、只 append 不改」。這種形狀叫 raw log（原始紀錄）：每筆瀏覽就是一列、寫進去就不再動它，彙總與分析交給後續的排程另外算。先講為什麼這樣設計，再講這張表能長到多大。</p>
<h2 id="raw-log-的欄位設計">raw log 的欄位設計</h2>
<p>raw log 表每一欄對應一個固定的維度，每一列是一筆完整的事件。流量統計的 raw log 大致是：</p>
<table>
  <thead>
      <tr>
          <th>時間</th>
          <th>路徑</th>
          <th>來源</th>
          <th>語言</th>
          <th>裝置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>2026-07-06 20:11:03</td>
          <td>/posts/foo/</td>
          <td>google.com</td>
          <td>zh-TW</td>
          <td>mobile</td>
      </tr>
      <tr>
          <td>2026-07-06 20:11:47</td>
          <td>/automation/</td>
          <td></td>
          <td>en-US</td>
          <td>desktop</td>
      </tr>
  </tbody>
</table>
<p>三個設計決定讓這張表好用又好彙總。<strong>時間放第一欄、用接收端的伺服器時間</strong>，讓資料天然按寫入順序排列，也讓「某天的資料」用時間範圍就能篩。<strong>每一欄都是原子維度、不把多個資訊塞進一欄</strong>（不要把「路徑+裝置」合併成一欄），因為彙總時要能單獨 group by 路徑、或單獨 group by 裝置，欄位分開才做得到。<strong>只 append、不回頭改已寫的列</strong>，讓這張表成為不可變的事實紀錄——要改的是彙總結果、不是原始 log，原始資料保持乾淨才能隨時重算。</p>
<p>raw log 直接看沒有意義（幾千列逐筆瀏覽），它的價值是當彙總的原料。把 raw log 依日期與路徑 group 成「每天每篇看幾次」的日報，是<a href="/blog/automation/04-triggers-automation/" data-link-title="模組四：觸發器與排程" data-link-desc="用 Apps Script 的時間觸發器把累積的原始瀏覽 log 定時彙總成看得懂的日報，以及觸發器的每日執行配額">模組四：觸發器與排程</a>的工作。</p>
<h2 id="sheets-的容量邊界">Sheets 的容量邊界</h2>
<p>Sheets 是為「人看的試算表」設計的，不是高吞吐資料庫，所以它有兩類上限要放在心上。</p>
<p><strong>總 cell 數上限</strong>：單一試算表有一個總儲存格數量的硬上限（目前以千萬 cell 計，Google 會不定期調整，實際數字以官方為準）。raw log 每列的 cell 數等於欄數，5 欄的話，這個上限換算成列數是相當大的量——對個人 blog 是好幾年的資料，不會很快碰到。但它是有限的，不是無限成長。</p>
<p><strong>讀寫效能隨列數下降</strong>：實務上先浮現的邊界是效能，比硬上限更早遇到。當 raw log 累積到數萬、數十萬列，任何「整表讀進來算」的操作（例如彙總 trigger 每次全表掃描）會越來越慢，可能逼近觸發器的<a href="/blog/automation/knowledge-cards/execution-quota/" data-link-title="Execution Quota（執行配額）" data-link-desc="Apps Script 個人帳號的執行時間、同時併發與觸發器每日總時間上限，決定免費膠水層能承受多大的量">單次 6 分鐘執行上限</a>。</p>
<h2 id="撐不住的訊號與應對">撐不住的訊號與應對</h2>
<p>判斷 Sheets 開始撐不住，看這幾個訊號：彙總 trigger 的執行時間逐月變長、逼近 6 分鐘；開啟試算表本身變慢、捲動卡頓；或 cell 數逼近上限的警告。出現這些訊號時，有兩條應對路徑，依情況選：</p>
<p><strong>分表</strong>：把 raw log 按月（或按季）拆成多張工作表，例如 <code>log-2026-07</code>、<code>log-2026-08</code>。彙總 trigger 只讀當月表、不必掃全部歷史，執行時間就回到穩定。這是最小改動、繼續留在 Sheets 的做法，適合量成長但還在 Sheets 能力範圍內。</p>
<p><strong>遷移</strong>：當量大到連分表都吃力、或需要更即時的查詢，訊號就指向「該換更重的儲存」——把接收端從 Apps Script 換成 Cloudflare Workers + D1（免費 SQLite），資料庫的查詢能力與吞吐遠高於 Sheets。遷移的完整訊號與路徑在<a href="/blog/automation/05-deploy-quota-security/" data-link-title="模組五：部署、配額與安全" data-link-desc="把匿名可存取的 beacon 接收端上線後，怎麼守住免費配額、擋掉濫用、保持資料乾淨、以及判斷何時該換更重的工具">模組五</a>，選型的取捨在<a href="/blog/automation/00-mental-model/free-tier-and-tool-choice/" data-link-title="免費額度的思考方式與工具選型" data-link-desc="判斷免費膠水層撐不撐得住自己流量時該看哪個限制、以及 Apps Script 與 Cloudflare Workers 各自適合什麼場景">模組零</a>。</p>
<p>判準用一句話收斂：<strong>Sheets 撐不住的第一訊號通常是彙總變慢、不是 cell 爆掉</strong>——先分表爭取空間，分表也吃力才談遷移。對絕大多數個人 blog，這些邊界很久才會碰到，先讓簡單版跑著、留意訊號即可。</p>
<h2 id="事件模型會改變這裡的估算">事件模型會改變這裡的估算</h2>
<p>上面的估算假設一次瀏覽產生一列。<a href="/blog/automation/06-reading-the-data/event-model/" data-link-title="事件模型與停留時間" data-link-desc="每次瀏覽只記一列時看不出讀者停留多久、有沒有真的在讀；補上離開事件之後，那個秒數的語意與既有報表公式的連帶影響">模組六的事件模型</a>把它改成一進一離兩列，並在 payload 上多加六個欄位——列數接近翻倍、單列的 cell 數從五增為十一，兩者相乘之後容量上限會比這裡算出來的早很多抵達。真要估的話，用那一章的欄位清單重算一次，而不是沿用這一節的數字。</p>
<h2 id="下一步">下一步</h2>
<p>資料進來、也知道表能長多大之後，把 raw log 變成看得懂的日報，見<a href="/blog/automation/04-triggers-automation/" data-link-title="模組四：觸發器與排程" data-link-desc="用 Apps Script 的時間觸發器把累積的原始瀏覽 log 定時彙總成看得懂的日報，以及觸發器的每日執行配額">模組四：觸發器與排程</a>。</p>
]]></content:encoded></item></channel></rss>