一個運轉一年多的 NodeJS 專案,每分鐘向 InfluxDB 寫入 40~50 個 point,某天發現不時出現ETIMEOUT以及No host available連接問題,導致部分資料無法儲存。

1. 不是網絡問題

這個 InfluxDB 實例運行在 Docker container 內,與 NodeJS 程序放在同一個 CentOS 機器上。influxDB 的 log 看不到異常,只有 NodeJS 報連接問題。

第一時間就想是不是本機 network interface 與 Docker / container 之間連接出錯。然而無論重啟或更新 Docker 服務,情況也未有改變。

的確,如果是本機 network 問題,多半是完全無法連接。(曾經有次 Linux 的網路服務自己出包了,要手動重啟。)

程序同時連接另一個運行 MongoDB 的 container,Docker 服務更新前後都沒有類似連接問題,也應該不是 Docker 代碼出包。

這個問題也在 Github 上被提出過 。當接收到數次ETIMEOUT後,node-influx 會把這個 influxDB 從連接池移除。因為這裡就只得一個 influxDB,移除後自然就No host available了。

但為甚麼 timeout 呢?

2. 官方的建議

事實上我還未找到一個確切的回答,不過 InfluxData 在他們的文檔提及一些操作姿勢。

2.1 時間戳精度

InfluxDB 文檔中的 FAQ 寫道

To maximize performance, use the coarsest possible timestamp precision when writing data to InfluxDB.

「為了使性能最大化,在將數據寫入 InfluxDB 時,請使用最粗略的時間戳精度。」

文檔記載 時間戳精度可為︰ns(納秒)、u(微秒)、ms(毫秒)、s(秒)、m(分鐘)、h(小時)

原本我沒有指定精度,使用預設的ns。現在改成m,是專案可接受最粗略精度了。用 node-flux 的話這樣加一行︰

1
2
3
4
5
influx.writePoints([...points], {
    precision: "m",
    retentionPolicy: "raw",
    database: "my_db",
});

2.2 批量寫入

在 node-influx 的文檔中寫著 這麼一句

For best performance, it’s recommended that you batch your data into sets of a couple thousand records before writing it.

「為了獲得最佳性能,建設您把資料以數千條為一組,批量寫入。」

所以我增加一個 array,暫存要寫入的 point,僅當這個 array length >= 1000 或者第一個 point 已經暫存超過一分鐘才進行influx.writePoints()動作。當然,寫入條件隨你按實際情況定義,我的情況就大約每分鐘一次寫入 40~50 個 point。

如果使用 Redis 或類近方案作暫存會更好,避免程序崩潰而令資料流失。

另外,如果influx.writePoints()再出現ETIMEOUTNo host available情況時,可以把資料放回暫存,等待下一次嘗試。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
const buffer = [];

//...
if (buffer.length >= 1000 || (buffer.length > 0 && buffer[0].timestamp.getTime() >= 60000) {
    const mb = buffer.splice(0, buffer.length);
    try {
        await influx.writePoints(mb, {
            precision: "m", //指定精度
            retentionPolicy: "raw",
            database: "my_db",
        });
    } catch (err) {
        //如不能寫入則放回暫存
        buffer.splice(buffer.length, 0, ...mb);
    }
}
//...

但要留意一點,你需要為每個 point 指定timestamp屬性,避免讓 influx 設定,因為重試機制會令時間戳變得不正確。

2.3 內部監控 (Internal monitoring)

InfluxDB 文檔在描述內部監控功能前 高調表示

Not recommended for production environments.

「不建議在生產環境中使用。」

就是強烈建議你停用預設開啟的內部監控功能。

我的 InfluxDB 是用官方的 Docker image,又嫌麻煩不想改 設定檔 ,就直接下環境變數做修改︰

-e INFLUXDB_MONITOR_STORE_ENABLED=false

同樣方法還可以停止上傳使用統計︰

-e INFLUXDB_REPORTING_DISABLED=true

3. 效果

ETIMEOUT以及No host available依然會出現,但頻率低了很多,而且由於順道引入了重試機制,資料最終也寫入到InfluxDB,沒有因為連接問題而流失。

4. 我做了甚麼

官方並沒有解釋為甚麼會出現這樣的問題,亦沒有清楚説明上述的優化到底目的何在。以下是我的猜測:

  1. 降低時間戳精度可以稍微減少資料量,提高壓縮率
  2. 每次寫入資料到InfluxDB時,會直接儲存到硬盤上
  3. 如果寫入頻率太高,硬盤速度會追不上,令大量寫入請求超時
  4. 如果資料量少,也會做成檔案系統的 寫放大 問題
  5. Internal Monitoring沒有實際用處的話,也可以被視為軟件層級的寫放大
  6. 使用InfluxDB頻繁寫入、以及 Retention Policy 重寫資料,令SSD加速老化,導致資料儲存速度減慢,令問題浮現