[{"data":1,"prerenderedAt":827},["ShallowReactive",2],{"navigation":3,"\u002Farticles\u002Fsingle-machine-performance":34,"\u002Farticles\u002Fsingle-machine-performance-surround":822},[4],{"title":5,"path":6,"stem":7,"children":8,"page":33},"Articles","\u002Farticles","articles",[9,13,17,21,25,29],{"title":10,"path":11,"stem":12},"用 Daily Snapshot 提升統計查詢速度","\u002Farticles\u002Fdaily-snapshot","articles\u002Fdaily-snapshot",{"title":14,"path":15,"stem":16},"GKE 部署","\u002Farticles\u002Fgke-deployment","articles\u002Fgke-deployment",{"title":18,"path":19,"stem":20},"從 LLM 到 Agent：打通底層邏輯","\u002Farticles\u002Fllm-to-agent","articles\u002Fllm-to-agent",{"title":22,"path":23,"stem":24},"資訊安全實踐","\u002Farticles\u002Fsecurity-best-practices","articles\u002Fsecurity-best-practices",{"title":26,"path":27,"stem":28},"單機架構的性能優化","\u002Farticles\u002Fsingle-machine-performance","articles\u002Fsingle-machine-performance",{"title":30,"path":31,"stem":32},"伺服器渲染 SSR","\u002Farticles\u002Fssr","articles\u002Fssr",false,{"id":35,"title":26,"author":36,"body":40,"date":814,"description":815,"extension":816,"externalUrl":817,"image":818,"meta":819,"minRead":240,"navigation":236,"path":27,"seo":820,"stem":28,"__hash__":821},"blog\u002Farticles\u002Fsingle-machine-performance.md",{"name":37,"avatar":38},"Gary",{"src":39,"alt":37},"\u002Fimages\u002Fselfie.webp",{"type":41,"value":42,"toc":795},"minimark",[43,48,52,58,61,64,67,78,81,87,90,92,95,101,104,108,111,124,128,131,277,282,302,304,307,310,314,317,348,354,358,365,395,398,400,404,407,413,576,581,589,591,595,598,604,607,618,620,623,627,634,637,643,650,654,660,663,719,724,730,733,735,738,784,791],[44,45,47],"h2",{"id":46},"為什麼從單機開始","為什麼從單機開始？",[49,50,51],"p",{},"不少談高性能的書籍直接跳到分散式架構，但如果連單機都處理不好，分散式只是把問題放大。",[49,53,54],{},[55,56,57],"strong",{},"單機處理好，才是高性能的前提。",[59,60],"hr",{},[44,62,63],{"id":63},"基本架構",[49,65,66],{},"最簡單的系統只有兩層：",[68,69,74],"pre",{"className":70,"code":72,"language":73},[71],"language-text","Client → 應用服務（Web Server）→ 資料庫服務（DB）\n","text",[75,76,72],"code",{"__ignoreMap":77},"",[49,79,80],{},"大多數系統成長後，會再加入兩個服務：",[68,82,85],{"className":83,"code":84,"language":73},[71],"Client → CDN → 應用服務 → Cache → 資料庫服務\n",[75,86,84],{"__ignoreMap":77},[49,88,89],{},"這個架構覆蓋了世界上 80% 以上的系統，從這裡出發優化，是最務實的路線。",[59,91],{},[44,93,94],{"id":94},"應用層優化",[49,96,97,98],{},"應用層的目標：",[55,99,100],{},"用最少的資源處理請求，並最快回應客戶。",[49,102,103],{},"可以拆成兩個方向：",[105,106,107],"h3",{"id":107},"運算優化",[49,109,110],{},"減少不必要的 CPU 消耗：",[112,113,114,118,121],"ul",{},[115,116,117],"li",{},"避免重複計算，善用 memoization",[115,119,120],{},"演算法與資料結構的選擇（時間複雜度）",[115,122,123],{},"避免同步阻塞的操作佔用主執行緒",[105,125,127],{"id":126},"io-優化","I\u002FO 優化",[49,129,130],{},"大多數 Web 應用的瓶頸都在 I\u002FO，而不是運算。",[68,132,136],{"className":133,"code":134,"language":135,"meta":77,"style":77},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F ❌ 循環內逐一查詢，N 次 I\u002FO\nfor (const id of ids) {\n  const user = await db.query('SELECT * FROM users WHERE id = $1', [id])\n}\n\n\u002F\u002F ✅ 一次批量查詢，1 次 I\u002FO\nconst users = await db.query('SELECT * FROM users WHERE id = ANY($1)', [ids])\n","ts",[75,137,138,147,175,225,231,238,244],{"__ignoreMap":77},[139,140,143],"span",{"class":141,"line":142},"line",1,[139,144,146],{"class":145},"sHwdD","\u002F\u002F ❌ 循環內逐一查詢，N 次 I\u002FO\n",[139,148,150,154,158,162,165,169,172],{"class":141,"line":149},2,[139,151,153],{"class":152},"s7zQu","for",[139,155,157],{"class":156},"sTEyZ"," (",[139,159,161],{"class":160},"spNyl","const",[139,163,164],{"class":156}," id ",[139,166,168],{"class":167},"sMK4o","of",[139,170,171],{"class":156}," ids) ",[139,173,174],{"class":167},"{\n",[139,176,178,181,184,187,190,193,196,200,204,207,211,213,216,219,222],{"class":141,"line":177},3,[139,179,180],{"class":160},"  const",[139,182,183],{"class":156}," user",[139,185,186],{"class":167}," =",[139,188,189],{"class":152}," await",[139,191,192],{"class":156}," db",[139,194,195],{"class":167},".",[139,197,199],{"class":198},"s2Zo4","query",[139,201,203],{"class":202},"swJcz","(",[139,205,206],{"class":167},"'",[139,208,210],{"class":209},"sfazB","SELECT * FROM users WHERE id = $1",[139,212,206],{"class":167},[139,214,215],{"class":167},",",[139,217,218],{"class":202}," [",[139,220,221],{"class":156},"id",[139,223,224],{"class":202},"])\n",[139,226,228],{"class":141,"line":227},4,[139,229,230],{"class":167},"}\n",[139,232,234],{"class":141,"line":233},5,[139,235,237],{"emptyLinePlaceholder":236},true,"\n",[139,239,241],{"class":141,"line":240},6,[139,242,243],{"class":145},"\u002F\u002F ✅ 一次批量查詢，1 次 I\u002FO\n",[139,245,247,249,252,255,257,259,261,263,265,267,270,272,274],{"class":141,"line":246},7,[139,248,161],{"class":160},[139,250,251],{"class":156}," users ",[139,253,254],{"class":167},"=",[139,256,189],{"class":152},[139,258,192],{"class":156},[139,260,195],{"class":167},[139,262,199],{"class":198},[139,264,203],{"class":156},[139,266,206],{"class":167},[139,268,269],{"class":209},"SELECT * FROM users WHERE id = ANY($1)",[139,271,206],{"class":167},[139,273,215],{"class":167},[139,275,276],{"class":156}," [ids])\n",[49,278,279],{},[55,280,281],{},"常見 I\u002FO 加速技術：",[112,283,284,290,296],{},[115,285,286,289],{},[55,287,288],{},"連線池（Connection Pool）","：複用資料庫連線，避免每次請求都重新建立連線",[115,291,292,295],{},[55,293,294],{},"Stream","：處理大檔案時逐段讀取，避免一次性載入記憶體",[115,297,298,301],{},[55,299,300],{},"非同步 I\u002FO","：Node.js 的事件驅動模型天生適合高併發 I\u002FO 場景",[59,303],{},[44,305,306],{"id":306},"資料庫層優化",[49,308,309],{},"資料庫層的目標同應用層，以最少的資源最快完成查詢。",[105,311,313],{"id":312},"索引index","索引（Index）",[49,315,316],{},"索引是影響資料庫性能最大的變數。用得好，查詢從全表掃描（O(n)）變成索引掃描（O(log n)）。",[68,318,322],{"className":319,"code":320,"language":321,"meta":77,"style":77},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- 沒有索引：全表掃描，資料量大時極慢\nSELECT * FROM orders WHERE user_id = 123;\n\n-- 建立索引後：直接定位\nCREATE INDEX idx_orders_user_id ON orders(user_id);\n","sql",[75,323,324,329,334,338,343],{"__ignoreMap":77},[139,325,326],{"class":141,"line":142},[139,327,328],{},"-- 沒有索引：全表掃描，資料量大時極慢\n",[139,330,331],{"class":141,"line":149},[139,332,333],{},"SELECT * FROM orders WHERE user_id = 123;\n",[139,335,336],{"class":141,"line":177},[139,337,237],{"emptyLinePlaceholder":236},[139,339,340],{"class":141,"line":227},[139,341,342],{},"-- 建立索引後：直接定位\n",[139,344,345],{"class":141,"line":233},[139,346,347],{},"CREATE INDEX idx_orders_user_id ON orders(user_id);\n",[49,349,350,353],{},[55,351,352],{},"注意："," 索引不是越多越好，寫入時需要維護索引，會拖慢 INSERT \u002F UPDATE。",[105,355,357],{"id":356},"鎖與事務lock-transaction","鎖與事務（Lock & Transaction）",[49,359,360,361,364],{},"鎖與事務是保障資料",[55,362,363],{},"一致性","的必要機制，但也是性能的隱患。",[68,366,368],{"className":319,"code":367,"language":321,"meta":77,"style":77},"-- 事務確保原子性：轉帳要嘛全成功，要嘛全失敗\nBEGIN;\n  UPDATE accounts SET balance = balance - 100 WHERE id = 1;\n  UPDATE accounts SET balance = balance + 100 WHERE id = 2;\nCOMMIT;\n",[75,369,370,375,380,385,390],{"__ignoreMap":77},[139,371,372],{"class":141,"line":142},[139,373,374],{},"-- 事務確保原子性：轉帳要嘛全成功，要嘛全失敗\n",[139,376,377],{"class":141,"line":149},[139,378,379],{},"BEGIN;\n",[139,381,382],{"class":141,"line":177},[139,383,384],{},"  UPDATE accounts SET balance = balance - 100 WHERE id = 1;\n",[139,386,387],{"class":141,"line":227},[139,388,389],{},"  UPDATE accounts SET balance = balance + 100 WHERE id = 2;\n",[139,391,392],{"class":141,"line":233},[139,393,394],{},"COMMIT;\n",[49,396,397],{},"鎖的範圍越小、持有時間越短，對性能的影響越低。",[59,399],{},[44,401,403],{"id":402},"cache-服務","Cache 服務",[49,405,406],{},"Cache 的目的是將資料庫的常用查詢結果提前存起來，讓讀取繞過資料庫。",[68,408,411],{"className":409,"code":410,"language":73},[71],"Client → 應用服務 → Cache（Redis）→ DB（Cache Miss 時才查）\n",[75,412,410],{"__ignoreMap":77},[68,414,416],{"className":133,"code":415,"language":135,"meta":77,"style":77},"const cacheKey = `user:${userId}`\nlet user = await redis.get(cacheKey)\n\nif (!user) {\n  user = await db.query('SELECT * FROM users WHERE id = $1', [userId])\n  await redis.set(cacheKey, JSON.stringify(user), 'EX', 3600) \u002F\u002F 快取 1 小時\n}\n",[75,417,418,442,465,469,484,515,572],{"__ignoreMap":77},[139,419,420,422,425,427,430,433,436,439],{"class":141,"line":142},[139,421,161],{"class":160},[139,423,424],{"class":156}," cacheKey ",[139,426,254],{"class":167},[139,428,429],{"class":167}," `",[139,431,432],{"class":209},"user:",[139,434,435],{"class":167},"${",[139,437,438],{"class":156},"userId",[139,440,441],{"class":167},"}`\n",[139,443,444,447,450,452,454,457,459,462],{"class":141,"line":149},[139,445,446],{"class":160},"let",[139,448,449],{"class":156}," user ",[139,451,254],{"class":167},[139,453,189],{"class":152},[139,455,456],{"class":156}," redis",[139,458,195],{"class":167},[139,460,461],{"class":198},"get",[139,463,464],{"class":156},"(cacheKey)\n",[139,466,467],{"class":141,"line":177},[139,468,237],{"emptyLinePlaceholder":236},[139,470,471,474,476,479,482],{"class":141,"line":227},[139,472,473],{"class":152},"if",[139,475,157],{"class":156},[139,477,478],{"class":167},"!",[139,480,481],{"class":156},"user) ",[139,483,174],{"class":167},[139,485,486,489,491,493,495,497,499,501,503,505,507,509,511,513],{"class":141,"line":233},[139,487,488],{"class":156},"  user",[139,490,186],{"class":167},[139,492,189],{"class":152},[139,494,192],{"class":156},[139,496,195],{"class":167},[139,498,199],{"class":198},[139,500,203],{"class":202},[139,502,206],{"class":167},[139,504,210],{"class":209},[139,506,206],{"class":167},[139,508,215],{"class":167},[139,510,218],{"class":202},[139,512,438],{"class":156},[139,514,224],{"class":202},[139,516,517,520,522,524,527,529,532,534,537,539,542,544,547,550,552,555,558,560,562,566,569],{"class":141,"line":240},[139,518,519],{"class":152},"  await",[139,521,456],{"class":156},[139,523,195],{"class":167},[139,525,526],{"class":198},"set",[139,528,203],{"class":202},[139,530,531],{"class":156},"cacheKey",[139,533,215],{"class":167},[139,535,536],{"class":156}," JSON",[139,538,195],{"class":167},[139,540,541],{"class":198},"stringify",[139,543,203],{"class":202},[139,545,546],{"class":156},"user",[139,548,549],{"class":202},")",[139,551,215],{"class":167},[139,553,554],{"class":167}," '",[139,556,557],{"class":209},"EX",[139,559,206],{"class":167},[139,561,215],{"class":167},[139,563,565],{"class":564},"sbssI"," 3600",[139,567,568],{"class":202},") ",[139,570,571],{"class":145},"\u002F\u002F 快取 1 小時\n",[139,573,574],{"class":141,"line":246},[139,575,230],{"class":167},[49,577,578],{},[55,579,580],{},"Cache 是雙刃劍：",[112,582,583,586],{},[115,584,585],{},"用得好：讀取性能大幅提升，資料庫壓力下降",[115,587,588],{},"用不好：Cache 與 DB 資料不一致，用戶看到過期資料",[59,590],{},[44,592,594],{"id":593},"cdn-服務","CDN 服務",[49,596,597],{},"CDN 放在用戶與應用服務之間，讓靜態資源（圖片、JS、CSS）從距離用戶最近的節點回應，而不必每次都打到源站。",[68,599,602],{"className":600,"code":601,"language":73},[71],"台灣用戶 → 台灣 CDN 節點 → （Cache Hit）直接回應\n                           → （Cache Miss）回源站取得後快取\n",[75,603,601],{"__ignoreMap":77},[49,605,606],{},"適合放上 CDN 的內容：",[112,608,609,612,615],{},[115,610,611],{},"圖片、影片、字型",[115,613,614],{},"前端 JS \u002F CSS bundle",[115,616,617],{},"不常變動的 API 回應",[59,619],{},[44,621,622],{"id":622},"性能評估指標",[105,624,626],{"id":625},"回應時間latency","回應時間（Latency）",[49,628,629,630,633],{},"用戶視角的指標，",[55,631,632],{},"越低越好","。",[49,635,636],{},"從用戶發出請求到收到回應的完整時間，包含：",[68,638,641],{"className":639,"code":640,"language":73},[71],"網路傳輸 → 應用服務處理 → DB 查詢 → 回傳結果\n",[75,642,640],{"__ignoreMap":77},[49,644,645,646,649],{},"實務上常用 ",[55,647,648],{},"p99","（第 99 百分位）來衡量，代表 99% 的請求都在這個時間內完成，比平均值更能反映真實體驗。",[105,651,653],{"id":652},"吞吐量throughput-qps","吞吐量（Throughput \u002F QPS）",[49,655,656,657,633],{},"開發者視角的指標，",[55,658,659],{},"越高越好",[49,661,662],{},"代表系統在單位時間內能處理的請求數量：",[664,665,666,682],"table",{},[667,668,669],"thead",{},[670,671,672,676,679],"tr",{},[673,674,675],"th",{},"單位",[673,677,678],{},"全名",[673,680,681],{},"適用場景",[683,684,685,697,708],"tbody",{},[670,686,687,691,694],{},[688,689,690],"td",{},"QPS",[688,692,693],{},"Queries Per Second",[688,695,696],{},"一般 Web API",[670,698,699,702,705],{},[688,700,701],{},"TPS",[688,703,704],{},"Transactions Per Second",[688,706,707],{},"資料庫、金融系統",[670,709,710,713,716],{},[688,711,712],{},"HPS",[688,714,715],{},"HTTP Requests Per Second",[688,717,718],{},"HTTP 服務",[49,720,721],{},[55,722,723],{},"計算公式：",[68,725,728],{"className":726,"code":727,"language":73},[71],"QPS = 併發數 ÷ 平均回應時間\n",[75,729,727],{"__ignoreMap":77},[49,731,732],{},"例如系統可承受 1000 併發、平均回應時間 1 秒，則 QPS = 1000。",[59,734],{},[44,736,737],{"id":737},"優化路線總結",[664,739,740,750],{},[667,741,742],{},[670,743,744,747],{},[673,745,746],{},"層級",[673,748,749],{},"優化方向",[683,751,752,760,768,776],{},[670,753,754,757],{},[688,755,756],{},"應用層",[688,758,759],{},"減少運算、優化 I\u002FO、連線池、非同步處理",[670,761,762,765],{},[688,763,764],{},"資料庫層",[688,766,767],{},"索引設計、減少鎖的範圍與持有時間",[670,769,770,773],{},[688,771,772],{},"Cache 層",[688,774,775],{},"熱點資料快取、合理設定 TTL",[670,777,778,781],{},[688,779,780],{},"CDN 層",[688,782,783],{},"靜態資源卸載、縮短傳輸路徑",[49,785,786,787,790],{},"高性能系統的本質很簡單：",[55,788,789],{},"以最少的資源做最多的事情。"," 從單機把每一層調好，才有資格談分散式。",[792,793,794],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":77,"searchDepth":149,"depth":149,"links":796},[797,798,799,803,807,808,809,813],{"id":46,"depth":149,"text":47},{"id":63,"depth":149,"text":63},{"id":94,"depth":149,"text":94,"children":800},[801,802],{"id":107,"depth":177,"text":107},{"id":126,"depth":177,"text":127},{"id":306,"depth":149,"text":306,"children":804},[805,806],{"id":312,"depth":177,"text":313},{"id":356,"depth":177,"text":357},{"id":402,"depth":149,"text":403},{"id":593,"depth":149,"text":594},{"id":622,"depth":149,"text":622,"children":810},[811,812],{"id":625,"depth":177,"text":626},{"id":652,"depth":177,"text":653},{"id":737,"depth":149,"text":737},"2025-09-18","拆解單機系統的優化路線，以及用回應時間與吞吐量來衡量系統性能。","md",null,"\u002Fimages\u002Fsingle-machine.jpg",{},{"title":26,"description":815},"9ChEms2oPjTPbuK8gwhXh4BcJkN8XFANcZEtaLR2hu4",[823,825],{"title":22,"path":23,"stem":24,"description":824,"children":-1},"整理日常開發中常用的資安觀念與實踐方式。",{"title":30,"path":31,"stem":32,"description":826,"children":-1},"介紹伺服器端渲染的運作原理、與 CSR 的差異、優缺點，以及在 Nuxt.js 開發中需要注意的事項。",1781661891556]