DAY20 MongoDB Oplog 玩坏它

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


<<:  DAY07_终於要来资产搬点啦~啊~不是~是盘点XDDD" 搬点是要搬家是吗~

>>:  Day 5 - 使用JWT Token帮Laravel 8.0做Authentication

[Day 28] Final Project (4/5) — 部署模型到 Google AI Platform

前情提要 昨天我们成功的让 App 在本机端运作,但按下 开始预测! 後却出现了错误: 这意味着虽然...

[D29] : 一个Queue+Docker在Local的实作(3/4)

前面说过Passer如何把请求发到MQ去 今天讲Center怎麽去找MQ把请求取出来。 Cente...

Day 25 : 经典气泡排序 Bubble Sort

接下来的五天我们会用不同的方式来解这题题目Sort an Array,一起来复习跟朝拜大师们想出来的...

Day 23 - 影像处理篇 - 影像与色彩 - 成为Canvas Ninja ~ 理解2D渲染的精髓

老实说我决定要写影像处理这个部分的时候还蛮犹豫的,因为在javascript 做影像处理的这个领域,...

16 综观各校资工系修课蓝图

资讯工程学系(Computer Science and Information Engineerin...