下載速度很快,但一有其他傳輸,通話和遊戲就開始卡頓?真正原因往往是緩衝區膨脹——過大緩衝區帶來的額外延遲,而非頻寬不足。本文解析成因與修復方法。
一條連線可能測出很棒的下載數字,卻在別人開始一次大上傳、或一次遊戲更新開始下載的瞬間變得難以使用——通話變得像機器人在說話、遊戲瘋狂拉扯回彈、網頁卡住不動。這通常不是頻寬問題,而是緩衝區膨脹(bufferbloat):路由器、數據機或電信業者設備裡過大的緩衝區把資料排隊積壓,而不是丟棄它們,由此產生的延遲只有在連線繁忙時才會顯現。以下說明其成因,以及如何修復。
重點摘要
- 緩衝區膨脹是過大緩衝區帶來的額外延遲,而非頻寬不足——你的方案速度可能完全正常,連線卻依然在負載下感覺卡頓。
- 連線閒置時它完全隱形。 快速 Ping 一下看起來很棒;延遲只有在上傳或下載把連線跑滿之後才會出現。
- 負載下的 Ping 可能比閒置 Ping 高出一個數量級——在 APNIC 發布的一次家用寬頻實測中,線路被跑滿之後,Ping 從幾毫秒一路升到將近 300 ms。
- 標準修復方法是主動佇列管理(AQM),特別是 FQ-CoDel,多數現代路由器與基於 OpenWrt 的韌體都能直接開啟,無需更換新硬體。
- 只跑一次測速發現不了它——Ping 是在資料開始傳輸之前量的。要先把線路跑滿,再測一次,然後比較兩個數值。
什麼是緩衝區膨脹?
路由器與數據機都會保留一小段佇列——緩衝區——用來在資料到達速度超過出口線路承載能力的瞬間暫存資料。這是正常且必要的。問題出在緩衝區被設定得遠比實際需要的大:過大的緩衝區不是短暫地削平一次突發流量,而是讓封包排隊等上幾百毫秒甚至更久才被送出,因為裝置寧願把封包排隊,也不願丟棄它。正如 APNIC 網路部落格所說,人們直覺上會以為效能不佳是頻寬不夠,而真正的元兇往往正是這種堆積在過深緩衝區裡的額外排隊延遲。
這個術語由工程師 Jim Gettys 在 2010 年前後提出,他追溯發現,家用網路莫名的卡頓源自消費級路由器與數據機——它們的緩衝區是為吞吐量跑分而設計的,而非為回應速度設計。更大的緩衝區確實能在單獨測試中跑出略高的尖峰吞吐數字,但代價是任何排在一次大傳輸後面的封包都得等它排到——而這個等待,就是緩衝區膨脹。
閒置 Ping vs. 負載 Ping
緩衝區膨脹之所以能躲過日常測試,是因為它只在負載下才會出現。在網路上沒有其他活動時 Ping 你的路由器,會看到一個乾淨的低數值——這是閒置 Ping,量的是一個空佇列。同時開始一次大上傳或下載,再重新測一次,你會得到負載 Ping:一個封包排在所有已排隊資料後面、真正需要等待的延遲。
兩者之間的落差才是問題所在,而且這個落差一點都不小。在上面那篇 APNIC 文章裡,作者一邊同時跑上傳和下載,一邊記錄到遠端伺服器的 Ping:測試開始後的前 5 秒,延遲一直只有幾毫秒;傳輸一開始,Ping 就升到了將近 300 ms——一個封包來回一趟就要花掉三分之一秒,足以讓視訊通話徹底沒法用。頻寬自始至終沒有改變,只是每個封包如今都排進了一個終於被用起來的佇列。
這正是為什麼業界的量測工作已轉向追蹤負載下的延遲,而非只看閒置 Ping:在一條存在緩衝區膨脹的連線上,閒置延遲與原始吞吐量看起來都沒問題,但兩者都無法預測這條連線真正用起來會是什麼感覺。
成因
緩衝區膨脹不是單一的 Bug——而是預設就配置得很寬裕的緩衝區,疊加多數傳輸的行為方式:
- 預設佇列過大。 家用路由器、有線數據機、DSL 數據機以及光纖 ONT,往往被設定成在吞吐量跑分中永不丟包,而這恰恰與低延遲所需要的相反。
- 基於丟包的壅塞控制。 許多傳輸(大檔案下載、雲端同步、大檔案上傳)會持續加快傳送速度,直到真正發生丟包才會放慢。緩衝區一深,這個丟包訊號要等到佇列幾乎排滿才會出現——於是緩衝區在傳送端減速之前幾乎已經完全填滿。
- 哪個方向被跑滿,哪個方向就膨脹。 一次大上傳會讓上行佇列膨脹(你的出站流量在等待);一次大下載則會讓電信業者或數據機一側的佇列膨脹。任何一個都足以讓連線上的其他一切感覺變慢。
- 電信業者端設備同樣可能攜帶同樣過大的緩衝區,不只是你自己的路由器——這也是為什麼換掉家用路由器後,緩衝區膨脹有時依然存在。
解決方法:主動佇列管理與 FQ-CoDel
修復方法不是換更大的方案——而是管理佇列,而不僅僅是讓它變大。**主動佇列管理(AQM)**會在佇列變長之前主動決定何時丟棄或標記封包,而非傳統的「尾端丟棄」做法——只在緩衝區完全占滿後才丟包。
現代標準是 FQ-CoDel(Fair Queuing with Controlled Delay,公平排隊 + 受控延遲),在 RFC 8290 中被標準化為一種實驗性演算法。它同時做兩件事。
CoDel 關注的不是佇列裡堆了多少個封包,而是每個封包在佇列裡實際停留了多久。一旦這個停留時間在一個觀察窗口(預設 100 毫秒)內持續高於一個很小的目標值(預設 5 毫秒),它就開始丟包,藉此告訴傳送端降速,把佇列壓得很淺,不讓它愈堆愈深。公平排隊則用雜湊把流量分散到大量按連線劃分(per-flow)的獨立佇列中(預設 1024 條),再輪流調度,讓一個大傳輸無法把排在它後面的一切都餓死。正是這個組合,讓一次單獨的大檔案下載不再能毀掉同一條連線上的視訊通話。
要用上這個方案,不一定需要新硬體。多數基於 OpenWrt 的路由器,以及愈來愈多來自電信業者與路由器廠商的原廠韌體,都以 智慧佇列管理(Smart Queue Management,SQM) 或類似的「緩衝區膨脹修復」開關提供這項功能——通常會同時給出 fq_codel 與 cake 兩個選項,後者是更新一代的改良方案,把流量整形與佇列管理打包成一個調校好的整體。
真正容易設錯的是速率那一項。要把設定的上下行速度設得比你實測的實際頻寬略低一些:這是刻意把瓶頸製造在你自己的路由器上,讓佇列堆在這台你能控制其佇列策略的裝置裡,而不是堆進你管不著的上游電信業者設備。這一點在下行方向尤其關鍵,因為下行的控制天生是間接的——封包一旦已經抵達你的路由器,就不可能再「退回去」,唯一的手段就是靠丟包或標記,讓遠端傳送方主動降速。要有心理準備:這會犧牲一部分尖峰吞吐(在上面那次 APNIC 實測中大約 10%),同時也要確認你的路由器有足夠的 CPU 餘裕按線速做整形——AQM 並不是免費的。
如何在家測試緩衝區膨脹
只跑一次測速是發現不了它的,因為測速工具是在開始傳資料之前量 Ping 的——而那正是佇列空著的時刻。訣竅在於:先把線路弄忙,再去量。跑兩次就夠了:
- 先拿到閒置基準。 在網路沒有其他活動時,跑一次 BrowserInsight 的網路測速,記下它給出的延遲與抖動,以及你的上下行速度。
- 把線路跑滿,而且要一直保持。 開始一個一分鐘後還沒結束的持續傳輸——例如一次大檔案上傳或雲端備份、一個正在下載的遊戲更新,或乾脆讓同一網路下的另一台裝置去跑測速。用上傳通常最容易暴露問題,因為上行頻寬一般是兩者中較小的那個。
- 趁這個傳輸還在跑,再測一次速。 由於測速工具是在每次執行的開頭讀取延遲,所以你按下開始之前,線路就必須已經處於負載狀態。此時得到的延遲與抖動,就是你的負載數值。
- 比較兩者。 如果負載延遲是閒置值的好幾倍——從幾十毫秒變成幾百毫秒——那就是緩衝區膨脹,而且抖動通常也會跟著一起漲。一條佇列管理良好的連線,即使在滿載下,兩次讀數也會維持接近。
關於閒置數值本身的意義,可參閱頻寬、延遲與抖動詳解;如果你的讀數在多次測試之間看起來不太一致,為什麼你的網路測速結果一直在變說明了其他常見原因,以免把正常波動誤認成緩衝區膨脹。
常見問題
換更大的方案能修復緩衝區膨脹嗎?
不能。緩衝區膨脹是排隊/延遲問題,不是容量問題——更大的方案甚至可能讓情況更糟,因為一台按舊的、較低速度設定的路由器,會在自身的整形機制生效之前,讓更大一波資料先排起隊來。真正有用的是修好佇列管理(AQM/FQ-CoDel),而不是方案大小。
緩衝區膨脹是電信業者的問題,還是我路由器的問題?
兩者皆有可能,甚至兩者兼有。消費級路由器普遍出廠就帶著過大的預設緩衝區,但電信業者提供的數據機或 ONT——以及更上游的設備——也可能攜帶同樣的問題。先透過自己的路由器測試負載 Ping,能幫你判斷修復應該在本地進行(開啟 SQM),還是需要聯繫電信業者。
為什麼我自己沒在上傳,通話與遊戲也會卡?
共享路徑上任何被跑滿的佇列都會影響穿過它的每一條流量,不只是把緩衝區填滿的那一次傳輸。如果家裡其他人開始一次大下載,或你自己的裝置在背景啟動了一次備份,佇列會以同樣的方式被填滿,而你的通話或遊戲——它們對延遲遠比對頻寬敏感——就會首當其衝。
不用買新硬體也能修復緩衝區膨脹嗎?
通常可以。許多現有路由器,尤其是執行 OpenWrt 或類似第三方韌體的,已經支援智慧佇列管理 / FQ-CoDel,只需要開啟一個設定項。只有在原廠韌體不提供這個功能時,換新(或刷機)硬體才是現實的解法。
結語
一個漂亮的下載數字和一條感覺卡頓的連線並不矛盾——它們正是緩衝區膨脹的典型表現:過大的緩衝區在連線一忙起來時把封包排隊積壓,而非及時送達。閒置 Ping 會把它完全隱藏起來;只有負載 Ping 測試才能揭示它。修復方法不是加更多頻寬,而是主動佇列管理——尤其是 FQ-CoDel——而多數路由器如今都能開啟它。在自己的連線上測一測閒置 Ping 與負載 Ping,如果落差很大,需要修的是佇列,而不是升級方案。
推薦閱讀:


