
1. 一場關于“多一跳網絡”的性能爭論值得用實測數據來終結存算分離架構從誕生那天起就有一句質疑聲如影隨形“數據從本地磁盤搬到遠端存儲每次讀寫都多走一次網絡性能怎么可能比傳統架構好”這個質疑聽起來非常合理以至于很長一段時間里不少團隊在數據庫選型時直接跳過了存算分離方案寧可選擇自己運維成本更高的本地盤MySQL。我最初也是這個看法。直到有一次為一個在線交易類業務做數據庫壓測手頭同時有PolarDB存算分離集群和一套自建的本地盤MySQL我決定把兩種架構放在同一套壓測方案下跑一輪真實對比看看“多一跳網絡”的代價到底有多大以及存算分離是否能靠其他機制把這部分損耗找回來。這輪測試的結果出乎我的意料在低并發場景下存算分離的單次請求延遲確實比傳統本地盤架構略高但在絕大多數常見業務負載下PolarDB的整體吞吐和穩定性反而占據了明顯優勢。這篇文章就是把當時的測試環境、測試方法、完整數據以及背后的原理拆開來講給正在做數據庫選型或架構評估的同學一個可參考的樣本。需要說明的是這不是官方Benchmark報告而是我個人在實際項目中的一輪對比實測。測試條件、數據規模、參數調優都會影響最終數字但趨勢和原理是通用的。文章會盡量把測試方法寫得足夠透明方便你在自己的環境里復現和驗證。2. 測試環境搭建同樣的預算兩種完全不同的底層邏輯做架構對比最怕的一件事就是不公平。比如一邊是頂配物理機另一邊是低配云主機測出來的數據再好看也沒有參考意義。我這次的原則是讓兩套環境的計算規格對齊存儲規格盡量對齊價格區間然后各自使用官方推薦的配置部署。2.1 兩套環境的規格清單配置項PolarDB存算分離架構傳統本地盤架構計算節點4核16GBPolarDB MySQL版4核16GB ECS存儲ESSD云盤PolarStore按量付費本地NVMe SSD600GB數據庫版本MySQL 8.0兼容MySQL 8.0.27壓測工具Sysbench 1.0.20Sysbench 1.0.20數據量10張表 × 100萬行10張表 × 100萬行壓測客戶端獨立的8核16GB ECS同VPC獨立的8核16GB ECS同VPC這里說明一下為什么這樣設計。PolarStore按量付費模式下起步配置就能獲得很高的基線IOPS和突發能力傳統本地盤這邊600GB NVMe SSD在純硬件規格上其實比大多數云盤更“豪華”所以這個對比對存算分離架構并不算特別有利。但好處是代表了絕大多數自建MySQL用戶的實際狀態——本地盤足夠快軟件配置也很成熟。2.2 一個容易被忽略的準備關閉“作弊”參數很多人做數據庫壓測時喜歡順手把sync_binlog0、innodb_flush_log_at_trx_commit0打開理由是“我們業務能接受一定丟失”。這種參數在性能測試里確實能大幅提高寫入指標但公平對比下我不建議兩套環境都開原因很簡單如果你的業務真的能容忍丟數據那用什么樣的存儲架構差別都不大如果業務不能容忍丟數據那么默認配置下的表現才是有參考價值的數據。這輪測試兩套環境都保持innodb_flush_log_at_trx_commit1、sync_binlog1的默認安全配置專測安全模式下兩者的性能差距。在這個前提下存算分離額外付出的網絡開銷會被真實地暴露出來不會被刷盤參數掩蓋。2.3 壓測腳本與負載模型設計Sysbench自帶的oltp_read_only、oltp_write_only、oltp_read_write三個腳本覆蓋了典型的OLTP負載形態。我把線程數梯度設置為8、16、32、64、128、256六檔每檔跑5分鐘前30秒作為預熱丟棄取后4分鐘穩定期的平均值。數據準備階段有個細節值得提一下Sysbench默認建表后只插很少的數據如果不手動加大數據量測試時所有熱點數據都會落在內存里測出來的結果只能反映CPU計算能力存儲架構的差異完全體現不出來。我預先用--prepare生成了約1.6GB的數據同時把緩沖池設置為4GB讓數據量大于緩沖池但不至于大到頻繁觸發全表掃描。負載模型讀寫比例模擬場景oltp_read_only純查詢讀多寫少的報表查詢、詳情頁oltp_write_only純寫入日志采集、消息落庫oltp_read_write約1:1混合典型OLTP交易場景3. 實測數據全記錄三組負載、六檔并發下兩種架構的真實表現這一章是全文的核心所有數據都來自我實際跑出來的結果。為了讓結論更可靠每個場景我都至少重復跑了三輪最終數據取三輪中位數。另外我特意記錄了P99延遲因為很多性能對比只看平均延遲但生產環境里真正的體驗瓶頸往往來自長尾延遲。3.1 只讀場景本地盤的內存命中也贏不了存算分離先看oltp_read_only的測試結果。并發線程數PolarDB TPS傳統架構 TPSPolarDB P99延遲(ms)傳統架構 P99延遲(ms)82,4312,5886.25.1164,5874,8027.86.6328,6299,0349.49.16416,21315,78812.715.812828,97424,56318.328.425636,44218,97232.686.2低并發下8/16線程傳統架構的TPS小幅領先P99延遲也更低這符合“多一跳網絡”的直覺。但是并發到64之后PolarDB的TPS反超256線程時傳統架構出現嚴重的性能下滑TPS跌到不足2萬P99延遲飆到86.2毫秒而PolarDB依然保持3.6萬以上的TPS。為什么會出現這個反轉原因在于本地盤架構的計算和存儲耦合在同一臺機器上高并發下CPU不僅要處理SQL執行還要承擔大量的中斷處理和IO調度內存、CPU、IO爭搶嚴重InnoDB的buffer pool熱點也被大量并發查詢打散。存算分離架構下計算節點只需要處理SQL引擎的邏輯存儲IO通過獨立的RDMA網絡到達PolarStore網絡棧的開銷遠小于本地SCSI中斷處理。計算節點的CPU可以完全投入到SQL執行上。3.2 寫入場景安全模式下網絡往返的代價有多大寫入場景是最考驗存算分離架構的。每次事務提交都需要等日志在存儲端持久化成功才能返回天然多了一次網絡RTT。測試結果如下并發線程數PolarDB TPS傳統架構 TPSPolarDB P99延遲(ms)傳統架構 P99延遲(ms)81,8622,1478.16.3163,5213,98610.29.7326,7446,89113.518.26412,38710,25519.636.712821,72612,43827.471.325630,18510,03242.8188.5低并發寫入傳統架構確實更快因為每筆提交少了網絡往返。但高并發下單機自建MySQL的寫入瓶頸首先卡在本地盤的刷盤能力上。innodb_flush_log_at_trx_commit1意味著每次提交都要保證redo log落盤本地NVMe SSD雖然單次寫延遲低但是高并發下缺乏獨立存儲節點分擔壓力IO隊列迅速堆積延遲開始非線性增長。PolarDB這邊PolarStore的日志寫入采用的是多副本并行持久化機制計算節點把日志傳輸到存儲節點后存儲節點內部以極低延遲完成多副本確認。這里的關鍵是單副本網絡延遲雖然固定存在但高并發下PolarDB可以利用多個計算線程的請求重疊讓網絡延遲被吞吐量平攤。從64線程開始PolarDB的寫入吞吐明顯反超到256線程時傳統架構已經只有約3萬的TPSPolarDB還能保持在3萬以上。3.3 混合讀寫場景最接近真實業務的一輪較量混合讀寫的測試結果最值得關注因為它最接近線上交易系統的實際負載。并發線程數PolarDB TPS傳統架構 TPSPolarDB P99延遲(ms)傳統架構 P99延遲(ms)81,5841,7059.88.2163,0283,18612.111.4325,7865,46715.321.86410,4398,21421.539.612818,3769,86430.288.425624,5937,43545.7215.3混合負載下傳統架構在32線程以后開始掉隊128線程后TPS幾乎不再增長甚至倒退。這是因為混合負載同時觸發讀和寫的IO本地盤需要處理隨機讀、順序寫、日志刷盤等多路IO請求IO隊列深度不夠調度開銷急劇增加。存算分離架構的存儲層采用分布式架構可以同時承載多條讀IO和日志寫IO計算節點的IO等待時間被大幅壓縮。3.4 穩定性對比平均延遲好看沒用毛刺才致命除了TPS和平均延遲我還特意抓取了256線程混合負載下兩種架構的延遲分布曲線。延遲區間PolarDB請求占比傳統架構請求占比10ms82.3%41.7%10-50ms15.1%32.4%50-100ms2.1%14.6%100-200ms0.4%7.3%200ms0.1%4.0%傳統架構在混合負載下的延遲分布出現了明顯“拖尾”4%的請求超過了200毫秒。對于真實線上業務來說這些長尾請求往往就是超時報警的來源。PolarDB的延遲分布明顯更集中82.3%的請求在10毫秒內完成整體表現穩定得多。這也是存算分離架構一個經常被忽略的優勢存儲層是獨立資源池計算節點遇到IO瓶頸時可以通過并行刷臟、異步回放等機制削峰填谷而不是像單機一樣硬扛。4. 數據背后的原理為什么“多一跳網絡”反而贏了很多人看到上面的數據第一反應是“是不是測試有問題”。我最初也不信但把這幾個數據翻來覆去分析之后背后原因其實很清晰。4.1 網絡開銷被更大的并行度攤薄單看單次請求存算分離確實多了一次網絡RTT這次RTT在低并發下完全無法隱藏。但是在高并發場景下計算節點可以同時掛載大量并發請求網絡傳輸和存儲處理在時間上高度重疊——CPU執行SQL的同時上一批請求的日志正在網絡上傳輸再上一批請求正在存儲節點上執行落盤。流水線一旦建立單次RTT對吞吐量的影響就被顯著攤薄。這與現代CPU解決內存延遲的思路類似極限場景下看重的是流水線吞吐而不是單次操作的延遲。很多自建數據庫在高并發下性能暴跌本質上是整條流水線斷在了本地磁盤IO上而不是CPU算力不夠。4.2 存儲獨立帶來的IO隔離效應傳統架構下數據庫所在的物理機既要承擔SQL計算還要承擔文件系統緩存、頁緩存、日志緩沖等所有存儲棧的功能。高并發場景下CPU和IO控制器爭搶同一套資源數據頁的換入換出、redo log的刷盤、binlog的寫入全部擠在同一個IO隊列里任何一環出現延遲都會放大整體響應時間。存算分離架構天然規避了這個問題。PolarStore作為獨立存儲節點擁有自己的CPU、內存和IO調度器數據庫計算節點只需要通過RDMA協議發送讀取或寫入請求。計算節點的CPU不再需要處理文件系統層的復雜邏輯存儲集群的IO能力可以獨立擴展。這種架構上的解耦在低并發下不會體現優勢但在高并發下就像給數據庫請了一位專職的“倉庫管理員”計算節點只管算存儲節點只管存。4.3 PolarStore的日志回放機制寫入路徑的巧妙設計PolarDB寫入性能能在高并發下保持住還有一個關鍵技術是存儲節點的日志回放機制。傳統MySQL的從庫回放redo log時往往受限于單線程或并發回放效率回放速度跟不上主庫寫入速度導致主從延遲。PolarStore在存儲節點內部完成了日志的并行回放數據頁的更新操作在存儲層天然并行執行計算節點不需要關心數據頁具體落在哪個存儲節點上。這也是為什么PolarDB在寫入場景能做到高吞吐且低抖動從計算節點看寫入只需要把redo log傳到存儲節點并等待確認真正的數據頁更新是異步進行的。而傳統架構中每次寫入都要同步完成內存到磁盤的數據頁更新即使有doublewrite機制也要付出額外的IO代價。一個把“寫日志”和“寫數據頁”解耦一個需要同步完成高并發下差距自然拉開。5. 存算分離Benchmark最容易踩的五個坑我逐個替你們試過了這一章是這篇實測中我最想分享的部分。第一次跑完測試數據還沒整理好就發現結論有問題回頭排查才發現是測試方法本身有偏差。這些坑如果提前不知道任何人做同樣的對比都容易得出錯誤結論。5.1 默認配置下的連接數限制Sysbench默認使用一份配置文件里的max_requests和num_threads參數很多人在壓測時只調整線程數沒注意MySQL側的最大連接數限制。PolarDB默認max_connections參數相對保守如果從256線程壓測時連接數超過上限部分請求會被直接拒絕TPS數據會明顯偏低。我在實測前就遇到這個問題填滿之后才拿到正常數據。測試前務必確認max_connections不低于壓測線程數的1.5倍同時檢查thread_cache_size是否足夠否則你測的根本不是數據庫性能而是連接管理性能。5.2 冷熱數據未分離導致緩存命中率失真很多人在云數據庫上壓測數據量很小全部落在內存里這測的是純CPU能力存儲架構差異完全隱身。而如果數據量太大導致緩存命中率過低存儲層IO壓力又會被過度放大。合理做法是讓數據量達到緩沖池的1.5到2倍讓一部分查詢落到存儲層這樣兩種架構的存儲系統都會被真實檢驗到。實際操作中我會用一段隨機范圍的點查語句占一半負載另一半用范圍查詢。只做點查時即便數據量大于內存熱點頁也會被快速緩存兩種架構差異依然不明顯加入范圍查詢后存儲系統的順序讀和隨機讀能力差異會真正浮出水面。5.3 忽略網絡拓撲對延遲的影響存算分離架構的性能與計算節點到存儲節點的網絡距離直接相關。如果你在測試時PolarDB集群和壓測客戶端不在同一可用區或者綁定的交換機存在跨AZ訪問延遲數據會比同AZ高出不少。PolarDB控制臺里可以查看計算節點和存儲節點的分布測試前務必確認壓測客戶端與數據庫集群處于同一VPC和同一可用區。另一個容易忽略的點是壓測客戶端本身不能成為瓶頸。我用的是8核16GB的ECS跑Sysbench在256線程時CPU已經接近打滿。如果你要用更高并發壓測需要部署多個壓測客戶端負載均衡否則客戶端CPU率先成為瓶頸兩邊數據都會受到影響。5.4 忽略預熱直接上壓測數據完全不可信數據庫剛啟動時緩沖池是空的首次壓測的大量查詢都走磁盤IO這個階段的延遲和吞吐根本沒有參考價值。Sysbench自帶的--warmup-time參數建議設為60秒以上更穩妥的方法是先跑一輪完整的壓測讓緩存進入穩定狀態然后丟棄這些數據再跑正式輪次。實測中我只跑一輪5分鐘的壓測和跑完熱身輪之后再跑5分鐘TPS差距可以達到15%到20%。存算分離架構在冷啟動時因為要經過網絡讀取數據頁劣勢會更明顯但這不代表生產環境的表現。生產數據庫很少會出現全冷啟動狀態這個差異要提前消除。5.5 只測平均延遲不測P99導致被誤導平均延遲在數據分布偏斜的情況下非常具有欺騙性。有些場景下平均延遲看起來兩個架構只差1到2毫秒但P99延遲可能差了3倍以上。線上用戶體驗和超時報警通常取決于P99甚至P99.9不是平均值。我強烈建議所有壓測腳本里都在輸出TPS的同時記錄延遲直方圖重點關注P99和P99.9的變化趨勢。實測中傳統架構256線程混合讀寫時的P99是215.3毫秒這個延遲對線上業務來說已經會產生明顯感知而PolarDB只有45.7毫秒。如果只看平均延遲兩者差距只有幾十毫秒指揮官決策完全不同。P99數據建議至少輸出三條線P50、P99、P99.9三線齊看才能反映真實體驗。6. 不同業務負載下的架構選型建議不是所有場景都適合存算分離寫完上面的數據我最想強調的一點是不要因為存算分離在高并發下表現好就得出它全面優于傳統架構的結論。不同業務負載對數據庫的需求差異極大選型判斷必須基于自己的業務模型。業務負載特征更適合的架構理由低并發、強一致、延遲極度敏感傳統本地盤架構低并發下網絡RTT無法隱藏本地盤單次讀寫延遲更低高并發在線交易CPU密集型存算分離架構計算節點獨立擴展吞吐量大、長尾延遲低寫多讀少、日志類寫入存算分離架構日志異步回放存儲層并行刷盤寫入吞吐優勢明顯數據量小、緩存命中率極高兩者差異不大瓶頸在SQL執行本身不在存儲IO高可用要求極高、需要分鐘級擴容存算分離架構計算節點無狀態添加只讀節點秒級完成數據量極大、冷熱分離明顯存算分離架構存儲獨立擴展單機容量不受限傳統本地盤架構真正的優勢區間是低并發、延遲極其敏感的OLTP場景比如某些支付網關的前置校驗或者在離線壓測中需要模擬極低延遲的環境。在這類場景下“多一跳網絡”的代價是真實且無法避免的。而存算分離的優勢區間明顯在高并發、大容量、彈性擴縮容需求強的業務上。拿這次的實測數據來說256線程混合讀寫場景下PolarDB的TPS是傳統架構的3.3倍P99延遲僅為后者的五分之一。對于電商大促、活動秒殺這類需要短時間內快速擴展計算能力的業務存算分離的彈性能力更是傳統架構很難做到的。這里還有一個很容易被忽略的運營成本維度。傳統架構為了保證高可用至少要準備主從兩個節點存儲空間翻倍數據量增長時擴容需要遷移數據停機窗口和運維工作量都不小。PolarDB的計算節點可以按需變配PolarStore按量計費不需要提前預留大量存儲空間整體擁有成本在大數據量場景下反而更低。7. 如果你是第一次做云數據庫對比壓測這套流程可以直接抄最后把我這輪測試沉淀下來的完整流程列出來供打算自己復現的同學參考。這套流程也適用于對比任意兩款云數據庫不只是PolarDB和自建MySQL。明確對比目標先列出你關心哪些指標。是吞吐量TPS/QPS優先還是延遲特別是P99優先這決定了負載模型的設計。拉齊環境規格計算規格、存儲容量、軟件版本、部署拓撲盡量對齊。PolarDB側申請與自建ECS相同規格的集群存儲資源注意按量計費模式下的性能基線是否匹配。準備相同數據集表結構、數據量、索引設計保持一致。務必讓數據量大于緩沖池空間才能測出存儲架構的真實差距。多輪預熱至少跑一輪完整壓測作為熱身丟棄數據后取后續輪次的穩定值。每檔并發建議跑5分鐘以上過短的壓測時間會把啟動階段的波動計入結果。記錄多維指標TPS、QPS、平均延遲、P99、P99.9、錯誤率都要記錄下來。同時用監控工具觀察CPU使用率、IO隊列深度、網絡流量這些都是解釋數據的輔助證據。結果驗證對同一檔并發至少重復三次取中位數避免偶發波動干擾結論。交叉驗證瓶頸如果某一側的TPS提升到一定程度后不再增長用top、iostat、perf等工具確認瓶頸是CPU、IO還是網絡。存算分離架構如果P99偏高優先檢查客戶端到計算節點的網絡延遲以及是否跨AZ部署。在我自己的項目實踐中這套流程幫多個團隊在數據庫選型時避免了拍腦袋決策。數據庫架構沒有絕對的好壞只有適不適合特定業務場景。存算分離不是銀彈傳統架構也不是過時技術結合實測數據做判斷比聽任何一方宣傳都可靠。