遊戲 Lag 白皮書 › 依症狀查找
輸入延遲:76 個原因與負責團隊
其他說法:反應慢半拍、操作遲鈍、沒有手感
在含圖解的完整版症狀辭典中開啟 →
按下去之後要等一段時間才看到結果。畫面本身可能很流暢。
按下技能鍵 0.2~0.5 秒後才發動。撿東西、對話、交易這類需要確認的動作都很慢。
往返時間(ping)很長,或是某處的佇列堆積起來。要查距離、分享器佇列、Nagle(把小封包湊在一起再送的 TCP 功能)、伺服器佇列。ping 很低卻總是遲鈍的話,就查垂直同步(V-Sync)、FPS 偏低這類自己電腦端的因素,或是每個動作都要等伺服器確認的設計(見「同步方式」一章)。
造成這個症狀的原因
L1 用戶端遊戲程式
- 大量角色同畫面的渲染負載: 攻城戰、世界王這類數百人擠進同一個畫面的場合,光是繪製的成本就負荷不了。 (遊戲開發團隊(用戶端開發))
- 主執行緒封包處理瓶頸: 每個畫格只處理固定數量的已接收封包時,一次湧入的封包會不斷延到下一個畫格。 (遊戲開發團隊(用戶端開發))
- 垂直同步(V-Sync)與渲染佇列: GPU 畫好的畫格會先在佇列裡堆上幾張,再配合螢幕的更新週期送出,這段期間輸入就被延遲。 (遊戲開發團隊(用戶端開發))
L2 用戶端 OS 與裝置
- 省電模式、過熱降頻: 筆電的電池模式、手機的省電模式、裝置過熱,都會讓 CPU、GPU 速度下降。過熱的特徵是一開始正常,過了一段時間才變慢。 (外部(外部))
- 同一台裝置上的其他 App 占用頻寬: 雲端同步、大型下載、遊戲更新在同一台電腦上執行時,遊戲封包得在佇列中等待。 (外部(外部))
- 顯示器、輸入裝置與畫格生成的延遲: ping 正常、操作卻很沉重時,可能是電視的影像處理、無線控制器或畫格生成功能在輸入與畫面之間增加了延遲。 (外部(外部))
L3 家用網路
L4 網際網路線路
- 傳播延遲(物理距離): 光在光纖中 1 秒也只能前進約 20 萬 km。伺服器在遠方,效能再好也會慢。 (基礎設施團隊(伺服器基礎設施))
- 衛星網路(低軌/同步軌道): 衛星網路的電波必須往返太空。同步軌道衛星光是往返就超過 0.5 秒;Starlink 這類低軌衛星平時很快,但在重新分配路徑的瞬間延遲會忽高忽低,有時還會短暫中斷。 (外部(外部))
- 繞遠路的路由: 受電信業者之間互連合約的影響,連到附近的伺服器也可能繞到遠處再回來。 (基礎設施團隊(網路基礎設施))
- 海底電纜/國際線路故障: 海底電纜一旦斷裂,修復前的幾週(長則幾個月)流量都得繞遠路,剩下的線路也會壅塞。 (外部(外部))
- 電信業者限速與流量管理: 在用量超過上限或會管理特定流量的資費方案中,封包會被延後或丟棄。 (外部(外部))
- 經由 VPN/遊戲加速器: 開啟 VPN 或遊戲加速器後,封包會經過該公司的中繼伺服器。中繼伺服器很遠或壅塞時,反而會變慢。 (外部(外部))
L5 資料中心網路設備
- DDoS 防護導流與誤判: 為了擋下攻擊而把流量導向清洗中心時,路徑會變長,有時還會把正常使用者誤判為攻擊而封鎖。 (基礎設施團隊(網路基礎設施))
- 資料中心線路飽和: 更新檔發布、log 傳送、備份與遊戲共用同一條線路時,線路會被塞滿。 (基礎設施團隊(網路基礎設施))
L6 伺服器網路卡
- NIC 中斷集中在單一核心: NIC 只把封包到達的中斷(interrupt)送給一個 CPU 核心時,那個核心就會成為瓶頸。 (基礎設施團隊(伺服器基礎設施))
- 中斷合併過度: 為了減輕 CPU 負擔,NIC 把封包累積起來再一次通知 CPU(中斷合併,interrupt coalescing)時,累積多久就會晚多久。 (基礎設施團隊(伺服器基礎設施))
- NIC 頻寬飽和: 把 1Gbps、10Gbps 網路卡用到極限時,傳送佇列會變長,最後封包被丟棄。 (遊戲開發團隊(伺服器開發))
- GRO/LRO 合併等待延遲: 這是把多個封包合併成一個來減輕 CPU 負擔的功能。依設定不同,小型遊戲封包有時會短暫等待下一個可以一起合併的封包。 (基礎設施團隊(伺服器基礎設施))
L7 伺服器 OS(kernel)
L8 Socket 與協定
- Nagle 演算法 + 延遲 ACK: 把小封包集中起來送的 Nagle 演算法,與延後送出 ACK 的延遲 ACK 互相牽制,每次把訊息分段寫入就會延遲 40~200ms。 (遊戲開發團隊(伺服器開發))
- 閒置後的慢啟動(slow start): 連線閒置一段時間後,TCP 會再次縮小壅塞視窗(一次能送出的量),所以突然要送大量資料時,得分成好幾次送出。 (基礎設施團隊(伺服器基礎設施))
- 壅塞控制造成傳送量驟降: TCP 把封包遺失視為壅塞的訊號,會把傳送速率降低 30~50%。對 Wi-Fi 造成的遺失也會做出同樣的反應。 (基礎設施團隊(伺服器基礎設施))
- 阻塞式 I/O 架構: 執行緒在等待某個 socket 時無法做其他事的架構,玩家越多整體就越慢。 (遊戲開發團隊(伺服器開發))
L9 伺服器遊戲程式
- 超出 tick 預算: 一個 tick 內要做的事超出預算時,伺服器的 tick 週期會拉長,整個區域的遊戲進行變慢,或出現卡頓。 (遊戲開發團隊(伺服器開發))
- 廣播量暴增: 把一個人的移動送給所有看得到他的人時,要送出的狀態更新數量會是聚集人數的平方。 (遊戲開發團隊(伺服器開發))
- 單執行緒區域過載(熱點): 在每個區域由一個執行緒負責的架構中,人潮擠到同一處時,只有那一個核心會到 100%。 (遊戲開發團隊(伺服器開發))
- 鎖競爭: 多個執行緒為了寫入同一份資料而等待同一個鎖時,執行緒再多也只能一次執行一個。 (遊戲開發團隊(伺服器開發))
- 訊息佇列積壓: 請求進來的速度比處理速度快而在佇列中累積時,排在後面的請求要幾秒後才會處理,或被丟棄。 (遊戲開發團隊(伺服器開發))
- 序列化與壓縮的成本: 把要送出的資料轉成位元組並壓縮也要耗用 CPU,人多時這項成本會暴增。 (遊戲開發團隊(伺服器開發))
- 執行緒池耗盡: 負責處理工作的工作執行緒全被慢的工作綁住時,新請求只能無止盡地等待。 (遊戲開發團隊(伺服器開發))
- 集中在單一目標的戰鬥(世界王): 數百人同時攻擊同一隻王時,這隻王的運算全集中在一處,打擊資訊也要傳送給所有看得到的人。 (遊戲開發團隊(伺服器開發))
- 進入密集區域時的生成暴增: 用傳送移動到擠滿人的城鎮時,伺服器必須一次送出數百位新進入視野者的外觀、裝備與狀態。 (遊戲開發團隊(伺服器開發))
- 物件累積(未清理的道具、召喚物): 該消失的地上道具、召喚物、已結束的計時器沒有清理而不斷累積時,伺服器開得越久,每個 tick 要做的事就越多。 (遊戲開發團隊(伺服器開發))
- 更新改變了流量模式: 新內容、特效、同步項目讓封包變大、變頻繁時,原本運作正常的伺服器在更新後就會碰到 MTU、頻寬、封包數上限。 (遊戲開發團隊(伺服器開發))
L11 磁碟
- fsync 暴增: 要求把資料「確實」寫入磁碟時,依磁碟不同,每次需要 0.1ms~數十 ms;請求一多,佇列就會變長。 (遊戲開發團隊(伺服器開發))
- 雲端磁碟 burst credit 耗盡: 部分雲端磁碟與小規格伺服器有 burst credit(突發額度),可以短時間跑得比基準效能快;忙碌時段一拉長、額度用完,速度就會突然下降。 (基礎設施團隊(伺服器基礎設施))
- IOPS 上限與佇列飽和: 請求數超過磁碟每秒能處理的數量時,佇列會變長,延遲急遽增加。 (基礎設施團隊(伺服器基礎設施))
- 備份、壓縮、掃描作業: 凌晨的備份、log 壓縮、安全掃描獨占磁碟時,遊戲伺服器的讀寫就會被擠到後面。 (基礎設施團隊(伺服器基礎設施))
- HDD 尋軌延遲: HDD 的讀寫頭必須在碟片上移動(尋軌,seek),所以讀寫分散各處的資料時,每次要花將近 10ms。 (基礎設施團隊(伺服器基礎設施))
L12 資料庫
- 沒有索引的查詢: 沒有索引時,要找出符合條件的資料列,就必須讀完整張資料表(全表掃描)。 (遊戲開發團隊(伺服器開發))
- 熱點資料列鎖定競爭: 所有人都要修改同一筆資料列(公會倉庫、拍賣場熱門道具、全伺服器共用的計數器)時,一次只有一個請求能取得鎖定。 (遊戲開發團隊(伺服器開發))
- DB 死結: 兩個 transaction(當成一個整體處理的一組 DB 操作)互相等待對方鎖住的資料列時,DB 會強制取消其中一方。 (遊戲開發團隊(伺服器開發))
- 連線池耗盡: 與 DB 建立的連線數是固定的,慢查詢占住連線時,其餘請求只能等待。 (遊戲開發團隊(伺服器開發))
- 檢查點與 log flush: DB 定期把記憶體中的變更一次大量寫入磁碟的瞬間,查詢會變慢。 (基礎設施團隊(DB 基礎設施))
- 冷快取(剛重新啟動時): DB 重新啟動後記憶體快取是空的,有一段時間所有查詢都要從磁碟讀取。 (基礎設施團隊(DB 基礎設施))
- 登入暴增與 N+1 查詢: 載入一個角色要分開查詢數十次的話,數萬人同時登入就會變成數百萬個查詢。 (遊戲開發團隊(伺服器開發))
- 大量批次作業: 在營運時段執行排行榜彙總、信件批次發送、舊資料清理時,會占用鎖定與磁碟。 (遊戲開發團隊(伺服器開發))
- Cache stampede: 熱門資料的快取同時過期時,數千個請求會一口氣湧向 DB。 (遊戲開發團隊(伺服器開發))
- 長時間未結束的 transaction: 一個 transaction 長時間不結束時,會一直持有鎖定,DB 也無法清理(purge)舊版本的資料,整體會越來越慢。 (遊戲開發團隊(伺服器開發))
- Redis 慢指令: Redis 一次只處理一個指令,因此一個慢指令就會擋住後面所有的請求。 (遊戲開發團隊(伺服器開發))
- 執行計畫改變造成的查詢延遲: 程式碼沒變,DB 卻改變了處理同一個查詢的方式(執行計畫)時,昨天只要 2ms 的查詢,今天會變成數百 ms。 (基礎設施團隊(DB 基礎設施))
- 營運中 schema 變更(DDL)的鎖定: 在服務中替資料表新增欄位或索引時,可能因為一個只需要短暫持有的鎖定,讓所有使用該資料表的請求都在等待。 (基礎設施團隊(DB 基礎設施))
L13 伺服器架構與維運
- 經由閘道/proxy: 在用戶端與遊戲伺服器之間放一台中介伺服器時,每經過一次就多一段處理時間,而且這台伺服器會成為單點故障。 (遊戲開發團隊(伺服器開發))
- 連鎖故障: 一個服務變慢時,呼叫它的伺服器會卡在等待回應,連不相關的功能也跟著停擺。 (遊戲開發團隊(伺服器開發))
- 部署與重新啟動: 為了更新而重新啟動伺服器時,如果不先把連線移走,原本在那台伺服器上的人就會斷線,關閉前的存檔與重新連線也會同時湧入。 (遊戲開發團隊(伺服器開發))
- 巨集/機器人過多: 機器人送出請求的頻率遠高於真人,會吃掉伺服器的處理量。 (遊戲開發團隊(伺服器開發))
- 配對與區域分配錯誤: 沒被分到近的區域、卻被分到遠方區域的伺服器時,即使線路正常,也只有那位玩家的 ping 一直偏高。 (遊戲開發團隊(伺服器開發))
同步設計
- 伺服器回應後才演出(請求-回應方式): 按下按鈕後,在伺服器回應之前既沒有動畫也沒有音效。ping 直接就是反應速度。 (遊戲開發團隊(用戶端開發))
- 依序往返過多的協定(chatty): 一次操作需要依序與伺服器往返好幾次時,ping 會乘上這個次數。 (遊戲開發團隊(伺服器開發))
- 沒有技能預輸入: 必須收到伺服器確認前一個技能已結束才能按下一個技能時,每次連段之間都會夾著一段往返時間。 (遊戲開發團隊(用戶端開發))
- 被 ping 吃掉的短判定區間: 閃避、格擋、防禦這類需要反應的時間很短時,ping 會把這段時間吃掉,出現躲不掉的攻擊。 (遊戲開發團隊(伺服器開發))
- Lockstep 中等待最慢的玩家: 在所有人一起計算同一回合的架構中,只要一個人的輸入晚到,所有人都要等。 (遊戲開發團隊(伺服器開發))
- 雙重 tick 等待: 請求先累積到下一個 tick 才處理,結果又等到再下一個 tick 才送出時,tick 間隔會被加上兩次。 (遊戲開發團隊(伺服器開發))
只有部分人遇到的問題
- 各玩家的輸入緩衝大小: 伺服器替每個人暫存一點輸入、每個 tick 取出一個使用時,在其他人眼中很流暢,但本人的動作在伺服器上確定的時間點也會晚上這麼多。 (遊戲開發團隊(伺服器開發))
- 一個慢的隊友與王的機制: 在所有人必須在指定瞬間一起反應的團隊副本機制中,一個慢的人反應太晚,就會讓整個隊伍失敗。 (遊戲開發團隊(伺服器開發))
- 特定角色的資料過於龐大: 累積了數千個道具或信件,或好友、封鎖名單、buff 特別多的角色,在登入、儲存與通知周圍玩家時要處理的量是別人的好幾倍。與線路無關,只有用那個角色時才慢。 (遊戲開發團隊(伺服器開發))
- 各連線的傳送預算與優先順序: 伺服器對每條連線的傳送量設上限、從近的開始送時,上限被設得較低的那一邊,會較晚收到或收不到遠處的 NPC。 (遊戲開發團隊(伺服器開發))
TCP 重傳的根本原因
- 接收端伺服器主機丟棄封包: 封包已經到了伺服器,卻因 NIC 的 ring buffer(暫存剛抵達封包的緩衝區)溢位,或 kernel 負責接收處理的 CPU 核心滿載而被丟棄。 (基礎設施團隊(伺服器基礎設施))
- 延遲飆升造成的不必要重傳: 封包沒有消失,只是短暫地很晚才到;但這段延遲比 RTO 長時,傳送端就會判斷為遺失而重傳。 (外部(外部))
- 封包亂序造成的不必要快速重傳: 封包經過多條路徑或聚合鏈路時順序被打亂,接收端會以重複 ACK 告知「有封包缺漏」,傳送端就把其實已正常送達的封包再送一次。 (基礎設施團隊(網路基礎設施))
- ACK 延遲或消失(上傳飽和): 資料已順利送達,但「收到了」的 ACK 在塞滿的上傳佇列裡延遲或消失時,傳送端會判斷為遺失而重傳。 (外部(外部))
- RTO 設定不符合環境: RTO 最小值調得太低時,只要稍微延遲就會產生不必要的重傳;預設值(200ms)對遊戲來說又太長,每遺失一次就要停住很久。 (基礎設施團隊(伺服器基礎設施))
查看含圖解的完整版症狀辭典