MongoDB的入门学习五之在MongoDB集群和安全(不全)
副本集-Replica Sets
简介
MongoDB的副本集是一组维护相同数据集的mongod服务。副本集可提供冗余和高可用性,是所有的生产部署的基础。
也可以说,副本集类似于自动故障恢复功能的主从集群。通俗的讲就是用多台机器进行同一数据的异步同步,从而使用多台机器拥有同一数据的多个副本,并且当主库当掉时不需要用户干预的情况下自动切换其他备份服务器做主库。而且还可以利用副本集服务器做只读服务器,实现读写分离,提高负载。
冗余和数据可用性
复制提供冗余并提高数据可用性,提过在不同数据库服务器上提供多个数据副本,复制可提供一定级别的容错功能,以防止丢失单个数据库服务器。
mongoDB中的复制
副本集是一组维护相同数据集的mongod实例。副本集包含多个数据节点和可选的一个仲裁节点。在承载数据的节点中,一个且仅一个成员被视为主节点,而其他节点被视为次要(从)节点。
主节点接受所有写操作。副本集只能有一个主要能够确认具有{w:“most”}写入关注的写入;虽然在某些情况下,另外一个mongod实例可能暂时认为自己也是主要的。主要记录其操作日志中的数据集的所有更改,即oplog。
主从复制和副本集的区别
主从集群和副本集最大的区别就是副本集没有固定的"主节点";整个集群会选出一个"主节点",当其挂掉后,又在剩下的从节点选出其他节点为"主节点",副本集总有一个活跃点(主,primary)和一个或多个备份节点(从、seconday)。
副本集的三个角色
副本集有两种类型三种角色
两种类型:
主节点类型:数据操作的主要连接点,可读写。
次要(辅助,从)节点类型:数据冗余备份节点,可以读或选举。
三种角色
主要成员(Primary):主要接受所有写操作。就是主节点。
副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,但可以读操作。是默认的一种从节点类型。
仲裁者(Arbiter):不保留任何数据的副本,只具有投票选举作用。当然也可以将仲裁服务器维护为副本集的一部分,即副本成员同时也可以是仲裁者。也是一种节点类型。
副本集架构目标
副本集的创建
第一步:创建主节点
1 2 | #主节点 mkdir -p /mongodb/replica_sets/myrs_27017/log \ & mkdir -p /mongodb/replica_sets/myrs_27017/data/db |
1 | 新建或修改配置文件 |
1 | vim /mongodb/replica_sets/myrs_27017/mongod.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 | [root@localhost ~]# cat /mongodb/replica_sets/myrs_27017/mongod.conf systemLog: #MongoDB发送所有日志输出的目标指定为文件 destination: file #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径 path: "/mongodb/replica_sets/myrs_27017/log/mongod.log" #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。 logAppend: true storage: #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。 dbPath: "/mongodb/replica_sets/myrs_27017/data/db" journal: #启用或禁用持久性日志以确保数据文件保持有效和可恢复。 enabled: true processManagement: #启用在后台运行mongos或mongod进程的守护进程模式。 fork: true #指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID pidFilePath: "/mongodb/replica_sets/myrs_27017/log/mongod.pid" net: #服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip #bindIpAll: true #服务实例绑定的IP bindIp: localhost #bindIp #绑定的端口 port: 27017 replication: #副本集的名称 replSetName: myrs |
创建副本节点
建立存放数据和日志的目录
1 | mkdir -p /mongodb/replica_sets/myrs_27018/log \ & mkdir -p /mongodb/replica_sets/myrs_27018/data/db |
新建或修改配置
1 | vim /mongodb/replica_sets/myrs_27018/mongod.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 | systemLog: #MongoDB发送所有日志输出的目标指定为文件 destination: file #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径 path: "/mongodb/replica_sets/myrs_27018/log/mongod.log" #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。 logAppend: true storage: #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。 dbPath: "/mongodb/replica_sets/myrs_27018/data/db" journal: #启用或禁用持久性日志以确保数据文件保持有效和可恢复。 enabled: true processManagement: #启用在后台运行mongos或mongod进程的守护进程模式。 fork: true #指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID pidFilePath: "/mongodb/replica_sets/myrs_27018/log/mongod.pid" net: #服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip #bindIpAll: true #服务实例绑定的IP bindIp: localhost,192.168.0.2 #bindIp #绑定的端口 port: 27018 replication: #副本集的名称 replSetName: myrs |
启动
1 | /usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27018/mongod.conf |
查看配置文件
1 | cat /mongodb/replica_sets/myrs_27018/mongod.conf |
创建仲裁节点
1 2 3 | #仲裁节点 mkdir -p /mongodb/replica_sets/myrs_27019/log \ & mkdir -p /mongodb/replica_sets/myrs_27019/data/db |
仲裁节点,新建或修改配置文件:
1 | vim /mongodb/replica_sets/myrs_27019/mongod.conf |
1 | 配置文件 |
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 | systemLog: #MongoDB发送所有日志输出的目标指定为文件 destination: file #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径 path: "/mongodb/replica_sets/myrs_27019/log/mongod.log" #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。 logAppend: true storage: #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。 dbPath: "/mongodb/replica_sets/myrs_27019/data/db" journal: #启用或禁用持久性日志以确保数据文件保持有效和可恢复。 enabled: true processManagement: #启用在后台运行mongos或mongod进程的守护进程模式。 fork: true #指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID pidFilePath: "/mongodb/replica_sets/myrs_27019/log/mongod.pid" net: #服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip #bindIpAll: true #服务实例绑定的IP bindIp: localhost,192.168.0.2 #bindIp #绑定的端口 port: 27019 replication: #副本集的名称 replSetName: myrs |
启动节点服务:
1 | /usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27019/mongod.conf |
初始化配置副本集和主节点
使用客户端连接任意一个节点,但这里尽量要连接主节点
1 | /usr/local/mongodb/bin/mongo --host=39.97.100.141 --port=27017 |
1 | 结果,连接上之后,很多命令无法使用,比如show,dbs等必须初始化副本集才可以<br>准备初始化新的副本集<br>语法: |
1 | rs.initiate(configuration) |
1 |
1 | rs.initiate() |
提示:
1)“ok”的值为1,说明创建成功。
2)命令行提示符发生变化,变成了一个从节点角色,此时默认不能读写。稍等片刻,回车,变成主节点。
查看副本集的配置内容
说明:
返回包含当前副本集配置的文档。
语法:
1 | rs.conf(configuration) |
rs.config() 是该方法的别名。
configuration:可选,如果没有配置,则使用默认主节点配置。
示例
myrs:PRIMARY> rs.conf() { "_id" : "myrs", "version" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "localhost:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5f84288139869ebb123f25db") } }
说明:
1) "_id" : "myrs" :副本集的配置数据存储的主键值,默认就是副本集的名字
2) "members" :副本集成员数组,此时只有一个: "host" : "180.76.159.126:27017" ,该成员不是仲裁节点: "arbiterOnly" : false ,优先级(权重值): "priority" : 1,
3) "settings" :副本集的参数配置。
提示:副本集配置的查看命令,本质是查询的是 system.replset 的表中的数据:
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | myrs:PRIMARY> use local switched to db local myrs:PRIMARY> how collections 2020-10-12T20:22:22.925+0800 E QUERY [js] SyntaxError: missing ; before statement @(shell):1:4 myrs:PRIMARY> show collections oplog.rs replset.election replset.minvalid replset.oplogTruncateAfterPoint startup_log system.replset system.rollback.id myrs:PRIMARY> db.system.replset.find() { "_id" : "myrs" , "version" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true , "members" : [ { "_id" : 0, "host" : "localhost:27017" , "arbiterOnly" : false , "buildIndexes" : true , "hidden" : false , "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true , "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId( "5f84288139869ebb123f25db" ) } } |
查看副本集状态
检查副本集状态。
说明:
返回包含状态信息的文档。此输出使用从副本集的其他成员发送的心跳包中获得的数据反映副本集的当
前状态。
语法:
1 | rs.status() |
1 |
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 63 | myrs:PRIMARY> rs.status() { "set" : "myrs" , "date" : ISODate( "2020-10-12T12:22:54.806Z" ), "myState" : 1, "term" : NumberLong(1), "syncingTo" : "" , "syncSourceHost" : "" , "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1602505374, 1), "t" : NumberLong(1) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1602505374, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1602505374, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1602505374, 1), "t" : NumberLong(1) } }, "lastStableCheckpointTimestamp" : Timestamp(1602505344, 1), "members" : [ { "_id" : 0, "name" : "localhost:27017" , "health" : 1, "state" : 1, "stateStr" : "PRIMARY" , "uptime" : 9087, "optime" : { "ts" : Timestamp(1602505374, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate( "2020-10-12T12:22:54Z" ), "syncingTo" : "" , "syncSourceHost" : "" , "syncSourceId" : -1, "infoMessage" : "" , "electionTime" : Timestamp(1602496641, 2), "electionDate" : ISODate( "2020-10-12T09:57:21Z" ), "configVersion" : 1, "self" : true , "lastHeartbeatMessage" : "" } ], "ok" : 1, "operationTime" : Timestamp(1602505374, 1), "$clusterTime" : { "clusterTime" : Timestamp(1602505374, 1), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } |
说明:
1) "set" : "myrs" :副本集的名字
2) "myState" : 1:说明状态正常
3) "members" :副本集成员数组,此时只有一个: "name" : "localhost:27017" ,该成员的角色是 "stateStr" : "PRIMARY", 该节点是健康的: "health" : 1 。
添加副本从节点
在主节点添加从节点,将其他成员加入到副本集
语法:
1 | rs.add(host, arbiterOnly) |
示例
将27018的副本节点添加到副本集中:
1 2 3 4 5 6 7 8 9 10 11 12 | myrs:PRIMARY> rs.add( "localhost:27018" ) { "ok" : 1, "operationTime" : Timestamp(1602505461, 1), "$clusterTime" : { "clusterTime" : Timestamp(1602505461, 1), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } |
说明:
1) "ok" : 1 :说明添加成功。
查看副本集状态:
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | myrs:PRIMARY> rs.status() { "set" : "myrs" , "date" : ISODate( "2020-10-12T12:24:40.502Z" ), "myState" : 1, "term" : NumberLong(1), "syncingTo" : "" , "syncSourceHost" : "" , "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1602505474, 1), "t" : NumberLong(1) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1602505474, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1602505474, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1602505474, 1), "t" : NumberLong(1) } }, "lastStableCheckpointTimestamp" : Timestamp(1602505461, 1), "members" : [ { "_id" : 0, "name" : "localhost:27017" , "health" : 1, "state" : 1, "stateStr" : "PRIMARY" , "uptime" : 9193, "optime" : { "ts" : Timestamp(1602505474, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate( "2020-10-12T12:24:34Z" ), "syncingTo" : "" , "syncSourceHost" : "" , "syncSourceId" : -1, "infoMessage" : "" , "electionTime" : Timestamp(1602496641, 2), "electionDate" : ISODate( "2020-10-12T09:57:21Z" ), "configVersion" : 2, "self" : true , "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "localhost:27018" , "health" : 1, "state" : 2, "stateStr" : "SECONDARY" , "uptime" : 18, "optime" : { "ts" : Timestamp(1602505474, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1602505474, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate( "2020-10-12T12:24:34Z" ), "optimeDurableDate" : ISODate( "2020-10-12T12:24:34Z" ), "lastHeartbeat" : ISODate( "2020-10-12T12:24:39.546Z" ), "lastHeartbeatRecv" : ISODate( "2020-10-12T12:24:39.823Z" ), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "" , "syncingTo" : "localhost:27017" , "syncSourceHost" : "localhost:27017" , "syncSourceId" : 0, "infoMessage" : "" , "configVersion" : 2 } ], "ok" : 1, "operationTime" : Timestamp(1602505474, 1), "$clusterTime" : { "clusterTime" : Timestamp(1602505474, 1), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } |
说明:
1) "name" : "localhost:27018" 是第二个节点的名字,其角色是 "stateStr" :"SECONDARY"
添加仲裁从节点
添加一个仲裁节点到副本集
语法:
1 | rs.addArb(host) |
将27019的仲裁节点添加到副本集中:
1 2 3 4 5 6 7 8 9 10 11 12 | myrs:PRIMARY> rs.addArb( "localhost:27019" ) { "ok" : 1, "operationTime" : Timestamp(1602505564, 2), "$clusterTime" : { "clusterTime" : Timestamp(1602505564, 2), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } |
说明:
1) "ok" : 1 :说明添加成功。
查看副本集状态:
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | myrs:PRIMARY> rs.status() { "set" : "myrs" , "date" : ISODate( "2020-10-12T12:26:27.106Z" ), "myState" : 1, "term" : NumberLong(1), "syncingTo" : "" , "syncSourceHost" : "" , "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1602505584, 1), "t" : NumberLong(1) }, "readConcernMajorityOpTime" : { "ts" : Timestamp(1602505584, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1602505584, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1602505584, 1), "t" : NumberLong(1) } }, "lastStableCheckpointTimestamp" : Timestamp(1602505564, 2), "members" : [ { "_id" : 0, "name" : "localhost:27017" , "health" : 1, "state" : 1, "stateStr" : "PRIMARY" , "uptime" : 9300, "optime" : { "ts" : Timestamp(1602505584, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate( "2020-10-12T12:26:24Z" ), "syncingTo" : "" , "syncSourceHost" : "" , "syncSourceId" : -1, "infoMessage" : "" , "electionTime" : Timestamp(1602496641, 2), "electionDate" : ISODate( "2020-10-12T09:57:21Z" ), "configVersion" : 3, "self" : true , "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "localhost:27018" , "health" : 1, "state" : 2, "stateStr" : "SECONDARY" , "uptime" : 125, "optime" : { "ts" : Timestamp(1602505584, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1602505584, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate( "2020-10-12T12:26:24Z" ), "optimeDurableDate" : ISODate( "2020-10-12T12:26:24Z" ), "lastHeartbeat" : ISODate( "2020-10-12T12:26:26.804Z" ), "lastHeartbeatRecv" : ISODate( "2020-10-12T12:26:25.818Z" ), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "" , "syncingTo" : "localhost:27017" , "syncSourceHost" : "localhost:27017" , "syncSourceId" : 0, "infoMessage" : "" , "configVersion" : 3 }, { "_id" : 2, "name" : "localhost:27019" , "health" : 1, "state" : 7, "stateStr" : "ARBITER" , "uptime" : 22, "lastHeartbeat" : ISODate( "2020-10-12T12:26:26.804Z" ), "lastHeartbeatRecv" : ISODate( "2020-10-12T12:26:26.818Z" ), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "" , "syncingTo" : "" , "syncSourceHost" : "" , "syncSourceId" : -1, "infoMessage" : "" , "configVersion" : 3 } ], "ok" : 1, "operationTime" : Timestamp(1602505584, 1), "$clusterTime" : { "clusterTime" : Timestamp(1602505584, 1), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } |
说明:
1) "name" : "localhost:27019" 是第二个节点的名字,其角色是 "stateStr" : "ARBITER"
副本集的数据读写操作
目标:测试三个不同角色的节点的数据读写情况。
登录主节点27017,写入和读取数据:
1 2 3 4 5 6 7 8 9 | myrs:PRIMARY> use articledb switched to db articledb myrs:PRIMARY> db articledb myrs:PRIMARY> db.comment.insert({ "articleid" : "100000" , "content" : "今天天气真好,阳光明媚" , "userid" : "1001" , "nickname" : "Rose" , "createdatetime" : new Date()}) WriteResult({ "nInserted" : 1 }) myrs:PRIMARY> db.comment.find() { "_id" : ObjectId( "5f844c02d75b8f0f5671bf36" ), "articleid" : "100000" , "content" : "今天天气真好,阳光明媚" , "userid" : "1001" , "nickname" : "Rose" , "createdatetime" : ISODate( "2020-10-12T12:28:50.774Z" ) } myrs:PRIMARY> |
登录从节点27018
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 | [root@localhost ~]# /usr/local/mongodb/bin/mongo --port 27018 MongoDB shell version v4.0.10 connecting to: mongodb: //127.0.0.1:27018/?gssapiServiceName=mongodb Implicit session: session { "id" : UUID( "bc619f58-4a9a-4863-99da-627459fc6f7f" ) } MongoDB server version: 4.0.10 Server has startup warnings: 2020-10-12T17:54:00.894+0800 I CONTROL [initandlisten] 2020-10-12T17:54:00.894+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database. 2020-10-12T17:54:00.894+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted. 2020-10-12T17:54:00.894+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended. 2020-10-12T17:54:00.894+0800 I CONTROL [initandlisten] 2020-10-12T17:54:00.895+0800 I CONTROL [initandlisten] 2020-10-12T17:54:00.895+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always' . 2020-10-12T17:54:00.895+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2020-10-12T17:54:00.895+0800 I CONTROL [initandlisten] 2020-10-12T17:54:00.895+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always' . 2020-10-12T17:54:00.895+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2020-10-12T17:54:00.895+0800 I CONTROL [initandlisten] myrs:SECONDARY> show dbs; 2020-10-12T20:29:40.653+0800 E QUERY [js] Error: listDatabases failed:{ "operationTime" : Timestamp(1602505774, 1), "ok" : 0, "errmsg" : "not master and slaveOk=false" , "code" : 13435, "codeName" : "NotMasterNoSlaveOk" , "$clusterTime" : { "clusterTime" : Timestamp(1602505774, 1), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 Mongo.prototype.getDBs@src/mongo/shell/mongo.js:139:1 shellHelper.show@src/mongo/shell/utils.js:882:13 shellHelper@src/mongo/shell/utils.js:766:15 @(shellhelp2):1:1 |
发现,不能读取集合的数据。当前从节点只是一个备份,不是奴隶节点,无法读取数据,写当然更不行。
因为默认情况下,从节点是没有读写权限的,可以增加读的权限,但需要进行设置。
设置读操作权限:
说明:
设置为奴隶节点,允许在从成员上运行读的操作
语法:
1 | rs.slaveOk() |
提示:
该命令是 db.getMongo().setSlaveOk() 的简化命令。
【示例】
在27018上设置作为奴隶节点权限,具备读权限
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 | myrs:SECONDARY> how dbs; 2020-10-12T20:30:10.502+0800 E QUERY [js] SyntaxError: missing ; before statement @(shell):1:4 myrs:SECONDARY> show dbs; admin 0.000GB articledb 0.000GB config 0.000GB local 0.000GB myrs:SECONDARY> se articledb 2020-10-12T20:30:20.606+0800 E QUERY [js] SyntaxError: missing ; before statement @(shell):1:3 myrs:SECONDARY> use articledb switched to db articledb myrs:SECONDARY> show collections comment myrs:SECONDARY> db.comment.find() { "_id" : ObjectId( "5f844c02d75b8f0f5671bf36" ), "articleid" : "100000" , "content" : "今天天气真好,阳光明媚" , "userid" : "1001" , "nickname" : "Rose" , "createdatetime" : ISODate( "2020-10-12T12:28:50.774Z" ) } myrs:SECONDARY> db.comment.insert({ "articleid" : "100002" , "content" : "今天天气真好,阳光明媚" , "userid" : "1001" , "nickname" : "Rose" , "createdatetime" : new Date()}) WriteCommandError({ "operationTime" : Timestamp(1602505854, 1), "ok" : 0, "errmsg" : "not master" , "code" : 10107, "codeName" : "NotMaster" , "$clusterTime" : { "clusterTime" : Timestamp(1602505854, 1), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } }) myrs:SECONDARY> |
仲裁者节点,不存放任何业务数据的,可以登录查看
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 | [root@localhost ~]# /usr/local/mongodb/bin/mongo --port 27019 MongoDB shell version v4.0.10 connecting to: mongodb: //127.0.0.1:27019/?gssapiServiceName=mongodb Implicit session: session { "id" : UUID( "8f6cfc15-cd7b-43d0-b13e-e2a9de81e017" ) } MongoDB server version: 4.0.10 Server has startup warnings: 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database. 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted. 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended. 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always' . 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always' . 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] myrs:ARBITER> rs.slaveOk() myrs:ARBITER> show dbs local 0.000GB myrs:ARBITER> use local switched to db local myrs:ARBITER> show collections replset.minvalid replset.oplogTruncateAfterPoint startup_log system.replset system.rollback.id myrs:ARBITER> |
发现,只存放副本集配置等数据。
如果要取消作为奴隶节点的读权限
1 2 3 | rs.slaveOk( false ) #查看 db.comment.find() |
仲裁者节点,不存放任何业务数据的,可以登录查看
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 | [root@localhost ~]# /usr/local/mongodb/bin/mongo --port 27019 MongoDB shell version v4.0.10 connecting to: mongodb: //127.0.0.1:27019/?gssapiServiceName=mongodb Implicit session: session { "id" : UUID( "8f6cfc15-cd7b-43d0-b13e-e2a9de81e017" ) } MongoDB server version: 4.0.10 Server has startup warnings: 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database. 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted. 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended. 2020-10-12T17:56:50.460+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always' . 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always' . 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] ** We suggest setting it to 'never' 2020-10-12T17:56:50.461+0800 I CONTROL [initandlisten] myrs:ARBITER> rs.slaveOk() myrs:ARBITER> show dbs local 0.000GB myrs:ARBITER> use local switched to db local myrs:ARBITER> show collections replset.minvalid replset.oplogTruncateAfterPoint startup_log system.replset system.rollback.id myrs:ARBITER> |
主节点的选举原则
MongoDB在副本集中,会自动进行主节点的选举,主节点选举的触发条件:
1) 主节点故障
2) 主节点网络不可达(默认心跳信息为10秒)
3) 人工干预(rs.stepDown(600))
一旦触发选举,就要根据一定规则来选主节点。
选举规则是根据票数来决定谁获胜:
票数最高,且获得了“大多数”成员的投票支持的节点获胜。
“大多数”的定义为:假设复制集内投票成员数量为N,则大多数为 N/2 + 1。例如:3个投票成员,则大多数的值是2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。
若票数相同,且都获得了“大多数”成员的投票支持的,数据新的节点获胜。数据的新旧是通过操作日志oplog来对比的。
在获得票数的时候,优先级(priority)参数影响重大。可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为0-1000,相当于可额外增加
0-1000的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员更有资格成为主要成员,更低的值可使成员更不符合条件。
默认情况下,优先级的值是1
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 63 64 65 | myrs:PRIMARY> rs.conf() { "_id" : "myrs" , "version" : 3, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true , "members" : [ { "_id" : 0, "host" : "localhost:27017" , "arbiterOnly" : false , "buildIndexes" : true , "hidden" : false , "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "localhost:27018" , "arbiterOnly" : false , "buildIndexes" : true , "hidden" : false , "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "localhost:27019" , "arbiterOnly" : true , "buildIndexes" : true , "hidden" : false , "priority" : 0, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true , "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId( "5f84288139869ebb123f25db" ) } } myrs:PRIMARY> |
可以看出,主节点和副本节点的优先级各为1,即,默认可以认为都已经有了一票。但选举节点,优先级是0,(要注意是,官方说了,选举节点的优先级必须是0,不能是别的值。即不具备选举权,但具有投票权)
【了解】修改优先级
比如,下面提升从节点的优先级:
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | myrs:PRIMARY> cfg=rs.conf() { "_id" : "myrs" , "version" : 3, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true , "members" : [ { "_id" : 0, "host" : "localhost:27017" , "arbiterOnly" : false , "buildIndexes" : true , "hidden" : false , "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "localhost:27018" , "arbiterOnly" : false , "buildIndexes" : true , "hidden" : false , "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "localhost:27019" , "arbiterOnly" : true , "buildIndexes" : true , "hidden" : false , "priority" : 0, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true , "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : -1, "catchUpTakeoverDelayMillis" : 30000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId( "5f84288139869ebb123f25db" ) } } myrs:PRIMARY> cfg.members[1].priority=2 2 myrs:PRIMARY> rs.reconfig(cfg) { "ok" : 1, "operationTime" : Timestamp(1602509361, 1), "$clusterTime" : { "clusterTime" : Timestamp(1602509361, 1), "signature" : { "hash" : BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAA=" ), "keyId" : NumberLong(0) } } } myrs:PRIMARY> |
故障测试
副本节点故障测试
1 2 3 4 | db.comment.insert({ "_id" : "2" , "articleid" : "100001" , "content" : "我们不应该把清晨浪费在 手机上,健康很重要,一杯温水幸福你我他。" , "userid" : "1002" , "nickname" : "相忘于江 湖" , "createdatetime" : new Date( "2019-08- 05T22:08:15.522Z" ), "likenum" :NumberInt(1000), "state" : "1" }) |
再启动从节点,会发现,主节点写入的数据,会自动同步给从节点。
具体操作:
1 2 3 4 | #查看进程 ps -ef | grep mongo #杀死27018的服务 kill -9 xxx |
1 | 进入主节点 |
1 2 | #查看 db.comment.find() |
在进行添加操作就有两条数据现在进入副本节点发现就挂了不能进行操作。
我们在把27018的节点给恢复一下发现父节点会同步主节点的数据。
主节点故障测试
关闭27017 节点发现,从节点和仲裁节点对27017 的心跳失败,当失败超过 10 秒,此时因为没有主节点了,会自动发起投票。 而副本节点只有27018 ,因此,候选人只有一个就是 27018 ,开始投票。 27019向 27018 投了一票, 27018 本身自带一票,因此共两票,超过了 “ 大多数 ”
27019 是仲裁节点,没有选举权, 27018 不向其投票,其票数是0。 最终结果, 27018 成为主节点。具备读写功能。 在 27018 写入数据查看。
1 2 3 4 5 | db.comment.insert({ "_id" : "3" , "articleid" : "100001" , "content" : "我夏天空腹喝凉开水,冬 天喝温开水" , "userid" : "1005" , "nickname" : "伊人憔悴" , "createdatetime" : new Date( "2019- 08-05T23:58:51.485Z" ), "likenum" :NumberInt(888), "state" : "1" }) |
具体操作:
1 2 3 4 | #查看进程 ps -ef | grep mongo #杀死27017的服务 kill -9 xxx |
1 | 进入父节点进行添加<br>再把主节点给恢复进行查看 |
仲裁节点和主节点故障
先关掉仲裁节点27019, 关掉现在的主节点27018 登录27017后,发现,27017仍然是从节点,副本集中没有主节点了,导致此时,副本集是只读状态, 无法写入。 为啥不选举了?因为27017的票数,没有获得大多数,即没有大于等于2,它只有默认的一票(优先级是1) 如果要触发选举,随便加入一个成员即可。
如果只加入27019仲裁节点成员,则主节点一定是27017,因为没得选了,仲裁节点不参与选举,但参与投票。(不演示)
如果只加入27018节点,会发起选举。因为27017和27018都是两票,则按照谁数据新,谁当主节点。
仲裁节点和从节点故障
先关掉仲裁节点27019, 关掉现在的副本节点27018 。10秒后,27017主节点自动降级为副本节点。(服务降级) 。副本集不可写数据了,已经故障了。
不小心把仲裁节点设置主节点了
删除27019主节点
1 | rs.remove( "localhost:27019" ); |
- 添加仲裁节点
rs.addArb("172.16.10.199:27020");
- 添加从节点
rs.add("172.16.10.90:27020");
1 | 修改节点 |
1 2 3 4 5 6 7 8 9 10 | 修改从节点为只读节点 获取当前配置,修改以后从新写入配置 cfg = rs.conf(); cfg.members[2].priority=0 rs.reconfig(cfg);复制代码 修改从节点为永远不可能被选为主节点(非必须) 获取当前配置,修改以后从新写入配置 cfg = rs.conf(); cfg.members[2].votes=0 rs.reconfig(cfg);复制代码 |
副本集状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | rs.status() "health" : 1, #代表机器正常 "stateStr" : "PRIMARY" , #代表是主节点,可读写,其中有以下几下状态 1. STARTUP:刚加入到复制集中,配置还未加载 2. STARTUP2:配置已加载完,初始化; 3. RECOVERING:正在恢复,不适用读 4. ARBITER: 仲裁者 5. DOWN:节点不可到达 6. UNKNOWN:未获取其他节点状态而不知是什么状态,一般发生在只有两个成员的架构 7. REMOVED:移除复制集 8. ROLLBACK:数据回滚,在回滚结束时,转移到RECOVERING或SECONDARY状态 9. FATAL:出错。查看日志grep “replSet FATAL”找出错原因,重新做同步 10. PRIMARY:主节点 11. SECONDARY:备份节点 |
1 | <br><br> |
1 | <br><br><br><br><br><br><br> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本