iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
Software Development

MongoDB披荊斬棘之路系列 第 20

DAY20 MongoDB Oplog 玩壞它

  • 分享至 

  • xImage
  •  

DAY20 MongoDB Oplog 玩壞它

把手弄髒,親眼於本機見證節點同步跟不上

本篇的目的就是要在本機端大量寫入資量,讓次節點的更新開始跟不上主節點,藉由這樣教你如何觀察這個現象以及處裡。

step1 建立 replica set

請直接參考這篇之前文章,在 local 啟動一組 MongoDB replica set,並直接連上 primary。

step1 寫入大量資料

準備了這個 script,大量寫入資料進資料庫中。
mongo --host localhost --port 27668 < test-data-script.js

  • 27668 是你的 primary node port
  • test-data-script.js 是腳本檔案名稱
use ith2021;

var TestDataCreator = {
    template: function () {
        return {
            "FieldA" : NumberDecimal("10"),
            "FieldB" : NumberDecimal("10"),
            "FieldC" : NumberDecimal("10"),
            "FieldD" : NumberDecimal("10")
        };
    },

    insertTestData: function () {
        let that = this;
        let batchSize = 10000;
        let totalBatch = 100000;

        for (let otrItr = 0 ; otrItr < totalBatch ; otrItr++) {

            let models = [];
            for (let itr = 0 ; itr < batchSize ; itr++) {
                let model = {};
                model.insertOne = {};
                model.insertOne.document = that.template();
                models.push(model);
            }

            db.getCollection('test-data').bulkWrite(models, {ordered: false});
            print(new Date() + " inserted batch ***");
        }
        print(new Date() + "insert " + totalBatch + " batches all done ***");

    },

    start: function () {
        this.insertTestData();
    }
};

TestDataCreator.start();

step2 發動毀滅性語法

  • 先準備好幾個 terminal 視窗,分別連上各個節點。

隨著資料量的增長,可以看到 oplog 偶有點小小延誤

ith2021-rs [direct: primary] ith2021> rs.printSecondaryReplicationInfo()
source: mongo_node1:27666
{
  syncedTo: 'Sun Sep 12 2021 22:07:46 GMT+0800 (台北標準時間)',
  replLag: '-2 secs (0 hrs) behind the primary '
}
---
source: mongo_node2:27667
{
  syncedTo: 'Sun Sep 12 2021 22:07:46 GMT+0800 (台北標準時間)',
  replLag: '-2 secs (0 hrs) behind the primary '
}

好像很可以,資料落差還在 5 秒內,這時只要去更改大量資料,就會產生大量差異。`

db.getCollection('test-data-2021-06-28').updateMany({"FieldA":10}, {$set:{"FieldA":"10-u"}})

step3 資料同步開始脫勾了

陸續下了幾個指令查看次節點資訊,我們看看發生什麼事情

test-RS:PRIMARY> db.printSlaveReplicationInfo()
source: test-rs-mongo2:27018
	syncedTo: Mon Jun 28 2021 00:51:18 GMT+0800 (CST)
	13 secs (0 hrs) behind the primary
source: test-rs-mongo3:27019
	syncedTo: Mon Jun 28 2021 00:51:17 GMT+0800 (CST)
	14 secs (0 hrs) behind the primary

test-RS:PRIMARY> db.printSlaveReplicationInfo()
source: test-rs-mongo2:27018
	syncedTo: Mon Jun 28 2021 00:51:46 GMT+0800 (CST)
	27 secs (0.01 hrs) behind the primary
source: test-rs-mongo3:27019
	syncedTo: Mon Jun 28 2021 00:51:41 GMT+0800 (CST)
	32 secs (0.01 hrs) behind the primary

test-RS:PRIMARY> db.printSlaveReplicationInfo()
source: test-rs-mongo2:27018
	syncedTo: Mon Jun 28 2021 00:56:11 GMT+0800 (CST)
	167 secs (0.05 hrs) behind the primary
source: test-rs-mongo3:27019
	syncedTo: Mon Jun 28 2021 00:56:20 GMT+0800 (CST)
	158 secs (0.04 hrs) behind the primary

這時我們看看主節點的資訊

test-RS:PRIMARY> db.printReplicationInfo()
configured oplog size:   990MB
log length start to end: 305secs (0.08hrs)
oplog first event time:  Mon Jun 28 2021 00:54:03 GMT+0800 (CST)
oplog last event time:   Mon Jun 28 2021 00:59:08 GMT+0800 (CST)
now:                     Mon Jun 28 2021 00:59:15 GMT+0800 (CST)

可以看到主節點還是不斷得在寫入新的資料,但是次節點的同步已經來到落後一百多秒了,再下去就會讓 oplog 滿出來,接著觸發 full resync,讓次節點失效。

其實就是透過這樣的方式實驗,在大流量時很容易就把同步機制弄到掛掉。


這邊要特別解釋一下關於 step3,我無意欺騙大家,step3 的內容是我在幾個月前在本機電腦跑出來的結果。

而幾個月後的現在,我改用 5版的 MongoDB 卻跑不出來了,用的步驟和手法是一模一樣的,暫時認為 MongoDB 在 Oplog 機制改善了不少,所以整體來說應該是件好事啦XD。

在測試過程中,資料量上到一千五百萬左右,還是有遇到某個節點直接崩潰的情形。


解決方案

其實說穿了目前的快速解決方案就是兩個,長期下來我還是認為治標不治本。MongoDB 資料同步這一塊在網路上很多人都會遇到,應該說只要把產品上線,資料量大一點就馬上會體會到,最常聽到的莫過於兩個人同時使用系統,但是查詢結果不同。

一般來說使用 replica set,主要目的也是讀寫分離,讓收資料端能夠發揮最大效益,而查詢面,只要不是最即時的需求,都會放到次節點去讀取。

oplog 剩餘的時間,代表的是 觸發全同步的剩餘時間,意思是你的次節點會停止服務,接著進行同步,直到全部與主節點同步為止。但這件事背後的隱憂是,就是因為資料來不及同步,才會觸發,那現在次節點無法服務,又同時在進行 full resync,會跟得上嗎?這有待商榷,但是可以確定的是主節點壓力又更重了,這可能會導致雪崩式的中斷服務,因此這問題一定都需要即時監控的。

目前來看,如果不想要自己寫服務接收指令回傳的資訊,大概都是要使用雲端收費服務才能有圖表監控功能了。

根除問題

  1. 以商業邏輯角度,確認是否需要對 DB 進行這麼大量的操作
  2. 這些操作是否可以分散到離峰時間進行
  3. replace 是否可以改用 update 特定欄位即可(replace對主節點更新速度最快,比 update 快到約 30%,所以請衡量使用情境)
  4. TTL很好用,但它也是對 DB 進行 Delete,大資料量同時觸發 TTL,也是一個負擔。
  5. 花更多錢,將高頻、併發的需求放在第一組 replica set,而查詢或統計類型的資料放在第二組 replica set,以更緩慢的方是進行寫入/同步,以減輕其壓力。

特效藥,花錢

網路上看到的解決方法多半都只有這兩個:

  1. 加大 oplog size,這個做法就是用空間換時間,爭取延長尖峰期觸發 full resync 的時間。
  2. 加大 replWriterThreadCount,提高次節點存取 thread 數。預設值是 16。這樣做代價就是記憶體使用率也會拉高。(這個項目在 MongoDB Atlas 似乎沒有辦法設定,但是5版的速度提升後,可能也不用改了)

本系列文章會同步發表於我個人的部落格 Pie Note


上一篇
DAY19 MongoDB Oplog 是什麼?邁向高手之路
下一篇
DAY21 MongoDB Profiler 如何監控效能差的操作
系列文
MongoDB披荊斬棘之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言