Skip to content

1.系统设置

禁用透明页
ssh mongo1 "echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag"
ssh mongo1 "echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled"
禁用透明页
ssh mongo1 "echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag"
ssh mongo1 "echo 'never' > /sys/kernel/mm/transparent_hugepage/enabled"
  • 添加 /etc/security/limits.conf
*                soft   fsize            unlimited
*                hard   fsize            unlimited
*                soft   cpu              unlimited
*                hard   cpu              unlimited
*                soft   as               unlimited
*                hard   as               unlimited
*                soft   memlock          unlimited
*                hard   memlock          unlimited
*                soft   nofile           64000
*                hard   nofile           64000
*                soft   nproc            64000
*                hard   nproc            64000
*                soft   fsize            unlimited
*                hard   fsize            unlimited
*                soft   cpu              unlimited
*                hard   cpu              unlimited
*                soft   as               unlimited
*                hard   as               unlimited
*                soft   memlock          unlimited
*                hard   memlock          unlimited
*                soft   nofile           64000
*                hard   nofile           64000
*                soft   nproc            64000
*                hard   nproc            64000
  • /etc/sysctl.conf
ssh mongo1 "echo "net.ipv4.tcp_keepalive_time = 300" >> /etc/sysctl.conf"
ssh mongo1 "echo "net.ipv4.tcp_keepalive_time = 300" >> /etc/sysctl.conf"
  • 关闭防火墙和selinux
systemctl disable --now firewalld
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config && setenforce 0
systemctl disable --now firewalld
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config && setenforce 0
  • 磁盘 io 调度策略
 echo deadline > /sys/block/sda/queue/scheduler
 echo deadline > /sys/block/sda/queue/scheduler
  • 修改内存分配策略
echo 0 > /proc/sys/vm/zone_reclaim_mode
sysctl -w vm.zone_reclaim_mode=0
sysctl vm.overcommit_memory=1
echo 0 > /proc/sys/vm/zone_reclaim_mode
sysctl -w vm.zone_reclaim_mode=0
sysctl vm.overcommit_memory=1

三种方式区别

mongodb集群的副本集和sharding模式目前是用的最广的方案,通常这2种方案的选择通过数据量和并发数来权衡。在GB级别的基本上副本集方案可满足,TB级别或以上采用sharding模式,解决单机容量和单机并发能力

  • Master-Slaver 是一种主从副本的模式,目前已经不推荐使用。
  • Replica Set 模式取代了 Master-Slaver 模式,是一种互为主从的关系。Replica Set 将数据复制多份保存,不同服务器保存同一份数据,在出现故障时自动切换,实现故障转移,在实际生产中非常实用。
  • Sharding 模式适合处理大量数据,它将数据分开存储,不同服务器保存不同的数据,所有服务器数据的总和即为整个数据集。

2.MongoDB 副本集介绍

MongoDB副本集(Replica Set)其实就是具有自动故障恢复功能的主从集群,和主从复制最大的区别就是在副本集中没有固定的“主节点;整个副本集会选出一个节点作为“主节点”,当其挂掉后,再在剩下的从节点中选举一个节点成为新的“主节点”,在副本集中总有一个主节点(primary)和一个或多个备份节点(secondary)

MongoDB 副本集(Replica Set)包括主节点(primary)跟副本节点(Secondaries)。

主节点只能有一个,所有的写操作请求都在主节点上面处理。副本节点可以有多个,通过同步主节点的操作日志(oplog)来备份主节点数据。

在主节点挂掉后,有选举权限的副本节点会自动发起选举,并从中选举出新的主节点

搭建一个副本集集群最少需要三个节点:一个主节点,两个备份节点,如果三个节点分布合理,基本可以保证线上数据99.9%安全

除了primary和secondary之外,副本集中的节点还可以是以下角色

官方帮助文档:https://docs.mongodb.com/manual/replication/

一主两从

一主一从

如果只有一个主节点,一个副本节点,且没有资源拿来当第二个副本节点,那就可以起一个仲裁者节点(arbiter),不存数据,只用来选举用,如下图所示

主要成员介绍

对于副本集成员属性,特别需要说明下这几个:priority、hidden、slaveDelay、tags、votes

priority

对于副本节点,可以通过该属性来增大或者减小该节点被选举成为主节点的可能性,取值范围为0-1000(如果是arbiters,则取值只有0或者1),数据越大,成为主节点的可能性越大,如果被配置为0,那么他就不能被选举成为主节点,而且也不能主动发起选举。

这种特性一般会被用在有多个数据中心的情况下,比如一个主数据中心,一个备份数据中心,主数据中心速度会更快,如果主节点挂掉,我们肯定希望新主节点也在主数据中心产生,那么我们就可以设置在备份数据中心的副本节点优先级为0,如下图所示:

hidden

隐藏节点会从主节点同步数据,但对客户端不可见,在mongo shell 执行 db.isMaster() 方法也不会展示该节点,隐藏节点必须Priority为0,即不可以被选举成为主节点。但是如果有配置选举权限的话,可以参与选举。

因为隐藏节点对客户端不可见,所以跟客户端不会互相影响,可以用来备份数据或者跑一些后端定时任务之类的操作,具体如下图,4个备份节点都从主节点同步数据,其中1个为隐藏节点

slaveDelay

延迟同步即延迟从主节点同步数据,比如延迟时间配置的1小时,现在时间是 09:52,那么延迟节点中只同步到主节点 08:52 之前的数据。另外需要注意延迟节点必须是隐藏节点,且Priority为0。

那这个延迟节点有什么用呢?有过数据库误操作惨痛经历的开发者肯定知道答案,那就是为了防止数据库误操作,比如更新服务前,一般会先执行数据库更新脚本,如果脚本有问题,且操作前未做备份,那数据可能就找不回了。但如果说配置了延迟节点,那误操作完,还有该节点可以兜底,只能说该功能真是贴心。具体延迟节点如下图所展示:

tags

支持对副本集成员打标签,在查询数据时会用到,比如找到对应标签的副本节点,然后从该节点读取数据,这点也非常有用,可以根据标签对节点分类,查询数据时不同服务的客户端指定其对应的标签的节点,对某个标签的节点数量进行增加或减少,也不怕会影响到使用其他标签的服务

votes

表示节点是否有权限参与选举,最大可以配置7个副本节点参与选举

副本集状态

NumberNameState Description
0STARTUPNot yet an active member of any set. All members start up in this state. The mongod parses the replica set configuration document while in STARTUP.
1PRIMARYThe member in state primary is the only member that can accept write operations. Eligible to vote.
2SECONDARYA member in state secondary is replicating the data store. Eligible to vote.
3RECOVERINGMembers either perform startup self-checks, or transition from completing a rollback or resync. Data is not available for reads from this member. Eligible to vote.
5STARTUP2The member has joined the set and is running an initial sync. Eligible to vote.NOTEStarting in MongoDB 5.0, if the member was newly added to the replica set, it is not eligible to vote and cannot be elected during the initial sync process.
6UNKNOWNThe member's state, as seen from another member of the set, is not yet known.
7ARBITERArbiters do not replicate data and exist solely to participate in elections. Eligible to vote.
8DOWNThe member, as seen from another member of the set, is unreachable.
9ROLLBACKThis member is actively performing a rollback. Eligible to vote. Data is not available for reads from this member.Starting in version 4.2, MongoDB kills all in-progress user operations when a member enters the ROLLBACK state.
10REMOVEDThis member was once in a replica set but was subsequently removed

3.副本集写跟读特性

副本集写关注是指写入一条数据,主节点处理完成后,需要其他承载数据的副本节点也确认写成功后,才能给客户端返回写入数据成功。

这个功能主要是解决主节点挂掉后,数据还未来得及同步到副本节点,而导致数据丢失的问题。

可以配置节点个数,默认配置 {“w”:1},这样表示主节点写入数据成功即可给客户端返回成功,“w” 配置为2,则表示除了主节点,还需要收到其中一个副本节点返回写入成功,“w” 还可以配置为 "majority",表示需要集群中大多数承载数据且有选举权限的节点返回写入成功。

如下图所示,P-S-S 结构(一个 primary 节点,两个 secondary 节点),写请求里面带了w : “majority" ,那么主节点写入完成后,数据同步到第一个副本节点,且第一个副本节点回复数据写入成功后,才给客户端返回成功

  • 1.在写请求中指定 writeConcern 相关参数
db.products.insert(
    { item: "envelopes", qty : 100, type: "Clasp" },
    { writeConcern: { w: "majority" , wtimeout: 5000 } }
)
db.products.insert(
    { item: "envelopes", qty : 100, type: "Clasp" },
    { writeConcern: { w: "majority" , wtimeout: 5000 } }
)
  • 2.修改副本集 getLastErrorDefaults 配置
cfg = rs.conf()
cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 }
rs.reconfig(cfg)
cfg = rs.conf()
cfg.settings.getLastErrorDefaults = { w: "majority", wtimeout: 5000 }
rs.reconfig(cfg)

读跟写不一样,为了保持一致性,写只能通过主节点,但读可以选择主节点,也可以选择副本节点,区别是主节点数据最新,副本节点因为同步问题可能会有延迟,但从副本节点读取数据可以分散对主节点的压力

因为承载数据的节点会有多个,那客户端如何选择从那个节点读呢?

主要有3个条件(Tag Sets、 maxStalenessSeconds、Hedged Read),5种模式(primary、primaryPreferred、secondary、secondaryPreferred、nearest)

5种模式

模式特点
primary所有读请求都从主节点读取
primaryPreferred主节点正常,则所有读请求都从主节点读取,如果主节点挂掉,则从符合条件的副本节点读取
secondary所有读请求都从副本节点读取
secondaryPreferred所有读请求都从副本节点读取,但如果副本节点都挂掉了,那就从主节点读取
nearest主要看网络延迟,选取延迟最小的节点,主节点跟副本节点均可

3个条件

条件是在符合模式的基础上,再根据条件删选具体的节点

  1. Tag Sets(标签)

    顾名思义,这个可以给节点加上标签,然后查找数据时,可以根据标签选择对应的节点,然后在该节点查找数据。可以通过mongo shell 使用 rs.conf() 查看当前每个节点下面的 tags, 修改或者添加tags 过程同上面修改 getLastErrorDefaults 配置 ,如:cfg.members[n].tags = { "region": "South", "datacenter": "A" }

  2. maxStalenessSeconds (可容忍的最大同步延迟)

    顾名思义+1,这个值是指副本节点同步主节点写入的时间 跟 主节点实际最近写入时间的对比值,如果主节点挂掉了,那就跟副本集中最新写入的时间做对比。

    这个值建议设置,避免因为部分副本节点网络原因导致比较长时间未同步主节点数据,然后读到比较老的数据。特别注意的是该值需要设置 90s 以上,因为客户端是定时去校验副本节点的同步延迟时间,数据不会特别准确,设置比 90s 小,会抛出异常。

  3. Hedged Read (对冲读取)

    该选项是在分片集群 MongoDB 4.4 版本后才支持,指 mongos 实例路由读取请求时会同时发给两个符合条件的副本集节点,然后那个先返回结果就返回这个结果给客户端

查询请求中如何

代码中连接数据库,使用 connection string uri 时,可以加上下面的这三个参数

参数说明
readPreference模式,枚举值有:primary(默认值)、 primaryPreferred、secondary、secondaryPreferred、nearest
maxStalenessSeconds最大同步延时秒数,取值0 - 90 会报错, -1 表示没有最大值
readPreferenceTags标签,如果标签是 { "dc": "ny", "rack": "r1" }, 则在uri 为 readPreferenceTags=dc:ny,rack:r1

例如下面:

mongodb://db0.example.com,db1.example.com,db2.example.com/?replicaSet=myRepl&readPreference=secondary&maxStalenessSeconds=120&readPreferenceTags=dc:ny,rack:r1
mongodb://db0.example.com,db1.example.com,db2.example.com/?replicaSet=myRepl&readPreference=secondary&maxStalenessSeconds=120&readPreferenceTags=dc:ny,rack:r1

在mogo shell 中,可以使用 cursor.readPref() 或者 Mongo.setReadPref()

cursor.readPref() 参数分别为: mode、tag set、hedge options, 具体请求例如下面这样

db.collection.find({ }).readPref(
    "secondary",                      // mode
    [ { "datacenter": "B" },  { } ],  // tag set
    { enabled: true }                 // hedge options
)
db.collection.find({ }).readPref(
    "secondary",                      // mode
    [ { "datacenter": "B" },  { } ],  // tag set
    { enabled: true }                 // hedge options
)

Mongo.setReadPref() 类似,只是预先设置请求条件,这样就不用每个请求后面带上 readPref 条件

案例

  1. 登录主节点: mongo localhost:27018

  2. 插入一条数据: db.nums.insert({name: “num0”})

    在当前节点查询: db.nums.find()

    可以看到本条数据: { "_id" : ObjectId("5f958687233b11771912ced5"), "name" : "num0" }

  3. 登录副本节点: mongo localhost:27019

    查询:db.nums.find()

    因为查询模式默认为 primary,所以在副本节点查询会报错,如下:

    Error: error: {
     "operationTime" : Timestamp(1603788383, 1),
     "ok" : 0,
     "errmsg" : "not master and slaveOk=false",
     "code" : 13435,
     "codeName" : "NotMasterNoSlaveOk",
     "$clusterTime" : {
         "clusterTime" : Timestamp(1603788383, 1),
         "signature" : {
             "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
             "keyId" : NumberLong(0)
         }
     }
    }
    Error: error: {
     "operationTime" : Timestamp(1603788383, 1),
     "ok" : 0,
     "errmsg" : "not master and slaveOk=false",
     "code" : 13435,
     "codeName" : "NotMasterNoSlaveOk",
     "$clusterTime" : {
         "clusterTime" : Timestamp(1603788383, 1),
         "signature" : {
             "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
             "keyId" : NumberLong(0)
         }
     }
    }

    查询时指定模式为 “secondary”: db.nums.find().readPref(“secondary")

    就可以查询到插入的数据:{ "_id" : ObjectId("5f958687233b11771912ced5"), "name" : "num0" }

4.安装

官方文档:https://docs.mongodb.com/manual/tutorial/deploy-replica-set/

3.6开始支持副本

配置文件

三台机器一样的配置,一主2从

cat >> /etc/mongod.conf <<EOF
systemLog:
    quiet: false
    path: "/data/mongodb_data/log/mongod.log"
    logRotate: rename
    logAppend: true   
    destination: file
    timeStampFormat: ctime
    #timeStampFormat: iso8601-local
processManagement:
    fork: true
    pidFilePath: "/data/mongodb_data/mongod.pid"
storage:
    dbPath: "/data/mongodb_data/data"
    indexBuildRetry: true
    journal:
        enabled: true
        commitIntervalMs: 100
    directoryPerDB: true
    syncPeriodSecs: 60
    engine: wiredTiger
    wiredTiger:
        engineConfig:
            cacheSizeGB: 12
            statisticsLogDelaySecs: 0
            journalCompressor: snappy
            directoryForIndexes: true
        collectionConfig:
            blockCompressor: snappy
        indexConfig:
            prefixCompression: true
net:  
    bindIp: 192.168.1.10,127.0.0.1
    port: 47017
    maxIncomingConnections: 52528
    wireObjectCheck: true
    ipv6: false
    unixDomainSocket:
        enabled: false
operationProfiling:
    slowOpThresholdMs: 200
    mode: slowOp
security:
    authorization: disabled
    #clusterAuthMode: keyFile
    #keyFile: "/home/mongod/keyfile"
    javascriptEnabled: true
setParameter:
    enableLocalhostAuthBypass: true
    authenticationMechanisms: SCRAM-SHA-1
replication:
    oplogSizeMB: 5120
    replSetName: configRS
    secondaryIndexPrefetch: all
#sharding:
    #clusterRole: configsvr
    #archiveMovedChunks: true
EOF
cat >> /etc/mongod.conf <<EOF
systemLog:
    quiet: false
    path: "/data/mongodb_data/log/mongod.log"
    logRotate: rename
    logAppend: true   
    destination: file
    timeStampFormat: ctime
    #timeStampFormat: iso8601-local
processManagement:
    fork: true
    pidFilePath: "/data/mongodb_data/mongod.pid"
storage:
    dbPath: "/data/mongodb_data/data"
    indexBuildRetry: true
    journal:
        enabled: true
        commitIntervalMs: 100
    directoryPerDB: true
    syncPeriodSecs: 60
    engine: wiredTiger
    wiredTiger:
        engineConfig:
            cacheSizeGB: 12
            statisticsLogDelaySecs: 0
            journalCompressor: snappy
            directoryForIndexes: true
        collectionConfig:
            blockCompressor: snappy
        indexConfig:
            prefixCompression: true
net:  
    bindIp: 192.168.1.10,127.0.0.1
    port: 47017
    maxIncomingConnections: 52528
    wireObjectCheck: true
    ipv6: false
    unixDomainSocket:
        enabled: false
operationProfiling:
    slowOpThresholdMs: 200
    mode: slowOp
security:
    authorization: disabled
    #clusterAuthMode: keyFile
    #keyFile: "/home/mongod/keyfile"
    javascriptEnabled: true
setParameter:
    enableLocalhostAuthBypass: true
    authenticationMechanisms: SCRAM-SHA-1
replication:
    oplogSizeMB: 5120
    replSetName: configRS
    secondaryIndexPrefetch: all
#sharding:
    #clusterRole: configsvr
    #archiveMovedChunks: true
EOF

重启三台mongodb服务

副本集初始化

  • 登陆任意一台mongo,执行如下操作
rs.initiate( {
   _id : "configRS",
   members: [
      { _id: 0, host: "192.168.122.245:47017" },
      { _id: 1, host: "192.168.122.246:47017" },
      { _id: 2, host: "192.168.122.14:47017" }
   ]
})
rs.initiate( {
   _id : "configRS",
   members: [
      { _id: 0, host: "192.168.122.245:47017" },
      { _id: 1, host: "192.168.122.246:47017" },
      { _id: 2, host: "192.168.122.14:47017" }
   ]
})
  • master
configRS:PRIMARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
configRS:PRIMARY>
configRS:PRIMARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
configRS:PRIMARY>
  • slave
configRS:SECONDARY> rs.slaveOk()

#或者

configRS:SECONDARY> rs.secondaryOk()
configRS:SECONDARY> rs.slaveOk()

#或者

configRS:SECONDARY> rs.secondaryOk()

测试效果

  • master
configRS:PRIMARY> use test;
switched to db test
configRS:PRIMARY>  db.user1.insertMany([{ id:3,name:"peter",age:14 },{ id:4,name:"tom",age:13 },{ id:5,name:"ben",age:24 }])

#或着

configRS:PRIMARY> for(var i=0;i<10000;i++){db.customer.insert({"name":"user"+i})}
WriteResult({ "nInserted" : 1 })

configRS:PRIMARY> db.customer.count()
10000
configRS:PRIMARY> use test;
switched to db test
configRS:PRIMARY>  db.user1.insertMany([{ id:3,name:"peter",age:14 },{ id:4,name:"tom",age:13 },{ id:5,name:"ben",age:24 }])

#或着

configRS:PRIMARY> for(var i=0;i<10000;i++){db.customer.insert({"name":"user"+i})}
WriteResult({ "nInserted" : 1 })

configRS:PRIMARY> db.customer.count()
10000
  • slave
#查看
configRS:SECONDARY> show dbs;
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB
#查看
configRS:SECONDARY> show dbs;
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

故障转移测试

把其中任意节点关闭

mongod --shutdown --dbpath /data/mongodb_data/data

#或者

configRS:SECONDARY>use admin
configRS:SECONDARY> db.shutdownServer()
mongod --shutdown --dbpath /data/mongodb_data/data

#或者

configRS:SECONDARY>use admin
configRS:SECONDARY> db.shutdownServer()

查看属性

configRS:PRIMARY> rs.conf()

{
	"_id" : "configRS",
	"version" : 1,
	"term" : 1,
	"members" : [
		{
			"_id" : 0,
			"host" : "192.168.122.245:47017",
			"arbiterOnly" : false,
			"buildIndexes" : true,
			"hidden" : false,
			"priority" : 1,
			"tags" : {
				
			},
			"secondaryDelaySecs" : NumberLong(0),
			"votes" : 1
		},
		{
			"_id" : 1,
			"host" : "192.168.122.246:47017",
			"arbiterOnly" : false,
			"buildIndexes" : true,
			"hidden" : false,
			"priority" : 1,
			"tags" : {
				
			},
			"secondaryDelaySecs" : NumberLong(0),
			"votes" : 1
		},
		{
			"_id" : 2,
			"host" : "192.168.122.14:47017",
			"arbiterOnly" : false,
			"buildIndexes" : true,
			"hidden" : false,
			"priority" : 1,
			"tags" : {
				
			},
			"secondaryDelaySecs" : NumberLong(0),
			"votes" : 1
		}
	],
	"protocolVersion" : NumberLong(1),
	"writeConcernMajorityJournalDefault" : true,
	"settings" : {
		"chainingAllowed" : true,
		"heartbeatIntervalMillis" : 2000,
		"heartbeatTimeoutSecs" : 10,
		"electionTimeoutMillis" : 10000,
		"catchUpTimeoutMillis" : -1,
		"catchUpTakeoverDelayMillis" : 30000,
		"getLastErrorModes" : {
			
		},
		"getLastErrorDefaults" : {
			"w" : 1,
			"wtimeout" : 0
		},
		"replicaSetId" : ObjectId("61d3d523406b36cb0b6f7710")
	}
}
configRS:PRIMARY> rs.conf()

{
	"_id" : "configRS",
	"version" : 1,
	"term" : 1,
	"members" : [
		{
			"_id" : 0,
			"host" : "192.168.122.245:47017",
			"arbiterOnly" : false,
			"buildIndexes" : true,
			"hidden" : false,
			"priority" : 1,
			"tags" : {
				
			},
			"secondaryDelaySecs" : NumberLong(0),
			"votes" : 1
		},
		{
			"_id" : 1,
			"host" : "192.168.122.246:47017",
			"arbiterOnly" : false,
			"buildIndexes" : true,
			"hidden" : false,
			"priority" : 1,
			"tags" : {
				
			},
			"secondaryDelaySecs" : NumberLong(0),
			"votes" : 1
		},
		{
			"_id" : 2,
			"host" : "192.168.122.14:47017",
			"arbiterOnly" : false,
			"buildIndexes" : true,
			"hidden" : false,
			"priority" : 1,
			"tags" : {
				
			},
			"secondaryDelaySecs" : NumberLong(0),
			"votes" : 1
		}
	],
	"protocolVersion" : NumberLong(1),
	"writeConcernMajorityJournalDefault" : true,
	"settings" : {
		"chainingAllowed" : true,
		"heartbeatIntervalMillis" : 2000,
		"heartbeatTimeoutSecs" : 10,
		"electionTimeoutMillis" : 10000,
		"catchUpTimeoutMillis" : -1,
		"catchUpTakeoverDelayMillis" : 30000,
		"getLastErrorModes" : {
			
		},
		"getLastErrorDefaults" : {
			"w" : 1,
			"wtimeout" : 0
		},
		"replicaSetId" : ObjectId("61d3d523406b36cb0b6f7710")
	}
}

查看master属性

configRS:PRIMARY> db.isMaster()
{
	"topologyVersion" : {
		"processId" : ObjectId("61d3c63b406b36cb0b6f7626"),
		"counter" : NumberLong(6)
	},
	"hosts" : [
		"192.168.122.245:47017",
		"192.168.122.246:47017",
		"192.168.122.14:47017"
	],
	"setName" : "configRS",
	"setVersion" : 1,
	"ismaster" : true,
	"secondary" : false,
	"primary" : "192.168.122.245:47017",
	"me" : "192.168.122.245:47017",
	"electionId" : ObjectId("7fffffff0000000000000001"),
	"lastWrite" : {
		"opTime" : {
			"ts" : Timestamp(1641273262, 1),
			"t" : NumberLong(1)
		},
		"lastWriteDate" : ISODate("2022-01-04T05:14:22Z"),
		"majorityOpTime" : {
			"ts" : Timestamp(1641273262, 1),
			"t" : NumberLong(1)
		},
		"majorityWriteDate" : ISODate("2022-01-04T05:14:22Z")
	},
	"maxBsonObjectSize" : 16777216,
	"maxMessageSizeBytes" : 48000000,
	"maxWriteBatchSize" : 100000,
	"localTime" : ISODate("2022-01-04T05:14:28.349Z"),
	"logicalSessionTimeoutMinutes" : 30,
	"connectionId" : 1,
	"minWireVersion" : 0,
	"maxWireVersion" : 13,
	"readOnly" : false,
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(1641273262, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1641273262, 1)
}
configRS:PRIMARY> db.isMaster()
{
	"topologyVersion" : {
		"processId" : ObjectId("61d3c63b406b36cb0b6f7626"),
		"counter" : NumberLong(6)
	},
	"hosts" : [
		"192.168.122.245:47017",
		"192.168.122.246:47017",
		"192.168.122.14:47017"
	],
	"setName" : "configRS",
	"setVersion" : 1,
	"ismaster" : true,
	"secondary" : false,
	"primary" : "192.168.122.245:47017",
	"me" : "192.168.122.245:47017",
	"electionId" : ObjectId("7fffffff0000000000000001"),
	"lastWrite" : {
		"opTime" : {
			"ts" : Timestamp(1641273262, 1),
			"t" : NumberLong(1)
		},
		"lastWriteDate" : ISODate("2022-01-04T05:14:22Z"),
		"majorityOpTime" : {
			"ts" : Timestamp(1641273262, 1),
			"t" : NumberLong(1)
		},
		"majorityWriteDate" : ISODate("2022-01-04T05:14:22Z")
	},
	"maxBsonObjectSize" : 16777216,
	"maxMessageSizeBytes" : 48000000,
	"maxWriteBatchSize" : 100000,
	"localTime" : ISODate("2022-01-04T05:14:28.349Z"),
	"logicalSessionTimeoutMinutes" : 30,
	"connectionId" : 1,
	"minWireVersion" : 0,
	"maxWireVersion" : 13,
	"readOnly" : false,
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(1641273262, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1641273262, 1)
}
  • 查看副本集状态
configRS:PRIMARY> rs.status()
configRS:PRIMARY> rs.status()

查看当前副本状态

configRS:PRIMARY>rs.status().members.forEach( 
    function(z){ 
            printjson(z.name);
            printjson(z.stateStr);
    } 
)



"192.168.122.245:47017"
"SECONDARY"
"192.168.122.246:47017"
"PRIMARY"
"192.168.122.14:47017"
"SECONDARY"
configRS:PRIMARY>


#其中一个slave掉线
"192.168.122.245:47017"
"SECONDARY"
"192.168.122.246:47017"
"PRIMARY"
"192.168.122.14:47017"
"(not reachable/healthy)"
configRS:PRIMARY>rs.status().members.forEach( 
    function(z){ 
            printjson(z.name);
            printjson(z.stateStr);
    } 
)



"192.168.122.245:47017"
"SECONDARY"
"192.168.122.246:47017"
"PRIMARY"
"192.168.122.14:47017"
"SECONDARY"
configRS:PRIMARY>


#其中一个slave掉线
"192.168.122.245:47017"
"SECONDARY"
"192.168.122.246:47017"
"PRIMARY"
"192.168.122.14:47017"
"(not reachable/healthy)"

副本集

删除

两种方式,使用rs.remove()或rs.reconfig()

rs.remove

configRS:PRIMARY> rs.remove("192.168.122.14:47017")
{
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(1641273945, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1641273945, 1)
}

#查看副本状态
configRS:PRIMARY> rs.status().members.forEach(     function(z){              printjson(z.name);             printjson(z.stateStr);     }  )

"192.168.122.245:47017"
"SECONDARY"
"192.168.122.246:47017"
"PRIMARY"
configRS:PRIMARY> rs.remove("192.168.122.14:47017")
{
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(1641273945, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1641273945, 1)
}

#查看副本状态
configRS:PRIMARY> rs.status().members.forEach(     function(z){              printjson(z.name);             printjson(z.stateStr);     }  )

"192.168.122.245:47017"
"SECONDARY"
"192.168.122.246:47017"
"PRIMARY"

rs.conf

configRS:PRIMARY> cfg = rs.conf()

configRS:PRIMARY> cfg.members.splice(2,1)

configRS:PRIMARY> rs.reconfig(cfg)
configRS:PRIMARY> cfg = rs.conf()

configRS:PRIMARY> cfg.members.splice(2,1)

configRS:PRIMARY> rs.reconfig(cfg)

添加副本集

mongodb副本集的扩展非常好,往副本集里添加实例和移除实例都非常方便

往mongodb副本集添加实例数据能够自动同步,无需人工干预

#清空原来数据,启动mongodb

#rs.add的优先权重默认为1

configRS:PRIMARY>use admin

configRS:PRIMARY> rs.add( { host: "192.168.122.14:47017", priority: 0, votes: 0 } )
{
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(1641274239, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1641274239, 1)
}

#查看
configRS:PRIMARY> rs.status().members.forEach(     function(z){              printjson(z.name);             printjson(z.stateStr);     }  )
#清空原来数据,启动mongodb

#rs.add的优先权重默认为1

configRS:PRIMARY>use admin

configRS:PRIMARY> rs.add( { host: "192.168.122.14:47017", priority: 0, votes: 0 } )
{
	"ok" : 1,
	"$clusterTime" : {
		"clusterTime" : Timestamp(1641274239, 1),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	},
	"operationTime" : Timestamp(1641274239, 1)
}

#查看
configRS:PRIMARY> rs.status().members.forEach(     function(z){              printjson(z.name);             printjson(z.stateStr);     }  )

如果数据量大,同步时间过长,可以考虑拷贝文件的 resync 方式来进行全量同步(initial sync)

重新配置优先级

使用rs.conf()查看新成员id,查看每个实列的位置,则将其priority和votes更新为 1

configRS:PRIMARY> conf = rs.conf()
configRS:PRIMARY> conf.members[1].priority = 11
configRS:PRIMARY> conf.members[2].priority = 11
configRS:PRIMARY> rs.reconfig(conf)
使用rs.conf()查看新成员id,查看每个实列的位置,则将其priority和votes更新为 1

configRS:PRIMARY> conf = rs.conf()
configRS:PRIMARY> conf.members[1].priority = 11
configRS:PRIMARY> conf.members[2].priority = 11
configRS:PRIMARY> rs.reconfig(conf)

添加仲裁

configRS:PRIMARY>rs.addArb("172.16.250.240:27017")
configRS:PRIMARY>rs.addArb("172.16.250.240:27017")

添加备份节点

configRS:PRIMARY>rs.add({"_id":3,"host":"172.16.250.240:27017","priority":0,"hidden":true})
configRS:PRIMARY>rs.add({"_id":3,"host":"172.16.250.240:27017","priority":0,"hidden":true})

节点成为Primary

#强制让一个节点成为Primary

cfg = rs.conf()
cfg.members[0].priority = 5
cfg.members[1].priority = 1
cfg.members[2].priority = 1
rs.reconfig(cfg)
#强制让一个节点成为Primary

cfg = rs.conf()
cfg.members[0].priority = 5
cfg.members[1].priority = 1
cfg.members[2].priority = 1
rs.reconfig(cfg)

副本集监控

# 查看副本集的配置信息
configRS:PRIMARY> rs.conf()

# 查看副本集运行状态
configRS:PRIMARY> rs.status()

health:为1表示健康

#查看备份节点的复制信息
configRS:PRIMARY> db.printSlaveReplicationInfo()
# 查看副本集的配置信息
configRS:PRIMARY> rs.conf()

# 查看副本集运行状态
configRS:PRIMARY> rs.status()

health:为1表示健康

#查看备份节点的复制信息
configRS:PRIMARY> db.printSlaveReplicationInfo()

参数信息

副本集参数说明

参数说明
_id集群中节点编号
name成员服务器名称及端口
health表示成员中的健康状态(0:down;1:up)
state为0~10,表示成员的当前状态
stateStr描述该成员是主库(PRIMARY)还是备库(SECONDARY)
uptime该成员在线时间(秒)
optime成员最后一次应用日志(oplog)的信息
optimeDate成员最后一次应用日志(oplog)的时间
electionTime当前primary从操作日志中选举信息
configVersionmongodb版本
self为true 表示当前节点

查看从库状态

# -- 获取当前集群内所有从库信息与复制延迟状态
dbawsp:PRIMARY> rs.printSlaveReplicationInfo()

# -- 查询当前节点是否为 Master 角色
dbawsp:PRIMARY> rs.isMaster()
# -- 获取当前集群内所有从库信息与复制延迟状态
dbawsp:PRIMARY> rs.printSlaveReplicationInfo()

# -- 查询当前节点是否为 Master 角色
dbawsp:PRIMARY> rs.isMaster()

tag标签

  • **注意:**Arbiter 节点( 仲裁节点 ) 无法设置标签

开启安全认证

创建用户

#登录 PRIMARY节点创建用户
use admin

db.createUser(
  {
    user: "sysadmin",
    pwd: passwordPrompt(),
    roles: [
    'clusterAdmin',
    'dbAdminAnyDatabase',
    'userAdminAnyDatabase',
    'readWriteAnyDatabase'
  ]
  }
)

#或者,超级管理者
db.createUser({ user: "admin", pwd: "123456", roles: [{ role: "root", db: "admin" }] })


configRS:PRIMARY>db.createUser({user:"admin",pwd:"123456",roles:[{role:"userAdminAnyDatabase",db:"admin"}]})

#对test开启安全认证
configRS:PRIMARY>db.createUser({user:"admin",pwd:"123456",roles:[{role:"readWrite",db:"test"}]})
#登录 PRIMARY节点创建用户
use admin

db.createUser(
  {
    user: "sysadmin",
    pwd: passwordPrompt(),
    roles: [
    'clusterAdmin',
    'dbAdminAnyDatabase',
    'userAdminAnyDatabase',
    'readWriteAnyDatabase'
  ]
  }
)

#或者,超级管理者
db.createUser({ user: "admin", pwd: "123456", roles: [{ role: "root", db: "admin" }] })


configRS:PRIMARY>db.createUser({user:"admin",pwd:"123456",roles:[{role:"userAdminAnyDatabase",db:"admin"}]})

#对test开启安全认证
configRS:PRIMARY>db.createUser({user:"admin",pwd:"123456",roles:[{role:"readWrite",db:"test"}]})

创建keyFile文件

  • 停止mogodb数据库

先停掉所有SECONDARY节点的MongoDB服务,然后再停掉PRIMARY节点的MongoDB服务,并在PRIMARY节点所在服务器上创建keyFile文件,之后把keyfile文件cp 到其余机器上面

openssl rand -base64 745 > /data/mongodb_data/keyfile
chmod 600 /data/mongodb_data/keyfile

scp keyfile 192.168.122.245:/data/mongodb_data/
scp keyfile 192.168.122.246:/data/mongodb_data/
openssl rand -base64 745 > /data/mongodb_data/keyfile
chmod 600 /data/mongodb_data/keyfile

scp keyfile 192.168.122.245:/data/mongodb_data/
scp keyfile 192.168.122.246:/data/mongodb_data/
#三台配置都添加如下
security:
    authorization: enabled
    clusterAuthMode: keyFile
    keyFile: "/data/mongodb_data/keyfile"
    javascriptEnabled: true
#三台配置都添加如下
security:
    authorization: enabled
    clusterAuthMode: keyFile
    keyFile: "/data/mongodb_data/keyfile"
    javascriptEnabled: true
  • 测试
#登陆任意一台机器

mongo --host 127.0.0.1 --port 47017

configRS:SECONDARY> db.auth("admin","123456")
1

configRS:SECONDARY> show dbs;
test  0.000GB

#或者 登陆时直接认证
mongo -uadmin -p123456 127.0.0.1:47017

configRS:PRIMARY> use admin
switched to db admin
configRS:PRIMARY> db.auth("admin","123456")
1
configRS:PRIMARY> show dbs;
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

#或者
mongo -u "root" --authenticationDatabase "admin" -p'password'
#登陆任意一台机器

mongo --host 127.0.0.1 --port 47017

configRS:SECONDARY> db.auth("admin","123456")
1

configRS:SECONDARY> show dbs;
test  0.000GB

#或者 登陆时直接认证
mongo -uadmin -p123456 127.0.0.1:47017

configRS:PRIMARY> use admin
switched to db admin
configRS:PRIMARY> db.auth("admin","123456")
1
configRS:PRIMARY> show dbs;
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

#或者
mongo -u "root" --authenticationDatabase "admin" -p'password'

连接副本集

[root@slave01 ~]# mongo "192.168.122.245:47017,192.168.122.246:47017,192.168.122.14:47017/test?replicaSet=configRS"

MongoDB shell version v5.0.5
connecting to: mongodb://192.168.122.245:47017,192.168.122.246:47017,192.168.122.14:47017/test%3FreplicaSet%3DconfigRS?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("8773dc9f-6373-49a3-8869-07b98064875e") }
MongoDB server version: 5.0.5
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
configRS:PRIMARY>
[root@slave01 ~]# mongo "192.168.122.245:47017,192.168.122.246:47017,192.168.122.14:47017/test?replicaSet=configRS"

MongoDB shell version v5.0.5
connecting to: mongodb://192.168.122.245:47017,192.168.122.246:47017,192.168.122.14:47017/test%3FreplicaSet%3DconfigRS?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("8773dc9f-6373-49a3-8869-07b98064875e") }
MongoDB server version: 5.0.5
================
Warning: the "mongo" shell has been superseded by "mongosh",
which delivers improved usability and compatibility.The "mongo" shell has been deprecated and will be removed in
an upcoming release.
For installation instructions, see
https://docs.mongodb.com/mongodb-shell/install/
================
configRS:PRIMARY>

rs.slaveOk();

这个命令在客户端命令中执行,只能在当前连接中生效,关闭重新连接,需要再次输入

可以在启动时加入,保证不需要在从库每次都输入。一直开启从库读操作

在 .mongorc.js 加入上面的命令即可 此文件和.bash_profile 有相同的左右,应该是mongo的入口执行文件。

https://www.cnblogs.com/operationhome/p/10744712.html

http://blog.huati365.com/7669e0e31f5afa06