第1章 MongoDB复制集
1.1 MongoDB复制集简介
一组Mongodb复制集,就是一组mongod进程,这些进程维护同一个数据集合。复制集提供了数据冗余和高等级的可靠性,这是生产部署的基础。
保证数据在生产部署时的冗余和可靠性,通过在不同的机器上保存副本来保证数据的不会因为单点损坏而丢失。能够随时应对数据丢失、机器损坏带来的风险,牛逼到不行。
换一句话来说,还能提高读取能力,用户的读取服务器和写入服务器在不同的地方,而且,由不同的服务器为不同的用户提供服务,提高整个系统的负载,简直就是云存储的翻版...
一组复制集就是一组mongod实例掌管同一个数据集,实例可以在不同的机器上面。实例中包含一个主导,接受客户端所有的写入操作,其他都是副本实例,从主服务器上获得数据并保持同步。
主服务器很重要,包含了所有的改变操作(写)的日志。但是副本服务器集群包含有所有的主服务器数据,因此当主服务器挂掉了,就会在副本服务器上重新选取一个成为主服务器
每个复制集还有一个仲裁者,仲裁者不存储数据,只是负责通过心跳包来确认集群中集合的数量,并在主服务器选举的时候作为仲裁决定结果。
1.2 复制的基本架构
一般是1主2从的架构或者是1主多从的架构,普通主从1主库,2个普通从库。由三个节点构成复制集的原因是mongodb中原生态支持心跳检测,并且通过raft的投票原则,规定必须大于50%的票数为准,因此需要至少3个以上的服务器。在mongodb中,虽然我们可以通过一些设置让从节点可以提供服务,但是推荐从节点不让做任何操作。
1.2.1 raft算法特点
- 自动投票选择主库
- 自动通知mongodb客户端连接新的主库
1.2.2 主从库变化分析
- 一个包含3个mongod的复制集架构如下所示:
- 如果主服务器失效,会变成:
- 如果加上可选仲裁节点:
- 如果主服务器失效:
1.3 特殊节点
1.3.1 仲裁节点
arbiter节点只做投票,不做数据复制,可以选择一个配置较低的主机部署,尽量和主节点放在不同的位置。
1.3.2 隐藏节点
隐藏节点是不参与选主的节点,但是参与投票,即使我们设定了复制集读选项,客户端也不会把读请求分发到隐藏节点上,这些隐藏节点将不会收到来自应用程序的请求。我们可以将隐藏节点专用于报表节点或是备份节点,并且一般会把delay(延时)节点设置为hidden,主要用做备份的节点。
隐藏节点是priority优先级为0的节点。
1.3.3 延时节点
延时节点的数据集是延时的,因此它可以帮助我们在人为误操作或是其他意外情况下恢复数据。举个例子:当应用升级失败或是误操作删除了表和数据库时,我们可以通过延时节点进行数据恢复。
1.4 复制集技术实现
1.4.1 搭建mongodb
[root@db01 ~]# cat >> /etc/rc.local << EOF if test -f /sys/kernel/mm/transparent_hugepage/enabled; then echo never > /sys/kernel/mm/transparent_hugepage/enabled fi if test -f /sys/kernel/mm/transparent_hugepage/defrag; then echo never > /sys/kernel/mm/transparent_hugepage/defrag fi EOF [root@db01 ~]# useradd -u900 mongod [root@db01 ~]# passwd mongod Changing password for user mongod. New password: 123456 BAD PASSWORD: it is too simplistic/systematic BAD PASSWORD: is too simple Retype new password: 123456 passwd: all authentication tokens updated successfully. [root@db01 ~]# cd /server/tools/ [root@db01 tools]# tar xf mongodb-linux-x86_64-3.2.8.tgz [root@db01 tools]# cp -a mongodb-linux-x86_64-3.2.8/bin /usr/local/mongodb/ [root@db01 ~]# su - mongod [mongod@db01 ~]$ echo "export PATH=/usr/local/mongodb/bin:$PATH" >> .bash_profile [mongod@db01 ~]$ source .bash_profile
1.4.2 创建多实例目录并授权
[root@db01 ~]# mkdir -p /usr/local/mongodb/{27017,27018,27019,27020}/{conf,data,log} [root@db01 ~]# chown -R mongod:mongod /usr/local/mongodb/
1.4.3 编写配置文件
[root@db01 ~]# vim /usr/local/mongodb/27017/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/27018/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/27019/conf/mongod.yml [root@db01 ~]# vim /usr/local/mongodb/27020/conf/mongod.yml # 主从库配置文件只要将标红的地方修改为相应的端口号即可 systemLog: destination: file path: "/usr/local/mongodb/27017/log/mongod.log" # path: "/usr/local/mongodb/27018/log/mongod.log" # path: "/usr/local/mongodb/27019/log/mongod.log" # path: "/usr/local/mongodb/27020/log/mongod.log" logAppend: true processManagement: fork: true storage: journal: enabled: true dbPath: "/usr/local/mongodb/27017/data" # dbPath: "/usr/local/mongodb/27018/data" # dbPath: "/usr/local/mongodb/27019/data" # dbPath: "/usr/local/mongodb/27020/data" directoryPerDB: true wiredTiger: engineConfig: cacheSizeGB: 1 directoryForIndexes: true collectionConfig: blockCompressor: zlib indexConfig: prefixCompression: true net: port: 27017 # port: 27018 # port: 27019 # port: 27020 replication: oplogSizeMB: 2048 replSetName: repl
1.4.4 开启所有节点服务
[root@db01 ~]# su - mongod [mongod@db01 ~]$ mongod -f /usr/local/mongodb/27017/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/27018/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/27019/conf/mongod.yml [mongod@db01 ~]$ mongod -f /usr/local/mongodb/27020/conf/mongod.yml
1.4.5 配置主库
[mongod@db01 ~]$ mongo --port 27017 MongoDB shell version: 3.2.8 connecting to: master > config = {_id: 'repl', members: [ ... {_id: 0, host: '10.0.0.41:27017'}, ... {_id: 1, host: '10.0.0.41:27018'}, ... {_id: 2, host: '10.0.0.41:27019'}, ... {_id: 3, host: '10.0.0.41:27020'}] ... } { "_id" : "repl", "members" : [ { "_id" : 0, "host" : "10.0.0.41:27017" }, { "_id" : 1, "host" : "10.0.0.41:27018" }, { "_id" : 2, "host" : "10.0.0.41:27019" }, { "_id" : 3, "host" : "10.0.0.41:27020" } ] } > > rs.initiate(config) { "ok" : 1 }
- 配置命令:
# 1主2从,从库为普通从库 config = {_id: 'repl', members: [ {_id: 0, host: '10.0.0.41:27017'}, {_id: 1, host: '10.0.0.41:27018'}, {_id: 2, host: '10.0.0.41:27019'}] } rs.initiate(config) # 1主1从1个arbiter config = {_id: 'repl', members: [ {_id: 0, host: '10.0.0.41:27017'}, {_id: 1, host: '10.0.0.41:27018'}, {_id: 2, host: '10.0.0.41:27019',"arbiterOnly":true}] } rs.initiate(config)
1.4.6 开始测试
1.4.6.1 主库操作
[mongod@db01 ~]$ mongo --port 27017 repl:SECONDARY> db.movies.insert([ { "title" : "Jaws", "year" : 1975, "imdb_rating" : 8.1 }, { "title" : "Batman", "year" : 1989, "imdb_rating" : 7.6 }, ] ); BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] }) repl:PRIMARY> db.movies.find().pretty(); { "_id" : ObjectId("5a1f6b48360e935f29ffda21"), "title" : "Jaws", "year" : 1975, "imdb_rating" : 8.1 } { "_id" : ObjectId("5a1f6b48360e935f29ffda22"), "title" : "Batman", "year" : 1989, "imdb_rating" : 7.6 }
1.4.6.2 从库操作
[root@db01 ~]# su - mongod [mongod@db01 ~]$ mongo --port 27018 MongoDB shell version: 3.2.8 connecting to: 127.0.0.1:27018/test repl:SECONDARY> db.movies.find().pretty(); # 在mongodb复制集当中,默认从库不允许读写,所以报错 Error: error: { "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 } repl:SECONDARY> rs.slaveOk() # 开启从库查询操作 repl:SECONDARY> db.movies.find().pretty(); { "_id" : ObjectId("5a1f928e31a2d09d5ef64651"), "title" : "Batman", "year" : 1989, "imdb_rating" : 7.6 } { "_id" : ObjectId("5a1f928e31a2d09d5ef64650"), "title" : "Jaws", "year" : 1975, "imdb_rating" : 8.1 }
1.5 复制集相关操作
1.5.1 查看复制集状态信息
1.5.1.1 查看整体复制集状态(主从节点均可操作)
repl:PRIMARY> rs.status(); { "set" : "repl", "date" : ISODate("2017-11-30T05:20:12.553Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "10.0.0.41:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 716, "optime" : { "ts" : Timestamp(1512018574, 3), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-30T05:09:34Z"), "electionTime" : Timestamp(1512018558, 1), "electionDate" : ISODate("2017-11-30T05:09:18Z"), "configVersion" : 1, "self" : true }, { "_id" : 1, "name" : "10.0.0.41:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 663, "optime" : { "ts" : Timestamp(1512018574, 3), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-30T05:09:34Z"), "lastHeartbeat" : ISODate("2017-11-30T05:20:11.617Z"), "lastHeartbeatRecv" : ISODate("2017-11-30T05:20:10.613Z"), "pingMs" : NumberLong(0), "syncingTo" : "10.0.0.41:27017", "configVersion" : 1 }, { "_id" : 2, "name" : "10.0.0.41:27019", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 663, "optime" : { "ts" : Timestamp(1512018574, 3), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-30T05:09:34Z"), "lastHeartbeat" : ISODate("2017-11-30T05:20:11.617Z"), "lastHeartbeatRecv" : ISODate("2017-11-30T05:20:10.613Z"), "pingMs" : NumberLong(0), "syncingTo" : "10.0.0.41:27017", "configVersion" : 1 } ], "ok" : 1 }
1.5.1.2 查看当前是否是主节点
# 主节点操作 repl:PRIMARY> rs.isMaster(); { "hosts" : [ "10.0.0.41:27017", "10.0.0.41:27018", "10.0.0.41:27019" ], "setName" : "repl", "setVersion" : 1, "ismaster" : true, "secondary" : false, "primary" : "10.0.0.41:27017", "me" : "10.0.0.41:27017", "electionId" : ObjectId("7fffffff0000000000000001"), "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "maxWriteBatchSize" : 1000, "localTime" : ISODate("2017-11-30T05:21:07.552Z"), "maxWireVersion" : 4, "minWireVersion" : 0, "ok" : 1 } # 从节点操作 repl:SECONDARY> rs.isMaster(); { "hosts" : [ "10.0.0.41:27017", "10.0.0.41:27018", "10.0.0.41:27019" ], "setName" : "repl", "setVersion" : 1, "ismaster" : false, "secondary" : true, "primary" : "10.0.0.41:27017", "me" : "10.0.0.41:27018", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "maxWriteBatchSize" : 1000, "localTime" : ISODate("2017-11-30T05:21:37.904Z"), "maxWireVersion" : 4, "minWireVersion" : 0, "ok" : 1 }
1.5.2 节点相关操作
1.5.2.1 新增一个从节点
repl:PRIMARY> rs.add("10.0.0.41:27020") { "ok" : 1 }
1.5.2.2 删除一个节点
repl:PRIMARY> rs.remove("10.0.0.41:27019") { "ok" : 1 }
1.5.2.3 新增一个仲裁节点
repl:PRIMARY> rs.addArb("10.0.0.41:27019") { "ok" : 1 }
1.5.2.4 查看节点状态
repl:PRIMARY> rs.status() { "set" : "repl", "date" : ISODate("2017-11-30T05:23:27.791Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), "members" : [ { "_id" : 0, "name" : "10.0.0.41:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 911, "optime" : { "ts" : Timestamp(1512019386, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-30T05:23:06Z"), "electionTime" : Timestamp(1512018558, 1), "electionDate" : ISODate("2017-11-30T05:09:18Z"), "configVersion" : 4, "self" : true }, { "_id" : 1, "name" : "10.0.0.41:27018", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 859, "optime" : { "ts" : Timestamp(1512019386, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-30T05:23:06Z"), "lastHeartbeat" : ISODate("2017-11-30T05:23:26.608Z"), "lastHeartbeatRecv" : ISODate("2017-11-30T05:23:26.608Z"), "pingMs" : NumberLong(0), "syncingTo" : "10.0.0.41:27017", "configVersion" : 4 }, { "_id" : 3, "name" : "10.0.0.41:27020", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 75, "optime" : { "ts" : Timestamp(1512019386, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2017-11-30T05:23:06Z"), "lastHeartbeat" : ISODate("2017-11-30T05:23:26.608Z"), "lastHeartbeatRecv" : ISODate("2017-11-30T05:23:26.613Z"), "pingMs" : NumberLong(0), "syncingTo" : "10.0.0.41:27018", "configVersion" : 4 }, { "_id" : 4, "name" : "10.0.0.41:27019", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 19, "lastHeartbeat" : ISODate("2017-11-30T05:23:26.608Z"), "lastHeartbeatRecv" : ISODate("2017-11-30T05:23:23.595Z"), "pingMs" : NumberLong(0), "configVersion" : 4 } ], "ok" : 1 }
1.5.3 添加特殊节点
1.5.3.1 添加特殊节点的方式
- 可以在搭建过程中设置特殊节点
- 可以通过修改配置的方式将普通从节点设置为特殊节点
1.5.3.2 设置延时节点
repl:PRIMARY> cfg.members[2].priority=0 0 repl:PRIMARY> cfg.members[2].slaveDelay=120 120
1.5.3.3 设置隐藏节点
repl:PRIMARY> cfg.members[2].hidden=true true
1.5.3.4 设置仲裁节点
repl:PRIMARY> cfg.members[3].arbiterOnly=true true
1.5.3.5 更新配置文件
repl:PRIMARY> rs.reconfig(cfg) { "ok" : 1 }
1.5.3.6 查看节点配置情况
repl:PRIMARY> rs.conf() { "_id" : "repl", "version" : 5, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "10.0.0.41:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "10.0.0.41:27018", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 3, "host" : "10.0.0.41:27020", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : true, "priority" : 0, "tags" : { }, "slaveDelay" : NumberLong(120), "votes" : 1 }, { "_id" : 4, "host" : "10.0.0.41:27019", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5a1f92740ab2685731c4f4d7") } }
