MongoDB Oplog Stones 实现分析及启动加载优化

本文涉及的产品
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 MongoDB,通用型 2核4GB
简介: 对 Oplog Stones 的实现和初始化流程进行了详细的分析,简单分析了 Oplog 回收的逻辑。并对 oplog stones 的启动加载流程进行了优化,对比有数量级提升。

本文基于 4.2 代码分析

背景

Oplog Collection 首先是作为一个 Capped Collection 来实现的,但是单纯的 Capped Collection 会在每一次的写操作之后,如果发现集合大小超出配置的最大值,就会同步的去进行删除文档的操作

删除文档的步骤大约是,

  • 计算:设置一个 end cursor 指向 capped collection 头部,然后不断的调用 cursor->next(),通过累积 value 大小来确定需要删除的文档数
  • 删除:

    • 需要删除的文档数量小于等于 3 个,直接循环调用 cursor->remove() 删除文档
    • 否则,设置两个 cursor,一个 start,一个 end,start 直接设置在 capped collection 的头部,然后调用 session->truncate(session, start, end) 来批量删除文档,session->truncate() 实际最终也是调用 cursor->remove()

具体代码可参考:WiredTigerRecordStore::_cappedDeleteAsNeeded_inlock

可以看到确定需要删除的文档数是这里面比较耗时的部分,cursor->next() 来统计 value 大小,实际就是在不断的做读取。对于 Oplog Collection 来说,所有的用户写操作都会记录 Oplog,当 Oplog 写满后,每一次的用户写操作都会触发同步的删除操作,显然效率很低。

所以 MongoDB 采用了一种标记删除位点,然后批量删除的策略来解决这个问题。

Oplog Stones 初始化

所谓的 Oplog Stone,实际上就是用 truncate point(删除位点) 在 oplog 上分隔的逻辑区域,而 truncate point 本质上就是 oplog entry 的 ts 字段,同时作为 RecordID,实际也是对应的 WiredTiger Table 中一条记录的 key。

Oplog Stone 的信息 MongoDB 并没有做持久化,而是选择每次重启的时候重新初始化

Oplog Stones 整体初始化的逻辑还是比较简单的,首先是根据 cappedMaxSize 计算需要多少个 stone,

    // The minimum oplog stone size should be BSONObjMaxInternalSize.
    const unsigned int oplogStoneSize =
        std::max(gOplogStoneSizeMB * 1024 * 1024, BSONObjMaxInternalSize);

    // IDL does not support unsigned long long types.
    const unsigned long long kMinStonesToKeep = static_cast<unsigned long long>(gMinOplogStones);
    const unsigned long long kMaxStonesToKeep =
        static_cast<unsigned long long>(gMaxOplogStonesDuringStartup);

    unsigned long long numStones = maxSize / oplogStoneSize;
    size_t numStonesToKeep = std::min(kMaxStonesToKeep, std::max(kMinStonesToKeep, numStones));

这里有很多默认值参与计算,我们需要知道的是,oplog stone 最多 100 个,最少 10 个,如果 oplogSizeMB 配置值超过 2GB,在默认情况下,基本上就需要 100 个 stone(这个是根据 Capped Collection配置的最大值算出来的基准,如果 Capped Collection 实际还没有写满,会根据实际大小来换算,stone 会更少,但是无论如何,上限不会超过 100)。

确定了 Oplog Stone 的个数,下面要做的就是确定每个 Oplog Stone 的边界,分两种情况

  • Oplog 集合当前的 record 数量太少,小于 20 倍的需要 sample 的数量(对于 100 个 stone 来说,每个 stone 默认 sample 10 条记录,所以 Collection record 数量低于 2w 条,就走全表的逻辑),直接通过全表扫描的方式来确定 Oplog Stone 边界,这个逻辑很简单,就是对扫描到的 oplog entry 累加大小,超过单个 oplog stone 大小上限就生成一个 stone,保存下来,直到扫描结束。(代码:WiredTigerRecordStore::OplogStones::_calculateStonesByScanning
  • 否则,就不能通过全表扫描了,效率太低。MongoDB 借助于 WiredTiger 提供的 random cursor 来进行采样,从而快速确定每个 oplog stone 的边界。(代码:WiredTigerRecordStore::OplogStones::_calculateStonesBySampling

    1. 正常来说,100 个 oplog stone,采样 100 次,似乎就可以确定所有 stone 的边界了,但是为了保证边界尽可能准确,MongoDB 采用了 oversampling 的方式,即,对于每一个 stone 采样 10 次(默认),100 个 stone 就采样 1000 次。然后,把这些采集到的 sample 按 key(opTime)排序,每个 oplog stone 使用每 10 个 sample 中最后一个的 key 作为其边界,即,从 0 开始数,那么 9th, 19th, 29th, ……号 sample 是顺序对应的 oplog stone 的边界。
    2. 另外,MongoDB 在后面的版本迭代中还使用了优化的 random cursor,可以保证更精确的采样,通过 next_random_sample_size 告知 WT 需要 random 采样的个数,WT 会把底层的 table 划分为 next_random_sample_size 份,分别从其中获取一个采样,显然这样划分后,每第 10 个 sample 可以更贴近预期的 oplog stone 边界。

这个是进程启动时 oplog stone 的初始化的方式,随着有新的写入,还会创建新的 oplog stone,这个时候 oplog stone 的大小是可以保证精确的,因为在写入的时候,可以很方便的统计到当前已经在最新的 stone 里面写了多少数据,这个数值是精确的。所以,如果初始启动的时候因为 oplog stone 边界不精确导致 oplog 删除的过多或过少,并不是个大问题,这个会在后续的更新中把误差抹掉。

Oplog 回收

有了 Oplog Stone 后,oplog 的回收就不需要再每次写入时去计算要删除的文档数,再同步去删除,只需要在当前 oplog stone 写满后,创建新的 oplog stone 时,把最老的 oplog stone 删除掉即可

这个按 stone 删除通常会一次性删除比较多的文档,所以oplog 的删除动作是放在后台的 OplogTruncaterThread来做的,删除时会直接调用 session->truncate 方法,使用 oldest oplog stone 的边界作为 range truncate 的上界。

//WiredTigerRecordStore::reclaimOplog
            setKey(cursor, stone->lastRecord);
            invariantWTOK(session->truncate(session, nullptr, nullptr, cursor, nullptr));

删除的时候还需要考虑 stable checkpoint 对 oplog 的依赖,具体逻辑后面再发文分析。

Oplog Stones 初始化的时间开销分析

单纯从代码逻辑看,可以看到 Oplog Stones 的初始化的绝大部分时间都会花在 random cursor 的采样上,因为其他的步骤都是简单的 in-memory compute,几个 ms 足以完成。总的初始化时长会和采样的个数成正比,采样个数最多是 1000 个,所以这个初始化时间是有上限的,并不是随 oplog 集合大小无限增长

为了验证上述结论,我们构造一个测试场景来证明。

先构造数据集,创建副本集 1,使用 mongo shell 自带的 benchmark 工具生成数据,

benchRun(
  {
  "host": "127.0.0.1:9111",
  "ops": [
    {
      "ns": "test.foo",
      "op": "insert",
      "doc": {
        "x": {
          "#RAND_STRING": [
            64
          ]
        },
        "y": {
          "#RAND_STRING": [
            128
          ]
        }
      }
    }
  ],
  "parallel": 16,
  "seconds": $seconds
  }
)

生成大约 1.5 亿条 oplog,33GB,(有压缩,实际是在 50G左右)

xdjmgset-dbfs1:PRIMARY> show dbs
admin    0.000GB
config   0.000GB
local   33.363GB
test    35.284GB
xdjmgset-dbfs1:PRIMARY> use local
switched to db local
xdjmgset-dbfs1:PRIMARY> db.oplog.rs.count()
150531637

在代码上加了一些日志用于查看 cursor random 的时间开销,以及每一次 random 采样的开销,

diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
index f2c3d1c220..7f029b788d 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
@@ -460,8 +460,14 @@ void WiredTigerRecordStore::OplogStones::_calculateStonesBySampling(OperationCon
     // each logical section.
     auto cursor = _rs->getRandomCursorWithOptions(opCtx, extraConfig);
     std::vector<RecordId> oplogEstimates;
+    const std::uint64_t startWaitTime = curTimeMicros64();
     for (int i = 0; i < numSamples; ++i) {
+        const std::uint64_t startWaitTime = curTimeMicros64();
         auto record = cursor->next();
+        auto waitTime = curTimeMicros64() - startWaitTime;
+        LOG(1) << "WT cursor random sample " << i << ", "
+               << Timestamp(record->id.repr()).toStringPretty() << ", took "
+               << waitTime / 1000.0 << "ms";
         if (!record) {
             // This shouldn't really happen unless the size storer values are far off from reality.
             // The collection is probably empty, but fall back to scanning the oplog just in case.
@@ -471,6 +477,8 @@ void WiredTigerRecordStore::OplogStones::_calculateStonesBySampling(OperationCon
         }
         oplogEstimates.push_back(record->id);
     }
+    auto waitTime = curTimeMicros64() - startWaitTime;
+    LOG(1) << "WT cursor random sampling total took " << waitTime/1000.0 << "ms";
     std::sort(oplogEstimates.begin(), oplogEstimates.end());

     for (int i = 1; i <= wholeStones; ++i) {

把副本集 1 重启,观察日志,

020-10-27T15:34:09.058+0800 I  STORAGE  [initandlisten] Taking 755 samples and assuming that each section of oplog contains approximately 1991955 records totaling to 687194924 bytes
2020-10-27T15:34:09.058+0800 D1 STORAGE  [initandlisten] WT cursor random sample 0, Oct 26 19:54:57:7347, took 0.415ms
2020-10-27T15:34:09.063+0800 D1 STORAGE  [initandlisten] WT cursor random sample 1, Oct 26 19:57:47:6215, took 4.488ms
2020-10-27T15:34:09.067+0800 D1 STORAGE  [initandlisten] WT cursor random sample 2, Oct 26 15:37:47:1030, took 4.608ms
2020-10-27T15:34:09.072+0800 D1 STORAGE  [initandlisten] WT cursor random sample 3, Oct 26 15:44:15:4619, took 4.471ms
2020-10-27T15:34:09.076+0800 D1 STORAGE  [initandlisten] WT cursor random sample 4, Oct 26 15:46:51:2640, took 4.597ms
2020-10-27T15:34:09.081+0800 D1 STORAGE  [initandlisten] WT cursor random sample 5, Oct 26 15:49:22:10335, took 4.556ms
2020-10-27T15:34:09.086+0800 D1 STORAGE  [initandlisten] WT cursor random sample 6, Oct 26 15:52:03:10684, took 4.746ms
2020-10-27T15:34:09.090+0800 D1 STORAGE  [initandlisten] WT cursor random sample 7, Oct 26 15:54:14:4494, took 4.586ms
2020-10-27T15:34:09.095+0800 D1 STORAGE  [initandlisten] WT cursor random sample 8, Oct 26 15:56:46:1960, took 4.889ms
2020-10-27T15:34:09.100+0800 D1 STORAGE  [initandlisten] WT cursor random sample 9, Oct 26 15:59:18:7246, took 4.695ms
2020-10-27T15:34:09.105+0800 D1 STORAGE  [initandlisten] WT cursor random sample 10, Oct 26 16:02:05:4727, took 4.895ms
2020-10-27T15:34:09.110+0800 D1 STORAGE  [initandlisten] WT cursor random sample 11, Oct 26 16:04:30:5742, took 4.673ms
2020-10-27T15:34:09.115+0800 D1 STORAGE  [initandlisten] WT cursor random sample 12, Oct 26 16:06:45:1917, took 4.881ms
2020-10-27T15:34:09.119+0800 D1 STORAGE  [initandlisten] WT cursor random sample 13, Oct 26 16:08:50:5188, took 4.786ms
2020-10-27T15:34:09.124+0800 D1 STORAGE  [initandlisten] WT cursor random sample 14, Oct 26 16:11:13:7634, took 4.449ms
2020-10-27T15:34:09.129+0800 D1 STORAGE  [initandlisten] WT cursor random sample 15, Oct 26 16:13:25:6775, took 5.204ms
...
2020-10-27T15:34:12.463+0800 D1 STORAGE  [initandlisten] WT cursor random sample 752, Oct 26 15:56:15:232, took 4.923ms
2020-10-27T15:34:12.467+0800 D1 STORAGE  [initandlisten] WT cursor random sample 753, Oct 26 15:58:47:1953, took 4.399ms
2020-10-27T15:34:12.472+0800 D1 STORAGE  [initandlisten] WT cursor random sample 754, Oct 26 16:01:28:5317, took 4.598ms
2020-10-27T15:34:12.472+0800 D1 STORAGE  [initandlisten] WT cursor random sampling total took 3414.51ms

可以看到这个实例采样了 755 次,总共耗时 3414ms,每次采样的时间都比较固定,在 4ms - 5ms 之间。

然后 MongoDB 的 serverStatus 命令本身也提供了一个 section 的输出用于查看启动时,初始化 oplog stones 的总时长,和初始化方法(是 scan 还是 sampling),

xdjmgset-dbfs1:PRIMARY> db.serverStatus().oplogTruncation
{
        "totalTimeProcessingMicros" : NumberLong(3418164),
        "processingMethod" : "sampling",
        "totalTimeTruncatingMicros" : NumberLong(0),
        "truncateCount" : NumberLong(0)
}

可以看到,其他部分的时间开销在 4ms 左右,不到总时长的 1%。

为了验证初始化时间和 sample 的个数成正比,同样根据上述方法构造另外一个数据集,25GB,1.13 亿条 oplog,

xdjmgset-dbfs1:PRIMARY> show dbs
admin    0.000GB
config   0.000GB
local   25.145GB
test    26.517GB
xdjmgset-dbfs1:PRIMARY> use local
switched to db local
xdjmgset-dbfs1:PRIMARY> db.oplog.rs.count()
113211477

重启之后查看日志输出,

2020-10-27T15:43:02.121+0800 I  STORAGE  [initandlisten] Taking 568 samples and assuming that each section of oplog contains approximately 1991875 records totaling to 687195044 bytes
2020-10-27T15:43:02.121+0800 D1 STORAGE  [initandlisten] WT cursor random sample 0, Oct 27 12:33:29:5201, took 0.216ms
2020-10-27T15:43:02.125+0800 D1 STORAGE  [initandlisten] WT cursor random sample 1, Oct 27 12:36:06:5577, took 4.489ms
2020-10-27T15:43:02.130+0800 D1 STORAGE  [initandlisten] WT cursor random sample 2, Oct 27 12:38:30:1191, took 4.417ms
2020-10-27T15:43:02.134+0800 D1 STORAGE  [initandlisten] WT cursor random sample 3, Oct 27 12:40:51:1654, took 4.526ms
2020-10-27T15:43:02.139+0800 D1 STORAGE  [initandlisten] WT cursor random sample 4, Oct 27 12:43:12:9085, took 4.51ms
2020-10-27T15:43:02.144+0800 D1 STORAGE  [initandlisten] WT cursor random sample 5, Oct 27 12:45:36:3523, took 4.465ms
2020-10-27T15:43:02.148+0800 D1 STORAGE  [initandlisten] WT cursor random sample 6, Oct 27 12:48:09:6883, took 4.63ms
2020-10-27T15:43:02.153+0800 D1 STORAGE  [initandlisten] WT cursor random sample 7, Oct 27 12:50:09:6716, took 4.484ms
2020-10-27T15:43:02.157+0800 D1 STORAGE  [initandlisten] WT cursor random sample 8, Oct 27 12:52:24:1495, took 4.531ms
2020-10-27T15:43:02.162+0800 D1 STORAGE  [initandlisten] WT cursor random sample 9, Oct 27 12:54:39:3871, took 4.705ms
2020-10-27T15:43:02.167+0800 D1 STORAGE  [initandlisten] WT cursor random sample 10, Oct 27 12:57:15:3946, took 4.661ms
2020-10-27T15:43:02.171+0800 D1 STORAGE  [initandlisten] WT cursor random sample 11, Oct 27 12:59:36:5033, took 4.74ms
2020-10-27T15:43:02.176+0800 D1 STORAGE  [initandlisten] WT cursor random sample 12, Oct 27 13:01:52:6908, took 4.424ms
2020-10-27T15:43:02.181+0800 D1 STORAGE  [initandlisten] WT cursor random sample 13, Oct 27 13:04:22:2838, took 4.637ms
2020-10-27T15:43:02.186+0800 D1 STORAGE  [initandlisten] WT cursor random sample 14, Oct 27 13:06:42:6574, took 5.21ms
...
2020-10-27T15:43:04.771+0800 D1 STORAGE  [initandlisten] WT cursor random sample 567, Oct 27 12:17:32:2820, took 4.397ms
2020-10-27T15:43:04.771+0800 D1 STORAGE  [initandlisten] WT cursor random sampling total took 2650.65ms

进行了 568 次 sample,总时间开销 2650ms,而 2650ms 基本上等于 ( 568.0/755) * 3414ms = 2568ms ,和 sample 个数成正比的结论可成立。

综上,考虑到单次 random cursor sample 的开销大约是 4-5ms,总 sample 上限是 1000,那么 oplog stones 初始化时间的上限在 5s 左右(NVMe SSD)。


update:上面的分析忽略了一个事情,即每次 random cursor next,是需要把获取 record id 的,因为这个就是 oplog 的 truncate point,但是同时也会把 value 读上来,而且 WiredTiger 的读取都是以 page(extent)为粒度的,如果平均每条 oplog 的 value 都很大,必然造成单个 disk page 也很大,重启的时候,显然所有 page 的读取都要自己读 disk,那么就会造成单次 random cursor next 耗时提升。

所以,经过进一步测试,最终的结论是,当单条 oplog 平均 size 不大时(几百个字节 - 几 KB), oplog stones 初始化时间的上限在 5s 左右。当单条 oplog 平均 size 太大时(几十 KB - 几 MB),那么因为 page size 太大,冷启动读取 disk page 开销变大, oplog stones 初始化时间可能达到几十秒甚至更高,具体时间和单条 oplog 平均 size 强相关。

Oplog Stones reload 优化

阿里云数据库平台有一套完善的日志采集系统,通过对线上运行日志分析,仍然发现有不少实例写入时的 Value 比较大,导致的结果是 oplog entry 的大小超过几百 KB,设置达到几 MB,这种情况下,如果重启,初始化 oplog stones 的时间开销就要达到几十秒甚至更多,所以仍然有必要对这个问题进行优化。

优化前后对比

构造 1MB 大小的 oplog entry,重启 100 个 stone,994 个 sample,加载时间在 27.7s 左右

xdjmgset-dbfs1:PRIMARY> db.serverStatus().oplogTruncation
{
        "totalTimeProcessingMicros" : NumberLong(27791069),
        "processingMethod" : "sampling",
        "totalTimeTruncatingMicros" : NumberLong(0),
        "truncateCount" : NumberLong(0)
}

2020-11-13T14:47:52.391+0800 I  STORAGE  [initandlisten] The size storer reports that the oplog contains 20378 records totaling to 21355524826 bytes
2020-11-13T14:47:52.391+0800 D1 COMMAND  [WT-OplogStonesSaverThread-local.oplogstones.rs] BackgroundJob starting: WT-OplogStonesSaverThread-local.oplogstones.rs
2020-11-13T14:47:52.391+0800 D2 STORAGE  [WT-OplogTruncaterThread-local.oplog.rs] no global storage engine yet
2020-11-13T14:47:52.391+0800 I  STORAGE  [initandlisten] Sampling the oplog to determine where to place markers for truncation
2020-11-13T14:47:52.391+0800 D2 STORAGE  [WT-OplogStonesSaverThread-local.oplogstones.rs] WT-OplogStonesSaverThread-local.oplogstones.rs: no global storage engine yet
2020-11-13T14:47:52.393+0800 I  STORAGE  [initandlisten] Sampling from the oplog between Nov 13 14:43:08:618 and Nov 13 14:47:21:3 to determine where to place markers for truncation
2020-11-13T14:47:52.393+0800 I  STORAGE  [initandlisten] Taking 994 samples and assuming that each section of oplog contains approximately 205 records totaling to 214833771 bytes
2020-11-13T14:47:52.572+0800 D1 STORAGE  [initandlisten] WT cursor random sample 0, Nov 13 14:43:28:232, took 178.246ms
2020-11-13T14:47:52.620+0800 D1 STORAGE  [initandlisten] WT cursor random sample 1, Nov 13 14:45:13:143, took 48.437ms
2020-11-13T14:47:52.661+0800 D1 STORAGE  [initandlisten] WT cursor random sample 2, Nov 13 14:45:35:242, took 40.899ms
2020-11-13T14:47:52.868+0800 D1 STORAGE  [initandlisten] WT cursor random sample 3, Nov 13 14:43:19:114, took 206.935ms
...
2020-11-13T14:48:20.182+0800 I  STORAGE  [initandlisten] WiredTiger record store oplog processing took 27791ms

优化后,加载时间在 7ms 左右,减少了 3-4 个数量级

xdjmgset-dbfs1:PRIMARY> db.serverStatus().oplogTruncation
{
        "totalTimeProcessingMicros" : NumberLong(7261),
        "processingMethod" : "reloadingLocal",
        "totalTimeTruncatingMicros" : NumberLong(0),
        "truncateCount" : NumberLong(0)
}

优化思路简述

  1. 把内存中的 oplog stones 保存在本地
  2. 后台线程负责在内存 oplog stones 变化时更新本地文件。
  3. 重启加载从本地文件加载保存的 oplog stones 信息,需要处理一些边界条件。

最后,打一个小广告,阿里云 MongoDB 目前上线了 Serverless 版本,让您方便的以超低成本试用 MongoDB,活动期间首购 1 元包月,续费也有 5 折优惠,欢迎试用:https://www.aliyun.com/product/mongodb

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
2天前
|
缓存 监控 NoSQL
【MongoDB 专栏】MongoDB 的内存管理与优化
【5月更文挑战第11天】MongoDB的内存管理优化对性能至关重要,涉及数据缓存、索引及执行操作的内存使用。动态内存管理根据访问模式和负载调整,可通过配置参数优化,如设置合适缓存大小,调整内存分配参数。索引管理也很重要,需定期评估优化,避免内存占用过高。监控内存使用、数据清理压缩、架构规划也是优化手段。面对挑战,如高并发下的内存不足,需灵活调整策略,平衡系统资源。不断学习新方法,提升内存管理能力,以优化MongoDB性能。
【MongoDB 专栏】MongoDB 的内存管理与优化
|
2天前
|
存储 监控 NoSQL
【MongoDB 专栏】MongoDB 的日志管理与分析
【5月更文挑战第11天】MongoDB日志管理与分析至关重要,包括系统日志和操作日志,用于监控、故障排查和性能优化。合理配置日志详细程度、存储位置和保留策略,使用日志分析工具提升效率,发现性能瓶颈和安全性问题。日志分析有助于优化查询、调整配置,确保数据安全,并可与其他监控系统集成。面对日志量增长的挑战,需采用新技术如分布式存储和数据压缩来保障存储和传输。随着技术发展,不断进化日志管理与分析能力,以支持MongoDB的稳定高效运行。
【MongoDB 专栏】MongoDB 的日志管理与分析
|
2天前
|
存储 监控 NoSQL
【MongoDB 专栏】MongoDB 的存储引擎选择与优化
【5月更文挑战第11天】MongoDB 的存储引擎选择与优化至关重要,影响数据库性能、可靠性和可扩展性。常见引擎有默认的 WiredTiger(提供高性能读写、文档级并发控制和压缩)和较旧的 MMAPv1。选择引擎需考虑性能需求、数据规模、并发操作和压缩需求。WiredTiger 以其高性能和并发控制脱颖而出。优化策略包括配置参数、规划数据结构、监控性能和定期维护。案例显示,WiredTiger 对于并发访问频繁的电商平台尤为适合。未来,更高效、智能的存储引擎将应运而生,持续优化将是保持数据库系统竞争力的关键。
【MongoDB 专栏】MongoDB 的存储引擎选择与优化
|
13天前
|
监控 NoSQL MongoDB
MongoDB索引机制与优化策略详解
【4月更文挑战第30天】本文深入解析MongoDB的索引机制,包括单字段、复合、地理空间、全文及哈希索引。介绍了创建与查看索引的方法,并提出了优化策略:选择性创建、使用复合索引、定期审查优化、避免不必要的索引扫描、利用索引前缀与覆盖索引,以及监控索引使用。通过这些策略,可提升MongoDB查询性能。
|
1月前
|
存储 NoSQL MongoDB
【MongoDB】MongoDB 索引结构底层原理分析
【4月更文挑战第1天】【MongoDB】MongoDB 索引结构底层原理分析
|
5月前
|
存储 NoSQL MongoDB
MongoDB 助力移动式汽车保养运营模式优化,将开发请求减少 90%
MongoDB针对初级,中级及熟练的技术开发人员推出系列技术文章与行业案例。深入浅出地剖析MongoDB产品基础原理,使用技巧,典型行业场景及应用,还有Code Demo及线上线下活动推荐!
4848 1
MongoDB 助力移动式汽车保养运营模式优化,将开发请求减少 90%
|
5月前
|
存储 监控 NoSQL
数据存储与分析:办公室电脑屏幕监控的MongoDB应用实例
在当今数字时代,数据的存储和分析变得愈发重要,尤其是在办公环境中,对电脑屏幕进行监控成为一种日益普遍的需求。本文将介绍如何利用MongoDB数据库实现办公室电脑屏幕监控,并通过代码实例展示其应用。
222 0
|
5月前
|
存储 监控 NoSQL
MongoDB助力腾讯游戏 优化游戏开发体验
无论在功能还是性能上,MongoDB都很好地契合了游戏业务场景,带给腾讯游戏的不只是功能价值,还有运维价值
MongoDB助力腾讯游戏 优化游戏开发体验
|
5月前
|
存储 人工智能 NoSQL
多维数据实时分析,MongoDB给零售企业提供快速高效的数据洞察力
客户行为正在迅速演变,供应链正在重组,员工也正在以新的方式工作。企业需要提供更加个性化的客户体验,对市场趋势做出更快速的反应,监测和预防潜在问题。
多维数据实时分析,MongoDB给零售企业提供快速高效的数据洞察力
|
9月前
|
NoSQL MongoDB
MongoDB-分片优化
分片的主要目的就是将数据分配到不同的服务器中保存, 提升服务器的容量, 让数据更加的均衡, 更有效的降低服务器的压力, 但是随着时间推移, 某些数据段中保存的数据会越来越多, 所以为了保证个分片均衡, 当某个数据段数据过多或体积过大的时候, 系统就会自动在下一次操作这个数据段时(新增/更新), 将一个大的数据段分裂成多个小的数据段。
126 0

相关产品

  • 云数据库 MongoDB 版