<?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>Knee-Point on Tarragon</title><link>https://tarrragon.github.io/blog/tags/knee-point/</link><description>Recent content in Knee-Point on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 03 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/knee-point/index.xml" rel="self" type="application/rss+xml"/><item><title>規模拐點判斷</title><link>https://tarrragon.github.io/blog/devops/05-capacity-planning/scaling-inflection-point/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/devops/05-capacity-planning/scaling-inflection-point/</guid><description>&lt;p>什麼訊號代表該擴容、什麼代表可以縮容，規模拐點判斷的根本概念是飽和曲線：系統不是「撐得住」跟「掛掉」的二元，中間有一段可測量的劣化區。負載往上加，系統先在延遲平穩的線性區，過了某個點進入延遲開始上升的膝點區（knee），再往上就是延遲不可預測的懸崖區（cliff）。拐點判斷的核心，是在系統進入膝點區、還沒到懸崖時就讀出訊號，而不是等它掉下懸崖。&lt;/p>
&lt;p>判準先建立在利用率的三段區間上。利用率低於 50% 是線性區，延遲平穩；50% 到 80% 是膝點區，延遲開始上升但仍可接受；超過 80% 是懸崖區，延遲不可預測、可能逾時或引發級聯故障。健康的系統應該運轉在 50% 到 70% 之間，這也是 &lt;a href="https://tarrragon.github.io/blog/devops/05-capacity-planning/peak-estimation/" data-link-title="峰值估算" data-link-desc="要估一個服務的峰值該準備多少時，先分清峰值屬於哪一種形狀、再用歷史倍率階梯與安全係數推算，並知道安全係數的數學根據在飽和曲線">峰值估算&lt;/a> 裡安全係數要覆蓋的那段 headroom。&lt;/p>
&lt;p>這三段區間背後是排隊理論：利用率往高處走，佇列長度增加的幅度越來越陡——50% 到 70% 增加數倍、70% 到 90% 再急遽放大（實際倍數視佇列模型而定，但方向一致是超線性）；延遲跟佇列長度成正比，所以延遲在高利用率區是指數成長。80% 這個懸崖門檻是通用的形狀，膝點的實際位置隨系統類型變——無狀態服務的膝點大約在 80% CPU、有鎖競爭的資料庫大約 60%、吃磁碟 I/O 的佇列服務可能 50% 就到。這就是為什麼安全係數不能對所有系統套同一個數：有狀態的關鍵系統膝點來得早，要留的 headroom 比無狀態服務厚。判斷一個系統該在多少利用率就準備擴容，要看它是哪一類、膝點在哪，不是一律套 80%。&lt;/p>
&lt;h2 id="膝點的早期訊號p99-先於平均劣化">膝點的早期訊號：p99 先於平均劣化&lt;/h2>
&lt;p>膝點的價值在於它是「該擴容」的早期訊號，但它只在對的指標上看得見。四個訊號共同指向系統正在進入膝點區：吞吐量從線性成長轉為次線性（加壓上去、吞吐不再等比增加）、p50 還穩但 p99 與 p999 開始飆、佇列開始堆積（不只是利用率上升）、而錯誤率仍接近零。最後這條很關鍵——膝點區的系統還沒開始報錯，只看錯誤率會以為一切正常，要看尾延遲才抓得到。&lt;/p>
&lt;p>尾延遲的敏感度是這裡的判讀核心。p50 對垃圾回收暫停、重試風暴、長尾都不敏感，純看平均或 p50 會誤判「飽和還沒到」；p99 對連線競爭敏感，能較早看到飽和的苗頭；p999 對垃圾回收的 stop-the-world、leader 選舉、重試風暴最敏感。看容量該不該擴，看的是 p99 跟 p999 的曲線有沒有開始上翹，不是平均值。等到吞吐量開始下降、p99 變成逾時、錯誤率飆升、重試風暴出現，那已經是掉進懸崖區了，擴容是在救火而不是預防。&lt;/p>
&lt;h2 id="用-ramp-up-找膝點不用單點壓測">用 ramp-up 找膝點，不用單點壓測&lt;/h2>
&lt;p>膝點的位置要靠階梯加壓（ramp-up）找，不能用固定的單一壓力值測。按 1 倍、2 倍、4 倍、8 倍逐級加壓，每一級維持 5 到 10 分鐘看穩態行為，才能看出曲線在哪個負載開始上翹。一個「2000 RPS 撐在 100 毫秒」的單點結果毫無定位價值——它可能離膝點還很遠、也可能已經在懸崖邊，單點測不出來。用哪個工具做這種階梯加壓、怎麼讀每一級的輸出，見 &lt;a href="https://tarrragon.github.io/blog/devops/05-capacity-planning/load-testing-tools/" data-link-title="壓力測試工具與方法" data-link-desc="要用壓測驗證容量規劃時，釐清壓測到底在找什麼、k6 與 wrk 各自的定位、怎麼讀壓測輸出的延遲分布，以及讓結果失真的幾個反模式">壓力測試工具與方法&lt;/a>；這裡要確立的是拐點判讀本身：拐點是一條曲線上的位置，要用曲線去找，不是一個點。&lt;/p>
&lt;h2 id="找出哪個資源先飽和">找出哪個資源先飽和&lt;/h2>
&lt;p>系統的容量由最先飽和的那個資源決定，所以每次 ramp-up 要同時盯多個維度，看哪個先到頂：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>CPU&lt;/strong>：看 load average 與 run queue，不只看利用率——利用率 100% 但 run queue 是空的仍撐得住，run queue 開始堆才是真的 CPU 飽和。&lt;/li>
&lt;li>&lt;strong>記憶體&lt;/strong>：垃圾回收暫停、swap、快取驅逐都是隱性的記憶體飽和，不會反映在單純的用量數字上。&lt;/li>
&lt;li>&lt;strong>磁碟 I/O&lt;/strong>：吞吐量、IOPS、佇列深度三個維度分開看——雲端 SSD 通常先撞 IOPS 上限，本機 NVMe 先撞吞吐量上限。&lt;/li>
&lt;li>&lt;strong>網路&lt;/strong>：頻寬、每秒封包數（PPS）、連線數。雲端的 PPS 上限超過時可能是靜默丟包，不報錯。&lt;/li>
&lt;li>&lt;strong>連線池&lt;/strong>：最常見的隱性瓶頸。一個大小 100 的連線池用到 95 就已經接近飽和，但 CPU、記憶體都還很閒——瓶頸不在機器、在池子。&lt;/li>
&lt;li>&lt;strong>外部 API 配額&lt;/strong>：看對方回的 429 錯誤率，這個上限不在自己手上。&lt;/li>
&lt;li>&lt;strong>檔案描述元與連接埠&lt;/strong>：ephemeral port 與 file descriptor 是 OS 層的配額，跟應用的連線池是不同層——連線池還沒滿、但 OS 的 fd 或可用 port 先耗盡，一樣接不了新連線，且症狀（&lt;code>Too many open files&lt;/code>、connect 失敗）容易被誤判成應用問題。&lt;/li>
&lt;/ul>
&lt;p>還有一種隱性飽和不在這六維裡：熱分區（hot partition）。分散式的鍵值或資料庫，名義容量是每個分區上限乘以分區數，但鍵分布不均時，整體利用率可能只有 20%、最熱的那個分區已經 100%，再加流量立刻被限流。訊號是「吞吐量上不去、但平均利用率很低」加上「某些鍵的延遲飆升、出現限流事件」。處理靠複合鍵、寫入分片或用快取吸收熱鍵——這種飽和用整體利用率完全看不到。&lt;/p>
&lt;h2 id="該擴容之後往垂直還是水平">該擴容之後：往垂直還是水平&lt;/h2>
&lt;p>確認要擴之後，下一個判斷是往哪個軸擴。垂直擴展（換更大的機器）不必改程式，但受單機物理上限限制、成本非線性（高階機型有溢價）、且是單點；水平擴展（加更多台）理論上線性，但要求服務無狀態或狀態能同步，且每台都要付基礎成本。經驗法則是無狀態的部分水平擴、有狀態的關鍵節點（如資料庫主節點）垂直擴加讀取副本，兩者常同時進行。&lt;/p>
&lt;p>垂直擴展有兩道牆要知道。第一道是物理上限，但真正的天花板常常比規格更早到——雲端有記憶體達 TiB 級的超大機型，但有狀態的交易型資料庫主節點，真實天花板常卡在 32 到 64 vCPU，因為鎖競爭、context switch、記憶體頻寬這些架構因素，不是規格不夠。第二道是成本曲線：中階機型從 4 到 8 vCPU、8 到 16 vCPU 大約是 1.8 到 1.9 倍（接近線性），但過了 48 vCPU 明顯偏離線性、高階機型溢價更陡。垂直擴到一個點之後，每多一分效能付的錢急遽變貴，那就是該轉水平或該分片的訊號。&lt;/p>
&lt;p>水平擴展的隱性成本則在協調與連線放大。加到很多台後會引入協調成本（共識、分散式鎖帶來新的故障模式與延遲），以及連線池放大——100 台每台開 10 條連線，資料庫就要扛 1000 條連線，機器數線性成長、資料庫連線數也跟著線性成長，最後瓶頸從應用層移到資料庫的連線上限。這條在 &lt;a href="https://tarrragon.github.io/blog/devops/02-horizontal-scaling/" data-link-title="模組二：水平擴展" data-link-desc="一個實例不夠時怎麼加第二個 — stateless 設計、shared storage、session 處理的工程約束">模組二 水平擴展&lt;/a> 展開；關鍵是水平擴展把成本換到協調與下游連線上，不是免費的線性擴張。&lt;/p>
&lt;h2 id="擴縮訊號的判讀">擴縮訊號的判讀&lt;/h2>
&lt;p>擴縮的自動化要選對訊號、設對門檻。常見的擴容訊號各有適用與失準：CPU 通用但對 I/O bound 的服務失準、佇列深度快且直接反映積壓、p95 延遲是用戶體驗訊號但等延遲出現才擴可能來不及。設定順序是先選訊號、再設門檻、再加冷卻時間（cool-down）防止反覆抖動。&lt;/p>
&lt;p>幾個擴縮後的訊號直接指出問題出在哪：加了機器但 QPS 沒提升，代表有狀態殘留（流量沒被新機器分攤）；加了機器但資料庫連線爆掉，是連線池放大；自動擴縮反覆擴了又縮，是冷卻時間太短或訊號在抖動；尖峰時新機器來不及起來，是冷啟動太慢或預測不夠提前。這些訊號讀對了，才知道該調的是訊號、門檻、冷卻時間，還是根本不該用自動擴縮（可預期的尖峰要用排程式或預測式擴容，不是等訊號觸發）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>膝點以下該留多少 headroom、安全係數怎麼定 → &lt;a href="https://tarrragon.github.io/blog/devops/05-capacity-planning/peak-estimation/" data-link-title="峰值估算" data-link-desc="要估一個服務的峰值該準備多少時，先分清峰值屬於哪一種形狀、再用歷史倍率階梯與安全係數推算，並知道安全係數的數學根據在飽和曲線">峰值估算&lt;/a>&lt;/li>
&lt;li>用 ramp-up 壓測實際找出膝點 → &lt;a href="https://tarrragon.github.io/blog/devops/05-capacity-planning/load-testing-tools/" data-link-title="壓力測試工具與方法" data-link-desc="要用壓測驗證容量規劃時，釐清壓測到底在找什麼、k6 與 wrk 各自的定位、怎麼讀壓測輸出的延遲分布，以及讓結果失真的幾個反模式">壓力測試工具與方法&lt;/a>&lt;/li>
&lt;li>擴容到底值不值得——把容量翻成成本 → &lt;a href="https://tarrragon.github.io/blog/devops/05-capacity-planning/cost-model/" data-link-title="成本模型" data-link-desc="把容量規劃的資源翻成錢時，釐清 on-demand、reserved、spot 三種計費模式的取捨、怎麼算單位請求成本、以及過度與不足配置各自的經濟代價">成本模型&lt;/a>&lt;/li>
&lt;li>水平擴展的無狀態前提與連線放大 → &lt;a href="https://tarrragon.github.io/blog/devops/02-horizontal-scaling/" data-link-title="模組二：水平擴展" data-link-desc="一個實例不夠時怎麼加第二個 — stateless 設計、shared storage、session 處理的工程約束">模組二 水平擴展&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>什麼訊號代表該擴容、什麼代表可以縮容，規模拐點判斷的根本概念是飽和曲線：系統不是「撐得住」跟「掛掉」的二元，中間有一段可測量的劣化區。負載往上加，系統先在延遲平穩的線性區，過了某個點進入延遲開始上升的膝點區（knee），再往上就是延遲不可預測的懸崖區（cliff）。拐點判斷的核心，是在系統進入膝點區、還沒到懸崖時就讀出訊號，而不是等它掉下懸崖。</p>
<p>判準先建立在利用率的三段區間上。利用率低於 50% 是線性區，延遲平穩；50% 到 80% 是膝點區，延遲開始上升但仍可接受；超過 80% 是懸崖區，延遲不可預測、可能逾時或引發級聯故障。健康的系統應該運轉在 50% 到 70% 之間，這也是 <a href="/blog/devops/05-capacity-planning/peak-estimation/" data-link-title="峰值估算" data-link-desc="要估一個服務的峰值該準備多少時，先分清峰值屬於哪一種形狀、再用歷史倍率階梯與安全係數推算，並知道安全係數的數學根據在飽和曲線">峰值估算</a> 裡安全係數要覆蓋的那段 headroom。</p>
<p>這三段區間背後是排隊理論：利用率往高處走，佇列長度增加的幅度越來越陡——50% 到 70% 增加數倍、70% 到 90% 再急遽放大（實際倍數視佇列模型而定，但方向一致是超線性）；延遲跟佇列長度成正比，所以延遲在高利用率區是指數成長。80% 這個懸崖門檻是通用的形狀，膝點的實際位置隨系統類型變——無狀態服務的膝點大約在 80% CPU、有鎖競爭的資料庫大約 60%、吃磁碟 I/O 的佇列服務可能 50% 就到。這就是為什麼安全係數不能對所有系統套同一個數：有狀態的關鍵系統膝點來得早，要留的 headroom 比無狀態服務厚。判斷一個系統該在多少利用率就準備擴容，要看它是哪一類、膝點在哪，不是一律套 80%。</p>
<h2 id="膝點的早期訊號p99-先於平均劣化">膝點的早期訊號：p99 先於平均劣化</h2>
<p>膝點的價值在於它是「該擴容」的早期訊號，但它只在對的指標上看得見。四個訊號共同指向系統正在進入膝點區：吞吐量從線性成長轉為次線性（加壓上去、吞吐不再等比增加）、p50 還穩但 p99 與 p999 開始飆、佇列開始堆積（不只是利用率上升）、而錯誤率仍接近零。最後這條很關鍵——膝點區的系統還沒開始報錯，只看錯誤率會以為一切正常，要看尾延遲才抓得到。</p>
<p>尾延遲的敏感度是這裡的判讀核心。p50 對垃圾回收暫停、重試風暴、長尾都不敏感，純看平均或 p50 會誤判「飽和還沒到」；p99 對連線競爭敏感，能較早看到飽和的苗頭；p999 對垃圾回收的 stop-the-world、leader 選舉、重試風暴最敏感。看容量該不該擴，看的是 p99 跟 p999 的曲線有沒有開始上翹，不是平均值。等到吞吐量開始下降、p99 變成逾時、錯誤率飆升、重試風暴出現，那已經是掉進懸崖區了，擴容是在救火而不是預防。</p>
<h2 id="用-ramp-up-找膝點不用單點壓測">用 ramp-up 找膝點，不用單點壓測</h2>
<p>膝點的位置要靠階梯加壓（ramp-up）找，不能用固定的單一壓力值測。按 1 倍、2 倍、4 倍、8 倍逐級加壓，每一級維持 5 到 10 分鐘看穩態行為，才能看出曲線在哪個負載開始上翹。一個「2000 RPS 撐在 100 毫秒」的單點結果毫無定位價值——它可能離膝點還很遠、也可能已經在懸崖邊，單點測不出來。用哪個工具做這種階梯加壓、怎麼讀每一級的輸出，見 <a href="/blog/devops/05-capacity-planning/load-testing-tools/" data-link-title="壓力測試工具與方法" data-link-desc="要用壓測驗證容量規劃時，釐清壓測到底在找什麼、k6 與 wrk 各自的定位、怎麼讀壓測輸出的延遲分布，以及讓結果失真的幾個反模式">壓力測試工具與方法</a>；這裡要確立的是拐點判讀本身：拐點是一條曲線上的位置，要用曲線去找，不是一個點。</p>
<h2 id="找出哪個資源先飽和">找出哪個資源先飽和</h2>
<p>系統的容量由最先飽和的那個資源決定，所以每次 ramp-up 要同時盯多個維度，看哪個先到頂：</p>
<ul>
<li><strong>CPU</strong>：看 load average 與 run queue，不只看利用率——利用率 100% 但 run queue 是空的仍撐得住，run queue 開始堆才是真的 CPU 飽和。</li>
<li><strong>記憶體</strong>：垃圾回收暫停、swap、快取驅逐都是隱性的記憶體飽和，不會反映在單純的用量數字上。</li>
<li><strong>磁碟 I/O</strong>：吞吐量、IOPS、佇列深度三個維度分開看——雲端 SSD 通常先撞 IOPS 上限，本機 NVMe 先撞吞吐量上限。</li>
<li><strong>網路</strong>：頻寬、每秒封包數（PPS）、連線數。雲端的 PPS 上限超過時可能是靜默丟包，不報錯。</li>
<li><strong>連線池</strong>：最常見的隱性瓶頸。一個大小 100 的連線池用到 95 就已經接近飽和，但 CPU、記憶體都還很閒——瓶頸不在機器、在池子。</li>
<li><strong>外部 API 配額</strong>：看對方回的 429 錯誤率，這個上限不在自己手上。</li>
<li><strong>檔案描述元與連接埠</strong>：ephemeral port 與 file descriptor 是 OS 層的配額，跟應用的連線池是不同層——連線池還沒滿、但 OS 的 fd 或可用 port 先耗盡，一樣接不了新連線，且症狀（<code>Too many open files</code>、connect 失敗）容易被誤判成應用問題。</li>
</ul>
<p>還有一種隱性飽和不在這六維裡：熱分區（hot partition）。分散式的鍵值或資料庫，名義容量是每個分區上限乘以分區數，但鍵分布不均時，整體利用率可能只有 20%、最熱的那個分區已經 100%，再加流量立刻被限流。訊號是「吞吐量上不去、但平均利用率很低」加上「某些鍵的延遲飆升、出現限流事件」。處理靠複合鍵、寫入分片或用快取吸收熱鍵——這種飽和用整體利用率完全看不到。</p>
<h2 id="該擴容之後往垂直還是水平">該擴容之後：往垂直還是水平</h2>
<p>確認要擴之後，下一個判斷是往哪個軸擴。垂直擴展（換更大的機器）不必改程式，但受單機物理上限限制、成本非線性（高階機型有溢價）、且是單點；水平擴展（加更多台）理論上線性，但要求服務無狀態或狀態能同步，且每台都要付基礎成本。經驗法則是無狀態的部分水平擴、有狀態的關鍵節點（如資料庫主節點）垂直擴加讀取副本，兩者常同時進行。</p>
<p>垂直擴展有兩道牆要知道。第一道是物理上限，但真正的天花板常常比規格更早到——雲端有記憶體達 TiB 級的超大機型，但有狀態的交易型資料庫主節點，真實天花板常卡在 32 到 64 vCPU，因為鎖競爭、context switch、記憶體頻寬這些架構因素，不是規格不夠。第二道是成本曲線：中階機型從 4 到 8 vCPU、8 到 16 vCPU 大約是 1.8 到 1.9 倍（接近線性），但過了 48 vCPU 明顯偏離線性、高階機型溢價更陡。垂直擴到一個點之後，每多一分效能付的錢急遽變貴，那就是該轉水平或該分片的訊號。</p>
<p>水平擴展的隱性成本則在協調與連線放大。加到很多台後會引入協調成本（共識、分散式鎖帶來新的故障模式與延遲），以及連線池放大——100 台每台開 10 條連線，資料庫就要扛 1000 條連線，機器數線性成長、資料庫連線數也跟著線性成長，最後瓶頸從應用層移到資料庫的連線上限。這條在 <a href="/blog/devops/02-horizontal-scaling/" data-link-title="模組二：水平擴展" data-link-desc="一個實例不夠時怎麼加第二個 — stateless 設計、shared storage、session 處理的工程約束">模組二 水平擴展</a> 展開；關鍵是水平擴展把成本換到協調與下游連線上，不是免費的線性擴張。</p>
<h2 id="擴縮訊號的判讀">擴縮訊號的判讀</h2>
<p>擴縮的自動化要選對訊號、設對門檻。常見的擴容訊號各有適用與失準：CPU 通用但對 I/O bound 的服務失準、佇列深度快且直接反映積壓、p95 延遲是用戶體驗訊號但等延遲出現才擴可能來不及。設定順序是先選訊號、再設門檻、再加冷卻時間（cool-down）防止反覆抖動。</p>
<p>幾個擴縮後的訊號直接指出問題出在哪：加了機器但 QPS 沒提升，代表有狀態殘留（流量沒被新機器分攤）；加了機器但資料庫連線爆掉，是連線池放大；自動擴縮反覆擴了又縮，是冷卻時間太短或訊號在抖動；尖峰時新機器來不及起來，是冷啟動太慢或預測不夠提前。這些訊號讀對了，才知道該調的是訊號、門檻、冷卻時間，還是根本不該用自動擴縮（可預期的尖峰要用排程式或預測式擴容，不是等訊號觸發）。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>膝點以下該留多少 headroom、安全係數怎麼定 → <a href="/blog/devops/05-capacity-planning/peak-estimation/" data-link-title="峰值估算" data-link-desc="要估一個服務的峰值該準備多少時，先分清峰值屬於哪一種形狀、再用歷史倍率階梯與安全係數推算，並知道安全係數的數學根據在飽和曲線">峰值估算</a></li>
<li>用 ramp-up 壓測實際找出膝點 → <a href="/blog/devops/05-capacity-planning/load-testing-tools/" data-link-title="壓力測試工具與方法" data-link-desc="要用壓測驗證容量規劃時，釐清壓測到底在找什麼、k6 與 wrk 各自的定位、怎麼讀壓測輸出的延遲分布，以及讓結果失真的幾個反模式">壓力測試工具與方法</a></li>
<li>擴容到底值不值得——把容量翻成成本 → <a href="/blog/devops/05-capacity-planning/cost-model/" data-link-title="成本模型" data-link-desc="把容量規劃的資源翻成錢時，釐清 on-demand、reserved、spot 三種計費模式的取捨、怎麼算單位請求成本、以及過度與不足配置各自的經濟代價">成本模型</a></li>
<li>水平擴展的無狀態前提與連線放大 → <a href="/blog/devops/02-horizontal-scaling/" data-link-title="模組二：水平擴展" data-link-desc="一個實例不夠時怎麼加第二個 — stateless 設計、shared storage、session 處理的工程約束">模組二 水平擴展</a></li>
</ul>
]]></content:encoded></item></channel></rss>