<?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>Slow-Log on Tarragon</title><link>https://tarrragon.github.io/blog/tags/slow-log/</link><description>Recent content in Slow-Log on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 27 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/slow-log/index.xml" rel="self" type="application/rss+xml"/><item><title>1.14 Production Slow Log Closed Loop</title><link>https://tarrragon.github.io/blog/backend/01-database/production-slow-log-loop/</link><pubDate>Wed, 27 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/production-slow-log-loop/</guid><description>&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/01-database/query-anti-patterns/" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&amp;#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 應用層查詢反模式&lt;/a> 列出了 query 反模式清單跟每請求預算、但沒覆蓋一件事：&lt;strong>production slow log 怎麼從「事故時才看」變成「定期審視能 catch 反模式」&lt;/strong>。本章把 slow log 包成 closed loop — 採集、分析、PR review 整合、regression 偵測四個動作串起來、讓反模式在進 production 之前就被攔下。&lt;/p>
&lt;h2 id="slow-log-的兩種讀法">Slow log 的兩種讀法&lt;/h2>
&lt;p>多數團隊把 slow log 當「事故診斷工具」— 服務變慢時去翻一下、找出當下的罪魁禍首。這條讀法在事故時有效、但有 systemic 缺陷：所有 catch 到的反模式都已經影響使用者一段時間。&lt;/p>
&lt;p>另一條讀法是把 slow log 當「定期審視訊號」— 每週 / 每 release cycle 抓 slow log top-N、看哪些 query 模式持續存在、哪些是新出現的。這條讀法的關鍵在於「對比基線」、不是「找絕對閾值」。&lt;/p>
&lt;p>兩種讀法的對比決定了 closed loop 的設計方向：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &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;/tr>
 &lt;tr>
 &lt;td>比較對象&lt;/td>
 &lt;td>跟絕對閾值比（query &amp;gt; 1 秒）&lt;/td>
 &lt;td>跟上週 / 上次 release 的 slow log 分布比&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>處理路徑&lt;/td>
 &lt;td>找出 root cause → 立即修&lt;/td>
 &lt;td>收進 PR backlog → 排序 → 規律修&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>介入點&lt;/td>
 &lt;td>事故發生後&lt;/td>
 &lt;td>反模式被引入後、影響使用者前&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>對應角色&lt;/td>
 &lt;td>On-call / SRE&lt;/td>
 &lt;td>整個團隊（每週輪流 review）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>定期審視這條讀法是本章的核心、後續四個動作都環繞它建立。&lt;/p>
&lt;h2 id="loop-第一步採集">Loop 第一步：採集&lt;/h2>
&lt;p>Slow log 採集的設計關鍵是「採集標準要穩定、retention 要夠長」。常見的採集配置選擇：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Threshold 設定&lt;/strong>：MySQL &lt;code>long_query_time&lt;/code>、PostgreSQL &lt;code>log_min_duration_statement&lt;/code> 設多久才記？常見 default 1 秒太寬鬆、會漏掉「200ms-1s」這層慢但累積成大量壓力的 query。建議 100ms 或更低（依 application 需求）。&lt;/li>
&lt;li>&lt;strong>採集對象&lt;/strong>：純 SELECT 慢？還是含 INSERT/UPDATE/DELETE？寫路徑慢通常代表 lock contention 或 transaction 範圍問題、跟讀路徑反模式不同、要分開分析。&lt;/li>
&lt;li>&lt;strong>Retention&lt;/strong>：log 保留多久？至少 30 天（覆蓋一個 sprint）、有資源的話 90 天（覆蓋季度 regression 對比）。雲端 managed DB（RDS / Aurora）的 slow log 通常自動匯出到 CloudWatch / S3、設定 retention policy 而不是依賴 DB instance 本身的 log。&lt;/li>
&lt;li>&lt;strong>Sample rate&lt;/strong>：高流量服務全採會把 disk I/O 拖垮。Production 環境用 sampling（如 10% 取樣）平衡採集完整度跟系統壓力。&lt;/li>
&lt;/ul>
&lt;p>採集出來的 raw log 不適合直接讀、要先 normalize。&lt;/p>
&lt;h2 id="loop-第二步normalize-與聚合">Loop 第二步：Normalize 與聚合&lt;/h2>
&lt;p>Raw slow log 每筆都帶具體參數（&lt;code>WHERE user_id = 12345&lt;/code>、&lt;code>WHERE user_id = 67890&lt;/code>），直接看會看到上千筆「不同 query」。實際上多數是同一個 query template 的不同參數實例。&lt;/p>
&lt;p>Normalize 動作把參數抽掉、留 query shape：&lt;/p>
&lt;ul>
&lt;li>&lt;code>WHERE user_id = 12345&lt;/code> → &lt;code>WHERE user_id = ?&lt;/code>&lt;/li>
&lt;li>&lt;code>IN (1, 2, 3, 4, 5)&lt;/code> → &lt;code>IN (?)&lt;/code>&lt;/li>
&lt;li>字串常數同樣抽掉&lt;/li>
&lt;/ul>
&lt;p>工具上：MySQL 用 &lt;code>pt-query-digest&lt;/code>（Percona Toolkit）；PostgreSQL 用 &lt;code>pg_stat_statements&lt;/code> extension（已內建 normalize）；雲端用 vendor 工具（AWS Performance Insights、GCP Query Insights、Azure SQL Insights）。Normalize 後可以按 query shape 聚合、看哪些 shape 累計時間最長、出現次數最多、平均延遲最高。&lt;/p></description><content:encoded><![CDATA[<p><a href="/blog/backend/01-database/query-anti-patterns/" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 應用層查詢反模式</a> 列出了 query 反模式清單跟每請求預算、但沒覆蓋一件事：<strong>production slow log 怎麼從「事故時才看」變成「定期審視能 catch 反模式」</strong>。本章把 slow log 包成 closed loop — 採集、分析、PR review 整合、regression 偵測四個動作串起來、讓反模式在進 production 之前就被攔下。</p>
<h2 id="slow-log-的兩種讀法">Slow log 的兩種讀法</h2>
<p>多數團隊把 slow log 當「事故診斷工具」— 服務變慢時去翻一下、找出當下的罪魁禍首。這條讀法在事故時有效、但有 systemic 缺陷：所有 catch 到的反模式都已經影響使用者一段時間。</p>
<p>另一條讀法是把 slow log 當「定期審視訊號」— 每週 / 每 release cycle 抓 slow log top-N、看哪些 query 模式持續存在、哪些是新出現的。這條讀法的關鍵在於「對比基線」、不是「找絕對閾值」。</p>
<p>兩種讀法的對比決定了 closed loop 的設計方向：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>事故診斷工具</th>
          <th>定期審視訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>觸發時機</td>
          <td>服務變慢時被動翻</td>
          <td>排程定期掃</td>
      </tr>
      <tr>
          <td>比較對象</td>
          <td>跟絕對閾值比（query &gt; 1 秒）</td>
          <td>跟上週 / 上次 release 的 slow log 分布比</td>
      </tr>
      <tr>
          <td>處理路徑</td>
          <td>找出 root cause → 立即修</td>
          <td>收進 PR backlog → 排序 → 規律修</td>
      </tr>
      <tr>
          <td>介入點</td>
          <td>事故發生後</td>
          <td>反模式被引入後、影響使用者前</td>
      </tr>
      <tr>
          <td>對應角色</td>
          <td>On-call / SRE</td>
          <td>整個團隊（每週輪流 review）</td>
      </tr>
  </tbody>
</table>
<p>定期審視這條讀法是本章的核心、後續四個動作都環繞它建立。</p>
<h2 id="loop-第一步採集">Loop 第一步：採集</h2>
<p>Slow log 採集的設計關鍵是「採集標準要穩定、retention 要夠長」。常見的採集配置選擇：</p>
<ul>
<li><strong>Threshold 設定</strong>：MySQL <code>long_query_time</code>、PostgreSQL <code>log_min_duration_statement</code> 設多久才記？常見 default 1 秒太寬鬆、會漏掉「200ms-1s」這層慢但累積成大量壓力的 query。建議 100ms 或更低（依 application 需求）。</li>
<li><strong>採集對象</strong>：純 SELECT 慢？還是含 INSERT/UPDATE/DELETE？寫路徑慢通常代表 lock contention 或 transaction 範圍問題、跟讀路徑反模式不同、要分開分析。</li>
<li><strong>Retention</strong>：log 保留多久？至少 30 天（覆蓋一個 sprint）、有資源的話 90 天（覆蓋季度 regression 對比）。雲端 managed DB（RDS / Aurora）的 slow log 通常自動匯出到 CloudWatch / S3、設定 retention policy 而不是依賴 DB instance 本身的 log。</li>
<li><strong>Sample rate</strong>：高流量服務全採會把 disk I/O 拖垮。Production 環境用 sampling（如 10% 取樣）平衡採集完整度跟系統壓力。</li>
</ul>
<p>採集出來的 raw log 不適合直接讀、要先 normalize。</p>
<h2 id="loop-第二步normalize-與聚合">Loop 第二步：Normalize 與聚合</h2>
<p>Raw slow log 每筆都帶具體參數（<code>WHERE user_id = 12345</code>、<code>WHERE user_id = 67890</code>），直接看會看到上千筆「不同 query」。實際上多數是同一個 query template 的不同參數實例。</p>
<p>Normalize 動作把參數抽掉、留 query shape：</p>
<ul>
<li><code>WHERE user_id = 12345</code> → <code>WHERE user_id = ?</code></li>
<li><code>IN (1, 2, 3, 4, 5)</code> → <code>IN (?)</code></li>
<li>字串常數同樣抽掉</li>
</ul>
<p>工具上：MySQL 用 <code>pt-query-digest</code>（Percona Toolkit）；PostgreSQL 用 <code>pg_stat_statements</code> extension（已內建 normalize）；雲端用 vendor 工具（AWS Performance Insights、GCP Query Insights、Azure SQL Insights）。Normalize 後可以按 query shape 聚合、看哪些 shape 累計時間最長、出現次數最多、平均延遲最高。</p>
<p>聚合後產出三條訊號：</p>
<ol>
<li><strong>Top-N by total time</strong>：累計時間最長的 query — 改一條就能省最多 DB 壓力</li>
<li><strong>Top-N by count</strong>：出現次數最多的 query — 改一條就能降最多 connection 占用</li>
<li><strong>Top-N by avg latency</strong>：平均延遲最高的 query — 個別 request 體驗最差的</li>
</ol>
<p>三條訊號可能指向不同 query、各自值得 attention。</p>
<h2 id="loop-第三步pr-review-整合">Loop 第三步：PR review 整合</h2>
<p>把 slow log 的 top-N 帶回 PR review 是 closed loop 的關鍵。常見三種整合機制：</p>
<ul>
<li><strong>每週 slow log review 會議</strong>：固定時段（每週 30 分鐘）、團隊輪流 owner、把 top-10 過一輪、決定每筆是修 / 留 / 標 acceptable。產出進 backlog、不是當場修。</li>
<li><strong>PR-level query budget check</strong>：CI 加 middleware 統計每個 endpoint 的 query 數（per <a href="/blog/backend/01-database/query-anti-patterns/#%e6%af%8f%e8%ab%8b%e6%b1%82%e7%9a%84-query-%e9%a0%90%e7%ae%97" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 query 預算</a>）、超過閾值的 PR 在 review 時觸發討論。這層比 slow log 早、catch 的是「新引入」反模式。</li>
<li><strong>Production regression alert</strong>：當某個 query shape 的 P99 latency 比上週 baseline 偏高 50%+、自動發 alert 給該服務 owner。這層 catch 的是「漸進惡化」反模式（如資料量增加、index 失效）。</li>
</ul>
<p>三層機制按介入點分層：PR check 是「進 production 前」、weekly review 是「進 production 後的固定盤點」、regression alert 是「漸進惡化的訊號偵測」。三層覆蓋率最高、單跑任一層都會漏。</p>
<h2 id="loop-第四步regression-偵測">Loop 第四步：Regression 偵測</h2>
<p>Slow log 的對比基線需要主動維護。沒有基線、定期審視會退化成「每次都看到同樣的 top-10、習以為常」。建立基線的常見做法：</p>
<ul>
<li><strong>每 release 凍結 baseline</strong>：上線新版本前抓一份 slow log snapshot、release 後跟它比。新增的 query shape 跟惡化的 query shape 都會浮出來。</li>
<li><strong>資料量分位點 marker</strong>：在 schema 加註「這張表預期 1M / 10M / 100M 行的 query 計畫」、實際成長到對應規模時驗證 plan 是否還對。Index 失效常常是「資料量過某個門檻、optimizer 改用 full scan」造成的。</li>
<li><strong>跨 release 趨勢圖</strong>：把 slow log top-10 的累計時間做時序圖、看一年的趨勢。穩定升高代表反模式 / 資料成長壓力、突然升高代表新引入問題。</li>
</ul>
<p>Regression 偵測的 false-positive 風險是「業務本身在變、流量本身在長」、不是反模式造成的。用「query shape 佔比」而非「絕對延遲」當訊號可以降低 false positive — 某個 query shape 從佔 5% 變成佔 30%，不論絕對延遲是否升高、都值得審視。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應動作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Slow log top-10 一直是同一批 query</td>
          <td>Closed loop 沒形成、review 退化成擺設</td>
          <td>啟動 PR-level query budget check 或 weekly review</td>
      </tr>
      <tr>
          <td>某個 query shape 突然從 top-100 升到 top-10</td>
          <td>新版本引入反模式 / 流量結構變化</td>
          <td>對照最近 release diff、找出引入時點</td>
      </tr>
      <tr>
          <td>Top-N 累計時間穩定升高、但 query shape 沒變</td>
          <td>資料量增加、index 退化或 query 計畫漂移</td>
          <td>EXPLAIN 對比、檢查是否該加 covering index 或 partition</td>
      </tr>
      <tr>
          <td>Slow log 異常稀少（&lt; 預期）</td>
          <td>Threshold 設太寬、或採集 sample rate 太低</td>
          <td>降 threshold、提高 sample rate</td>
      </tr>
      <tr>
          <td>同一個 endpoint 在 PR check 過、production 卻爆</td>
          <td>PR 環境資料量太小、CI 無法 catch 大資料量退化</td>
          <td>加 production-like load test、或在 CI 用 anonymized prod data</td>
      </tr>
  </tbody>
</table>
<h2 id="常見誤區">常見誤區</h2>
<p>把 slow log 當「事故工具」、不做定期審視。事故時的 slow log 是 lagging indicator — 反模式已經影響使用者一段時間才被看見。定期審視是把它變成 leading indicator 的關鍵。</p>
<p>把 threshold 設太鬆（1 秒、5 秒）。多數反模式落在 100ms-1s 區間、設 1 秒會漏掉。Threshold 應該對齊「user-perceived 慢」門檻、通常 100-500ms。</p>
<p>把 top-10 當「不能動」。一些 top-10 是業務本質慢（複雜 report、bulk write）、改起來代價遠超效益。Review 時要明示標記「acceptable」、避免下週又被當未解決問題討論。</p>
<h2 id="定位邊界">定位邊界</h2>
<p>本章專注「production slow log 怎麼變成 closed loop」。當問題進入具體反模式分析（這條 query 是哪種反模式？怎麼改？）、回到 <a href="/blog/backend/01-database/query-anti-patterns/" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 應用層查詢反模式</a>；進入 EXPLAIN 解讀細節、回到 <a href="/blog/backend/01-database/schema-design/" data-link-title="1.2 Schema Design 與資料建模" data-link-desc="整理 table、index、key、partition、denormalization 與命名規則">1.2 schema design</a>；進入 application-side query 數量控制機制（ORM middleware、query log 觀察），跨到 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 observability</a> 模組。</p>
<h2 id="案例回寫">案例回寫</h2>
<p>09 案例庫中、slow log closed loop 直接示範的案例稀少（多數案例談規模 / vendor、不談 ops loop 設計）。可用以下案例反向追問：</p>
<ul>
<li><a href="/blog/backend/09-performance-capacity/cases/doordash-cockroachdb-orders-platform/" data-link-title="9.C39 DoorDash：Aurora Postgres 寫入瓶頸 → CockroachDB 多主寫入" data-link-desc="DoorDash 從 Aurora Postgres 遷到 CockroachDB、解 1.6 M QPS 單主寫入瓶頸、外送平台爆量壓力下重做 OLTP 拓樸">9.C39 DoorDash：Aurora Postgres 寫入瓶頸</a> — 寫入飽和被識別為 vendor 層問題、但若 production slow log loop 早期就 catch 到 transaction 範圍跟熱 row 競爭、可能延後遷移時點。對照本章可問：DoorDash 在啟動遷移前、是否有定期 slow log review 機制？</li>
<li><a href="/blog/backend/09-performance-capacity/cases/standard-chartered-aurora-banking/" data-link-title="9.C14 Standard Chartered：受監管銀行的 Aurora 4000 TPS 容量提升" data-link-desc="Standard Chartered 銀行遷移到 Aurora 後吞吐量提升 10 倍至 4000 TPS、跨 7 個受監管市場">9.C14 Standard Chartered：合規驅動容量規劃</a> — 容量規劃以合規為驅動、但 query 預算假設若無 production 驗證、規劃出的 TPS 上限會偏低。對照本章「Regression 偵測」段：合規 cluster 是否有 query shape 趨勢圖？</li>
</ul>
<p>反向追問框架（per <a href="/blog/report/case-misalignment-reverse-inquiry/" data-link-title="案例庫不對齊章節主題時用反向追問取代強掛" data-link-desc="當案例庫主軸跟章節主題不在同一維度時、引用框架要從『正向掛入』切換到『反向追問』；強掛 case 的根因是『想填滿案例段』的模板配額、而非『想讓讀者看到證據』；反向追問把案例庫的限制當 first-class 訊息傳給讀者、case 變成『沒做 X 的後果』的反證、不是 X 的示範；reviewer 第一輪 fact-check 就能抓出強掛、修正成本高；判讀徵兆是引用句寫不出 case 具體段落 / 多個 case 句型雷同 / 章節主題跟 case 庫主軸不同維度">#146</a>）：案例本身不直接示範 closed loop、但用「啟動 vendor 升級前、closed loop 能不能延後撞牆」這條追問、能看出 slow log loop 的事前價值。</p>
<h2 id="跨模組路由">跨模組路由</h2>
<ol>
<li>與 <a href="/blog/backend/01-database/query-anti-patterns/" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 query 反模式</a> 的交接：1.13 給反模式清單、本章給「定期 catch 它們」的機制。</li>
<li>與 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 observability</a> 的交接：slow log 採集跟聚合是 observability 的子問題、跨服務的 query trace 需要 04 的 telemetry pipeline。</li>
<li>與 <a href="/blog/backend/09-performance-capacity/bottleneck-localization/" data-link-title="9.5 瓶頸定位流程" data-link-desc="從 app 到 DB / cache / broker / 第三方 quota 的逐層瓶頸定位">9.5 瓶頸定位</a> 的交接：9.5 用 USE / RED method 定位、本章用 slow log 在 DB 層做更精細的 query-level 定位。</li>
<li>與 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">06 reliability ci-pipeline</a> 的交接：PR-level query budget check 是 CI 環節、屬 06 模組的 release gate 設計。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<p>要看具體反模式怎麼修、回 <a href="/blog/backend/01-database/query-anti-patterns/" data-link-title="1.13 應用層查詢反模式與 Query 預算" data-link-desc="整理 N&#43;1、select *、缺索引、ORM lazy load、long transaction 等查詢反模式與每請求的 query 預算判讀">1.13 應用層查詢反模式</a>。要把 query 觀測接進完整 telemetry pipeline、進 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 observability</a>。要看 PR-level check 怎麼接 release gate、進 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>。</p>
]]></content:encoded></item></channel></rss>