本篇的目的就是要在本機端大量寫入資量,讓次節點的更新開始跟不上主節點,藉由這樣教你如何觀察這個現象以及處裡。
請直接參考這篇之前文章,在 local 啟動一組 MongoDB replica set,並直接連上 primary。
準備了這個 script,大量寫入資料進資料庫中。mongo --host localhost --port 27668 < 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();
隨著資料量的增長,可以看到 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"}})
陸續下了幾個指令查看次節點資訊,我們看看發生什麼事情
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,會跟得上嗎?這有待商榷,但是可以確定的是主節點壓力又更重了,這可能會導致雪崩式的中斷服務,因此這問題一定都需要即時監控的。
目前來看,如果不想要自己寫服務接收指令回傳的資訊,大概都是要使用雲端收費服務才能有圖表監控功能了。
replace
是否可以改用 update
特定欄位即可(replace對主節點更新速度最快,比 update 快到約 30%,所以請衡量使用情境)TTL
很好用,但它也是對 DB 進行 Delete
,大資料量同時觸發 TTL,也是一個負擔。網路上看到的解決方法多半都只有這兩個:
oplog size
,這個做法就是用空間換時間,爭取延長尖峰期觸發 full resync 的時間。replWriterThreadCount
,提高次節點存取 thread 數。預設值是 16
。這樣做代價就是記憶體使用率也會拉高。(這個項目在 MongoDB Atlas 似乎沒有辦法設定,但是5版的速度提升後,可能也不用改了)本系列文章會同步發表於我個人的部落格 Pie Note