MongoDB之旅(下)

深入挖掘海底的宝藏。

本文所使用的MongoDB版本为3.4.2

副本集

建立

在本地启动所有成员,分别设置不同的路径和端口,使用相同的标识符student

1
2
3
4
5
6
7
8
# terminal-1
mongod --dbpath ~/Desktop/db --port 27017 --replSet "student"
# terminal-2
mongod --dbpath ~/Desktop/db-rs0 --port 27016 --replSet "student"
# terminal-3
mongod --dbpath ~/Desktop/db-rs1 --port 27015 --replSet "student"

配置

连接一个节点

1
mongo --port 27017

创建配置对象与初始化

1
2
3
4
5
6
7
8
9
> var config = {
"_id" : "student",
"members" : [
{"_id": 0, "host": "127.0.0.1:27017"},
{"_id": 1, "host": "127.0.0.1:27016"},
{"_id": 2, "host": "127.0.0.1:27015"}
]
}
> rs.initiate(config)

所连接的节点解析这个对象后会将配置文件发给其他成员,所有成员配置完成后会自动选出一个主节点(primary member),其他成员为备份节点(secondary member)。

这里27017端口的副本集被选为主节点,其他两个为备份节点,先在主节点中添加一条数据。

1
student:PRIMARY> db.stu.insert({"name":"yrq","grade":"3","class":"7"})

默认情况下备份节点是不可读的,需要执行db.setSlaveOk()后才可读取数据。

1
2
3
4
5
6
7
8
9
10
11
> mongo --port 27016
student:SECONDARY> db.stu.find()
Error: error: {
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk"
}
student:SECONDARY> db.setSlaveOk()
student:SECONDARY> db.stu.find()
{ "_id" : ObjectId("58f58ded93e9bbcb0bceb3c5"), "name" : "yrq", "grade" : "3", "class" : "7" }

查看配置与状态

  1. rs.conf()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    student:PRIMARY> rs.conf()
    {
    "_id" : "student",
    "version" : 1, # 每个修改配置时这个值都会自增+1
    "protocolVersion" : NumberLong(1),
    "members" : [ # 副本集成员列表
    {
    "_id" : 0,
    "host" : "127.0.0.1:27017",
    "arbiterOnly" : false,
    "buildIndexes" : true,
    "hidden" : false,
    "priority" : 1,
    "tags" : {
    },
    "slaveDelay" : NumberLong(0),
    "votes" : 1
    },
    {
    "_id" : 1,
    "host" : "127.0.0.1:27016",
    "arbiterOnly" : false,
    "buildIndexes" : true,
    "hidden" : false,
    "priority" : 1,
    "tags" : {
    },
    "slaveDelay" : NumberLong(0),
    "votes" : 1
    },
    {
    "_id" : 2,
    "host" : "127.0.0.1:27015",
    "arbiterOnly" : false,
    "buildIndexes" : true,
    "hidden" : false,
    "priority" : 1,
    "tags" : {
    },
    "slaveDelay" : NumberLong(0),
    "votes" : 1
    }
    ],
    "settings" : {
    "chainingAllowed" : true,
    "heartbeatIntervalMillis" : 2000,
    "heartbeatTimeoutSecs" : 10,
    "electionTimeoutMillis" : 10000,
    "catchUpTimeoutMillis" : 2000,
    "getLastErrorModes" : {
    },
    "getLastErrorDefaults" : {
    "w" : 1,
    "wtimeout" : 0
    },
    "replicaSetId" : ObjectId("58f58a448ab72530955abf84")
    }
    }
  2. db.isMaster()
    查看当前mongod实例的角色,若mongod实例是副本集中的成员,则通过ismastersecondary可以判断它是主节点还是备份节点。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    student:PRIMARY> db.isMaster()
    {
    "hosts" : [
    "127.0.0.1:27017",
    "127.0.0.1:27016",
    "127.0.0.1:27015"
    ],
    "setName" : "student",
    "setVersion" : 1,
    "ismaster" : true, # 是否为主节点
    "secondary" : false, # 是否为备份节点
    "primary" : "127.0.0.1:27017", # 主节点成员
    "me" : "127.0.0.1:27017", # 自己
    ...
    }
  3. rs.status()
    显示当前副本集的状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    student:PRIMARY> rs.status()
    {
    "set" : "student",
    "date" : ISODate("2017-04-18T04:03:52.781Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    ...
    "members" : [
    {
    "_id" : 0,
    "name" : "127.0.0.1:27017",
    "health" : 1,
    "state" : 1, # 1表示主节点,可进行写操作,有投票资格
    "stateStr" : "PRIMARY",
    ...
    "self" : true
    },
    {
    "_id" : 1,
    "name" : "127.0.0.1:27016",
    "health" : 1,
    "state" : 2, # 2表示备份节点,有投票资格
    "stateStr" : "SECONDARY",
    ...
    },
    {
    "_id" : 2,
    "name" : "127.0.0.1:27015",
    "health" : 1,
    "state" : 2,
    "stateStr" : "SECONDARY",
    ...
    }
    ],
    "ok" : 1
    }

修改配置

  1. 添加成员
    启动一个成员

    1
    mongod --dbpath ~/Desktop/db-rs2 --port 27014 --replSet "student"

    添加到副本集中

    1
    2
    3
    4
    student:PRIMARY> rs.add("127.0.0.1:27014")
    { "ok" : 1 }
    student:PRIMARY> rs.status().members.length
    4
  2. 删除成员

    1
    2
    3
    4
    student:PRIMARY> rs.remove("127.0.0.1:27014")
    { "ok" : 1 }
    student:PRIMARY> rs.status().members.length
    3
  3. 将主节点降级为备份节点

    1
    student:PRIMARY> rs.stepDown()
  4. 阻止选举
    若主节点要进行维护,不需要再这期间选举新的主节点,可以在每个备份节点执行freeze命令冻结状态,始终为备份节点。

    1
    student:SECONDARY> rs.freeze(1000000)

    输入参数为秒数,需要释放时执行rs.freeze(0)即可。

设计

仲裁成员

仲裁成员(arbiter)不保存数据,不为客户端提供服务,仅参与投票选举,避免出现平票的情况,可部署在性能较差的服务器上。
添加仲裁成员的方法:

1
student:PRIMARY> rs.addArb("127.0.0.1:27014")


1
student:PRIMARY> rs.add({"_id":3,"host":"127.0.0.1:27014","arbiterOnly":true})

隐藏成员

隐藏成员对用户不可见,不为客户端提供服务。只有优先级为0的成员才能被隐藏,

隐藏一个成员

1
2
3
4
5
6
student:PRIMARY> var config = rs.conf()
student:PRIMARY> config.members[2].hidden = true;
true
student:PRIMARY> config.members[2].priority = 0;
0
student:PRIMARY> rs.reconfig(config)

隐藏成员对isMaster()是不可见的,对status()和conf()是可见的。

1
2
3
4
5
6
student:PRIMARY> rs.status().members.length
3
student:PRIMARY> rs.conf().members.length
3
student:PRIMARY> db.isMaster().hosts.length
2

取消隐藏需要将hidden属性设为false或者删掉hidden。

延迟备份

config中的每个成员都有一个slaveDelay选项,用来设置备份节点同步主节点的延迟,默认为0,表示当主节点有操作改变时立即同步。

设置延迟可以防止对主节点的误操作,如删除或有严重bug的情况。

设置这个选项要求成员的优先级为0。

创建索引

有时备份节点并不需要像主节点拥有索引,可以通过设置"buildIndexes" : false来阻止备份节点创建索引,这个修改是永久操作,无法再次恢复为可拥有索引的状态。

设置这个选项要求成员的优先级为0。

同步

复制功能是使用操作日志oplog实现的,操作日志包含了主节点每一次写操作,备份节点查询oplog获取需要执行的操作,然后在自己的数据集上执行操作。

心跳

每个成员每隔一段时间都会向其它成员发送心跳请求(heartbeat request),用于检查每个成员的状态。

成员状态

序号 名称 描述
0 STARTUP 成员刚启动的状态,若继续加载配置则进入STARTUP2状态
1 PRIMARY 主节点,有投票资格
2 SECONDARY 备份节点,有投票资格
3 RECOVERING 成员在进行启动自检,或刚完成回滚或重同步操作的过渡,有投票资格
5 STARTUP2 已启动在初始化过程中
6 UNKNOWN 状态对其它成员是未知的
7 ARBITER 仲裁成员,不复制数据,仅参与选举
8 DOWN 其它成员不可达
9 ROLLBACK 成员正在执行回滚操作,不能进行读操作
10 REMOVED 成员被移出副本集

分片

作用

  • 增加可用RAM
  • 增加可用磁盘空间
  • 减轻单台服务器的负载
  • 处理单个mongod无法承受的吞吐量

    分片集群

    每个分片集群都由三个部分组成:
  1. 分片(shard) - 每个分片包含分片数据中的一个子集,分片可以是副本集。
  2. mongos - mongos作为查询数据的路由,提供客户端与分片集群间的接口。
  3. 配置服务器(config server) - 保存集群的元数据与配置,在3.4版本后要求配置服务器必须为副本集。

生产环境中的配置:

  1. 3个及以上的配置服务器
  2. 3个及以上的分片
  3. 1个及以上的mongos路由

开发环境中的配置:

  1. 一个配置服务器(副本集)
  2. 至少一个分片(副本集)
  3. 一个mongo实例

启动

端口设计:

  • 27014~27016 配置服务器
  • 27001~27003 分片1(副本集)
  • 27004 分片2
  • 27000 mongos
  1. 创建配置服务器副本集
    mongos需要从配置服务器中获取配置信息,因此需要优先启动.

    1
    2
    3
    4
    5
    6
    7
    8
    # terminal-1
    mongod --configsvr --dbpath ~/Desktop/db-conf1 --port 27016 --replSet "config"
    # terminal-2
    mongod --configsvr --dbpath ~/Desktop/db-conf2 --port 27015 --replSet "config"
    # terminal-3
    mongod --configsvr --dbpath ~/Desktop/db-conf3 --port 27014 --replSet "config"

    配置服务器不需要分配太大的空间,它存放数据分布表.

    初始化副本集:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    > rs.initiate(
    ... {
    ... _id: "config",
    ... configsvr: true,
    ... members: [
    ... { _id : 0, host : "127.0.0.1:27016" },
    ... { _id : 1, host : "127.0.0.1:27015" },
    ... { _id : 2, host : "127.0.0.1:27014" }
    ... ]
    ... })
  2. 创建分片
    创建副本集分片

    1
    2
    3
    4
    5
    6
    7
    8
    # terminal-4
    mongod --shardsvr --dbpath ~/Desktop/db-sh1 --port 27001 --replSet "shard"
    # terminal-5
    mongod --shardsvr --dbpath ~/Desktop/db-sh2 --port 27002 --replSet "shard"
    # terminal-6
    mongod --shardsvr --dbpath ~/Desktop/db-sh3 --port 27003 --replSet "shard"

    配置副本集

    1
    2
    3
    4
    5
    6
    7
    8
    9
    > rs.initiate(
    ... {
    ... _id: "shard",
    ... members: [
    ... { _id : 0, host : "127.0.0.1:27001" },
    ... { _id : 1, host : "127.0.0.1:27002" },
    ... { _id : 2, host : "127.0.0.1:27003" }
    ... ]
    ... })

    创建普通分片

    1
    2
    # terminal-7
    mongod --shardsvr --dbpath ~/Desktop/db-sh4 --port 27004
  3. 启动mongos

    1
    mongos --configdb config/127.0.0.1:27016,127.0.0.1:27015,127.0.0.1:27014

    config为配置服务器副本集名称,斜杠后为具体数据集地址。

    连接到mongos

    1
    mongo --port 27000
  4. 添加分片

    1
    2
    mongos> sh.addShard("shard/127.0.0.1:27001,127.0.0.1:27002,127.0.0.1:27003")
    mongos> sh.addShard("127.0.0.1:27004")

    shard为分片副本集名称,斜杠后为具体数据集地址,若为普通数据集则不用添加名称。

  5. 查看状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    mongos> sh.status().shards
    --- Sharding Status ---
    ...
    shards:
    { "_id" : "shard", "host" : "shard/127.0.0.1:27001,127.0.0.1:27002,127.0.0.1:27003", "state" : 1 }
    { "_id" : "shard0001", "host" : "127.0.0.1:27004", "state" : 1 }
    ...
    databases:
    { "_id" : "foo", "primary" : "shard", "partitioned" : false }

    可以看到有两个分片,一个是副本集”shard”,一个是普通分片”shard0001”。
    databases中的_id为数据库名。primary表示主分片,为随机选择的,所有分片都会在主分片上。partitioned表示是否启用了分片。

    先在mongos中操作添加一些数据

    1
    2
    3
    4
    5
    6
    7
    mongos> for(var i=0;i<5000;i++){
    ... db.user.insert(
    ... {
    ... "name":"user_"+i,
    ... "created": new Date()
    ... });
    ... }
  6. 启用分片

    1
    2
    3
    mongos> use foo
    switched to db foo
    mongos> sh.enableSharding("foo")
  7. 创建索引
    对集合分片时需要选择一个片键,为集合的一个键,分片时会根据这个片键拆分数据。只有具有索引的键才能成为片键,因此先要在想使用片键的键上创建索引。

    1
    mongos> db.user.ensureIndex({"name":1})
  8. 对集合进行分片

    1
    mongos> sh.shardCollection("foo.user",{"name":1})
  9. 分片后的状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    mongos> sh.status().shards
    --- Sharding Status ---
    ...
    foo.user
    shard key: { "name" : 1 }
    unique: false
    balancing: true
    chunks:
    shard 1
    { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard Timestamp(1, 0)

    可以看到分片后只有一个块,存储在分片”shard”上,”$minKey”与”$maxKey”表示键所对应值范围的端值,

  10. 查看块(chunk)的状态
    需要在config db下操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    mongos> use config
    switched to db config
    mongos> db.chunks.find().pretty()
    {
    "_id" : "foo.user-name_MinKey",
    "ns" : "foo.user",
    "min" : {
    "name" : { "$minKey" : 1 }
    },
    "max" : {
    "name" : { "$maxKey" : 1 }
    },
    "shard" : "shard",
    "lastmod" : Timestamp(1, 0),
    "lastmodEpoch" : ObjectId("58f75c5b9cde39278500058c")
    }

片键

备份

文件系统快照

使用文件系统快照有两个条件:

  1. 需要文件系统本身支持快照(如Linux的LVM和Amazon EBS的EC2)。
  2. 启用journaling日记系统。

cp或rsync

若系统不支持快照,可直接使用cp、rsync或其它工具备份数据。

  1. 锁定数据库,禁止写入等操作

    1
    > db.fsync()
  2. 复制数据库文件到指定目录

    1
    $ cp -R ~/Desktop/db/* ~/Desktop/backup
  3. 复制完成后解锁数据库

    1
    > db.fsyncUnlock()

mongodump

mongodump和mongorestore是备份小型mongo数据库的有效工具,不适合大型的系统的备份。

  1. 使用mongodump创建备份

    1
    2
    3
    4
    5
    6
    7
    $ mongodump
    2017-04-10T23:20:46.221+0800 writing admin.system.version to
    2017-04-10T23:20:46.222+0800 done dumping admin.system.version (1 document)
    2017-04-10T23:20:46.223+0800 writing test.users to
    2017-04-10T23:20:46.223+0800 writing test.comments to
    2017-04-10T23:20:46.224+0800 done dumping test.comments (40 documents)
    2017-04-10T23:20:46.228+0800 done dumping test.users (1124 documents)

    可以设置–dbpath指定数据文件进行备份,若mongod正在运行则不要设置使用这个选项,

  2. 使用mongorestore恢复mongodump产生的备份
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ mongorestore
    2017-04-10T23:22:26.789+0800 using default 'dump' directory
    2017-04-10T23:22:26.789+0800 preparing collections to restore from
    2017-04-10T23:22:26.790+0800 reading metadata for test.comments from dump/test/comments.metadata.json
    2017-04-10T23:22:26.791+0800 reading metadata for test.users from dump/test/users.metadata.json
    2017-04-10T23:22:26.791+0800 restoring test.comments from dump/test/comments.bson
    2017-04-10T23:22:26.792+0800 restoring test.users from dump/test/users.bson
    ...
    2017-04-10T23:22:26.842+0800 restoring indexes for collection test.users from metadata
    2017-04-10T23:22:26.843+0800 finished restoring test.users (1124 documents)
    2017-04-10T23:22:26.843+0800 done

参考

文章目录
  1. 1. 副本集
    1. 1.1. 建立
    2. 1.2. 配置
      1. 1.2.1. 查看配置与状态
      2. 1.2.2. 修改配置
    3. 1.3. 设计
      1. 1.3.1. 仲裁成员
      2. 1.3.2. 隐藏成员
      3. 1.3.3. 延迟备份
      4. 1.3.4. 创建索引
      5. 1.3.5. 同步
      6. 1.3.6. 心跳
    4. 1.4. 成员状态
  2. 2. 分片
    1. 2.1. 作用
    2. 2.2. 分片集群
    3. 2.3. 启动
    4. 2.4. 片键
    5. 2.5.
  3. 3. 备份
    1. 3.1. 文件系统快照
    2. 3.2. cp或rsync
    3. 3.3. mongodump
  4. 4. 参考
|