DatabaseMongoDBPerformance

MongoDB 寫入 Timeout 的排查:Write Ticket 到底卡在哪?

作者:Simon Cho•
MongoDB 寫入 Timeout 的排查:Write Ticket 到底卡在哪?

以前遇到 MongoDB 的效能問題,十之八九都是讀太慢。加個 index、調整一下 schema,通常就能處理掉。

但這幾年開始碰到的是寫入問題,而且有一次讓我卡了很久:同一支 API,只有某一組參數會噴 Transaction Timeout,其他參數都沒事。一開始看起來很像單純資料量太大,但後來才發現,事情比想像中繞了一點。


問題

那組參數帶進來後,準備寫入的資料確實比較多。所以當時的判斷很直覺:這個 transaction 應該跑超過 60 秒,然後就被 timeout 了。

不過資料多只是一個現象,不代表 MongoDB 就是慢的地方。真的開始拆流程後,才發現前面還有一段 JavaScript 在拖時間。

JavaScript 運算

這個 case 的 transaction 會先經過大量 JavaScript CPU 運算,再進入 MongoDB 寫入流程。

這個 case 的 transaction 會先經過大量 JavaScript CPU 運算,再進入 MongoDB 寫入流程。

這個 case 中間會用 JavaScript 遞迴處理大量、而且結構不太一樣的 object。這種資料丟給 V8 並不討喜,沒辦法好好走到 machine code 的最佳化路線,光是這段轉換就吃掉了不少時間。

把這段重構掉之後,JavaScript 的延遲基本上就消失了。這也是我後來會先做的事:transaction timeout 不要看到 MongoDB 就一路往 DB 查,先調查有哪些 CPU heavy 的運算。

Dirty Cache 和 Write Ticket

接著我用 MongoDB Compass 看 local DB。每次 API 一打,圖表上的指標都往上衝,但我還是霧煞煞,不知道該從哪裡下手。

後來在 Agent 提醒下,才知道有兩個重要指標不是 Compass 裡直接看得到的,要自己用指令撈:Dirty Cache Ratio 和 Write Ticket。

Dirty Cache

MongoDB 啟動時會拿一部分記憶體當 cache,資料要讀的時候能先從記憶體找。資料一改,記憶體和硬碟上的內容暫時不一樣,這些還沒寫回硬碟的內容就是 dirty cache。

Dirty Cache 比例高到一定程度時,MongoDB 會很積極地把資料 flush 回硬碟。回寫忙不過來時,新的寫入也會被拖住。

Write Ticket

Write Ticket 是 WiredTiger 控制同時能有多少個 operation 寫入記憶體和硬碟的方式。可以把它想成入場券:ticket 用完了,後面的寫入就只能等前面的工作把 ticket 還回來。

Write Ticket 歸零不一定是 bug,它比較像 MongoDB 在踩煞車,避免 cache 跟 storage 被壓垮。但如果等太久,transaction 就一起被拖到 timeout。

指標

把指標放在一起看後,脈絡就比較清楚了。那支 API 讓 Dirty Cache Ratio 一直上下震盪,而且衝得夠高,MongoDB 得一直回寫硬碟;回寫壓力一大,Write Ticket 真的被用到 0。

後面的寫入開始排隊,transaction 時間就一路被拉長,最後 timeout。

資料量比較大的請求一進來,JavaScript 運算、cache 壓力和磁碟回寫就一起疊上去。只盯著其中一個圖表,很難看出這條鏈。

解法

當下最直接的做法就是升級 DB,把記憶體加大。cache 空間變大後,Dirty Cache Ratio 降了下來,不會那麼常被迫停下來等回寫,Write Ticket 也就不再一直被耗盡。Timeout 到這裡算是先止血了。

但機器升級只是先讓服務恢復穩定,後面還是得回頭優化那支寫入 API。一次 transaction 裡面塞太多資料轉換或不必要的工作,規格再大也只是把問題往後推。

本機重現

這個問題發生在雲端,但我一開始怎麼在本機都重現不了。後來才發現不是問題消失了,是我的本機比雲端機器強太多,運算跟回寫都快,根本壓不出同樣的狀況。

我那時用 Docker Compose 跑 MongoDB,所以 CPU 和 memory 都要刻意限制到接近雲端規格。Node.js 也一樣:V8 的 call stack 雖然是 single thread,但 core 的能力和 worker thread 的數量都會影響結果。

← 返回所有故事