关于MongoDB Sharding,你应该知道的

MongoDB Sharded Cluster 原理

如果你还不了解 MongoDB Sharded cluster,可以先看文档认识一下

什么时候考虑用 Sharded cluster?

当你考虑使用 Sharded cluster 时,通常是要解决如下2个问题

  1. 存储容量受单机限制,即磁盘资源遭遇瓶颈。
  2. 读写能力受单机限制(读能力也可以在复制集里加 secondary 节点来扩展),可能是 CPU、内存或者网卡等资源遭遇瓶颈,导致读写能力无法扩展。

如果你没有遇到上述问题,使用 MongoDB 复制集就足够了,管理维护上比 Sharded cluster 要简单很多。

查看全文

网络分区引发的 oplog 乱序问题

线上一个Secondary节点crash,错误原因是出现了 OplogOutOfOrder 错误,也就是说Secondary 重放了一条比『已经重放过最新的 oplog』更早的操作,经过分析,发现问题是因网络分区导致出现2个 Primary 的问题导致,详细的过程如下表分析。

说明:Node2、Node1、Node0分别是优先级为2、1、0的复制集成员。

Timestamp Node2 Node1 Node0 Notes
t1 Primary Secondary Secondary Node2被选为主
t2 Primary write A Seconary sync A Secondary sync A Node2上写入A,并同步到 Node1、Node0
t3 Primary write B Secondary Secondary Node2上写入 B,但未同步到 Node1、Node0
t4 Primary Secondary Secondary Node2网络与 Node1、Node0隔离,发生新的选举
t5 Primary Primary Secondary Node1被选为主
t6 Primary Primary WRITE C Secondary SYNC C Node1上写入 C,同步到 Node0
t7 Primary Primary WRITE D Secondary SYNC D Node1上写入 D,同步到 Node0
t8 Primary Write E Primary Secondary Node2上写入 E, a实际上是一个耗时很长的建索引操作结束
t9 Secondary Primary Secondary network recovered, Node1发现 Node2的选举时间戳更早,请求Node2降级
t10 Secondary Secondary Secondary Node1发现 Node2优先级更高,并且oplog 足够,主动降级
t11 Primary Secondary Secondary Node2赢得新的选举
t12 Priamry Secondary Secondary Node1 Node0 从 Node2同步,先回滚 C D
t13 Primary Secondary Secondary Node1从 Node2 同步 B E
t14 Primary Secondary Secondary Node0从 Node2 同步 B E ,因为 Node0已经同步过 C D,时间戳比 B 更新,触发oplogOutOfOrder 错误 crash

上述问题也提给了MongoDB 官方团队,参考https://jira.mongodb.org/browse/SERVER-25838,这个问题在『3.0及以前的版本』或『使用 protocol version 为0的3.2版本』都有可能发生,但几率很小,使用 MongoDB 的同学不用担心,我们也是因为MongoDB云数据库 用户比较多,才触发了一例;MongoDB 3.2引入了 raft 协议,避免了上述场景发生,遇到类似问题的用户可以升级到 MongoDB-3.2,并使用复制集协议protocoal version 1

MongoDB 如何保证 oplog 顺序?

MongoDB 复制集里,主备节点间通过 oplog 来同步数据,Priamry 上写入数据时,会记录一条oplog,Secondary 从 Primary 节点拉取 oplog并重放,以保证最终存储相同的数据集。

oplog 主要特性

  • 幂等性,每一条oplog,重放一次或多次,得到的结果是一样的;为实现幂等 mongodb 对很多操作进行来转换,比如将 insert 转换为 upsert、$inc 操作转换为 $set等等。
  • 固定大小(capped collection),oplog 使用固定空间存储,当空间满了时,会自动删除最旧的文档。
  • oplog 按时间戳排序,并且在所有节点上顺序保持一致

本文主要介绍MongoDBD 如何保证 oplog 有序存储并读取,关于 oplog 扩展阅读

查看全文

为什么 MongoDB 连接数被用满了?

使用 MongoDB 时,可能会遇到因为 mongod 连接数用满了,导致客户端无法连接的问题。mongod的最大连接数通过 net.maxIncomingConnections 指定,默认值为1000000,相当于没有限制,生产环境强烈建议根据实际需求配置,以避免客户端误用导致 mongod 负载过高。

Mongod 为什么需要限制连接数?

Mongod 的服务模型是每个网络连接由一个单独的线程来处理,每个线程配置了1MB 的栈空间,当网络连接数太多时,过多的线程会导致上下文切换开销变大,同时内存开销也会上涨。

详细的分析参考 云数据库MongoDB为什么需要限制连接数?

查看全文

MongoDB Sharded Cluster 路由策略

本文是对MongoDB 世界大会上『Life of a Sharded Write』主题分享的总结,这个分享很有意思,主要内容是介绍 MongoDB Sharded Cluster 里写操作的路由策略,以及config server变为复制集后面临的一些挑战。

如果不了解 Sharded Cluster 的基础知识,可以先看看这篇文章再回来。

查看全文

MongoDB位置查询内存使用优化

生产环境实例频繁 OOM ,调查发现主要由一些$near查询导致,查询类似如下,其中 latlng 字段建立了2d index

{
    "find" : "userData",
    "filter" : {
        "latlng" : {
            "$near" : [
                116.34642045073839,
                39.87082232130999
            ],
            "$maxDistance" : 0.9009009009009009
        }
    },
    "ntoreturn" : 10000
}

官方解释是因为$near需要排序,实际上是从中心点,不断往外扩展,找出附近的点,所以$near cursor会缓存计算结果。MongoDB cursor 的默认超时时间是10分钟,所以如果10分钟内所有打开 cursor 缓存的内存总量超过总内存,就会出现 OOM 的情况。

查看全文

MongoDB 创建大量集合测试问题

问题背景

对使用 wiredtiger 引擎的 mongod 进行如下测试,不断的『创建集合、创建索引,插入一条记录』,然后统计这3个动作的耗时。

var db = db.getSiblingDB("testdb");
for (var i = 0; i < 100000; i++) {
    var start = (new Date()).getTime();
    var collName = "test" + i;
    var doc = {name: "name" +i, seq: i};
    db.createCollection(collName);        // 创建集合
    db[collName].createIndex({name: 1});  // 创建索引
    db[collName].insert(doc);             // 插入一条记录
    var end = (new Date()).getTime();     // 统计耗时
    print("cost: " + (end - start));
}

随着集合数越来越多,测试过程中发现2个问题

  1. 偶尔会出现耗时很长的请求(1s、2s、3s..不断上升),统计了下频率,大约1分钟左右出现一次。
  2. 平均耗时不断增加,从最开始平均10ms 不到,一直到20ms、30ms、40ms...

查看全文

机器宕机引发的复制集心跳异常问题

问题背景

MongoDB云数据库是由3个节点组成的复制集,node3原来是 Primary 节点,因为硬件故障宕机,云数据库高可用模块检测到后,立即进行了主备切换,保证服务正常,node3重启之后重新加入复制集,变为 Hidden 节点,最终的状态如下表所示。

Primary Secondary Hidden
node1:port1 node2:port2 node3:port3

宕机引发的问题

node3重新加入后,服务正常,但复制集内部的通信却还有问题。

从node3的 rs.status()看整个复制集,一切正常,说明 node3到 node1、node2发送心跳请求都正常(每个节点周期性向其他节点发送心跳,通过心跳应答来更新其他节点的状态信息)。

当从 node1、node2的 rs.status()看,node3却处于宕机状态,错误如下

 {
            "_id" : 3,
            "name" : "node3:port3",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : Timestamp(0, 0),
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2016-07-21T12:30:17.807Z"),
            "lastHeartbeatRecv" : ISODate("2016-07-21T12:30:17.544Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Couldn't get a connection within the time limit",
            "configVersion" : -1
        }

也就是说 node1向 node3发送心跳信息是一直失败的,失败的原因是Couldn't get a connection within the time limit

查看全文

MongoDB复制集同步原理解析

MongoDB副本集数据同步](https://docs.mongodb.com/manual/core/replica-set-sync/)主要包含2个步骤

  1. intial sync,可以理解为全量同步
  2. replication,追同步源的oplog,可以理解为增量同步

本文是对MongoDB高可用复制集原理的补充,会详细介绍MongoDB数据同步的实现原理。

initial sync

Secondary节点当出现如下状况时,需要先进行全量同步

  1. oplog为空
  2. local.replset.minvalid集合里_initialSyncFlag字段设置为true
  3. 内存标记initialSyncRequested设置为true

查看全文

大量的集合为何导致Secondary无法同步?

最近遇到一个user case,因为集合数量太多,导致Secondary节点无法进行initial sync(主备同步的第一步,可理解为从Primary上全量拷贝数据)。

副本集使用wiredtiger存储引擎,一共60,000+集合,平均每个集合4个索引,wiredtiger的集合及每个索引都对应一个单独的文件来存储,数据目录下总共300,000+文件,listDatabases命令执行时,会遍历所有DB的每个集合,获取集合及其索引文件占用的存储空间信息,实现类似如下的伪代码。

listDatabases() {
    dbNames = getAllDatabaseNames();
    for db in dbNames {
        sizeOnDisk = 0;
        for coll in db.getAllColletions() {
                size += coll.size();
                for index in coll.getAllIndexes() {
                    size += index.size();
                }    
        }
        addToOutput(db, size);
    }      
}

使用wiredtiger引擎时,获取集合及索引文件大小信息时,需要打开一个特殊的cursor来读取,整个listDatabases需要遍历300,000+个文件来逐个获取大小信息,导致整个命令的执行开销很大,总耗时在30s以上。

查看全文

1 2 3 12