一.local系统库
1.1 local本地库介绍
- local库存储replication进程的信息和本地数据,记录着是时间戳,索引以及复制等信息
- local库里面的内容是不能复制的
rs1:PRIMARY> use local
switched to db local
rs1:PRIMARY> show tables
oplog.rs //记录所有操作,mongodb就是通过oplog.rs实现数据同步的
replset.election //副本集选择信息
replset.minvalid //保存数据库最新操作的时间戳
replset.oplogTruncateAfterPoint
startup_log //记录mongod每次的启动信息
system.indexs //记录当前库的所有索引信息
system.replset //记录主从配置信息,不能直接修改该集合,必须通过rs.initiate()来初始化,rs.reconfig()来重新配置
1.2 关于oplogTruncateAfterPoint
- 4.2及以前版本中,oplogTruncateAfterPoint只用于备库,用来保证oplog batch应用的原子性,最终目的是确保主备数据的一致性。
- 4.4中,主库也会维护oplogTruncateAfterPoint,主要是和oplog hole相关
- oplog hole是因为事务在WiredTiger层面的提交顺序和oplog顺序不一致导致,复制时如果要保证主备一致性需要避开这个hole
rs1:PRIMARY> db.replset.oplogTruncateAfterPoint.find()
{ "_id" : "oplogTruncateAfterPoint", "oplogTruncateAfterPoint" : Timestamp(0, 0) }
1.3 关于system.replset
rs1:PRIMARY> db.system.replset.find()
{ "_id" : "rs1", "version" : 1, "protocolVersion" : NumberLong(1), "writeConcernMajorityJournalDefault" : true, "members" : [ { "_id" : 0, "host" : "172.16.0.103:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "172.16.0.104:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "172.16.0.105: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("61efafe1f9b107af5deb6af5") } }
- arbiterOnly:表示该成员为仲裁者
- buildIndexes:表示实在在成员上创建索引,该属性不能修改,只能在增加成员时设置属性
- hidden:将成员配置为隐藏成员,priority需要设置为0。client不会向隐藏成员发送请求,因此成员不会收到client的request
- priority:优先级,默认是1,取值范围0~100,为0的节点永远不能成为primary节点
- slaveDelay:将Secondary 成员配置为延迟备份节点,要求Priority 为0,表示该成员比Primary 成员滞后指定的时间,才能将Primary上进行的写操作同步到本地。
- votes:0或1,表示有权限参与选举
1.4 关于的oplog
- oplog:存储replica set的写操作,记录信息存储在local数据库中的oplog.rs的集合中。oplog.rs是一个固定长度的capped collection。
- oplog日志默认情况下是比较大的,可达到5%的磁盘空间,可通过参数--oplogSize来设置大小
- 修改oplogSize需要重启服务,而在为大型的oplog预分配空间非常消耗时间,且可能导致主节点停机时间增加,所以尽可能手动预分配数据文件。
- 修改oplogSize重启主节点之后,所有从节点需要使用–autoresync重启,否则需要手动重新同步
// 查看master的oplog元数据信息
db.printReplicationInfo()
configured oplog size: 990MB
log length start to end: 3073secs (0.85hrs)
oplog first event time: Tue Jan 25 2022 16:08:01 GMT+0800 (CST)
oplog last event time: Tue Jan 25 2022 16:59:14 GMT+0800 (CST)
now: Tue Jan 25 2022 16:59:23 GMT+0800 (CST)
printReplicationInfo字段含义说明:
- configured oplog size:配置的oplog文件大小
- log length start to end:oplog日志的启用时间短
- oplog first event time:第一个事务日志的产生时间
- oplog last event time:最后一个事务日志的产生时间
- now:现在的时间
// 查看oplog的信息,oplog.rs系统集合只用于复制,不能创建索引,查询语句会比较慢
db.oplog.rs.findOne()
{
"ts" : Timestamp(1643098081, 1),
"h" : NumberLong("-5182796776932811184"),
"v" : 2,
"op" : "n",
"ns" : "",
"wall" : ISODate("2022-01-25T08:08:01.364Z"),
"o" : {
"msg" : "initiating set"
}
}
// 查询1小时内的oplog
var now = Math.floor((new Date().getTime())/1000)
db.oplog.rs.find({"ts":{"$lt":Timestamp(now,1),"$gt":Timestamp(now-3600,1)}})
// 查询指定时间段(时间UTC)
var start_time = Math.floor(ISODate("2022-01-26T10:00:00.000Z").getTime()/1000)
var end_time = Math.floor(ISODate("2022-01-26T10:30:00.000Z").getTime()/1000)
db.oplog.rs.find({"ts":{"$lt":Timestamp(start_time,1),"$gt":Timestamp(end_time,1)}})
// 统计各个集合的insert操作
db.oplog.rs.aggregate([{$match:{"op":"i"}},{$group:{_id:"$ns",count:{$sum:1}}}])
{ "_id" : "test.blog", "count" : 1 }
{ "_id" : "admin.system.keys", "count" : 2 }
{ "_id" : "config.system.sessions", "count" : 9 }
{ "_id" : "admin.system.version", "count" : 1 }
{ "_id" : "admin.system.users", "count" : 2 }
oplog记录字段含义:
- ts:某个操作的时间戳,在选举时,会选择max(ts)的那个secondary作为新的primary
- op:操作类型(i:insert,d:delete,u:update,c:db cmd,db:声明当前数据库,n:空操作)
- ns:操作所在的namespace
- o:当前操作的内容
- o2:在执行更新操作时的条件,仅限于update才有该属性
二.副本集primary和secondary基本信息查询
// 查看副本集状态
rs.status()
// 查看副本集配置信息
rs.conf()
// 查看是否是主节点
rs.isMaster()
// 查看slave的同步状态
db.printSlaveReplicationInfo()
// printSlaveReplicationInfo输出信息字段含义说明:
- source:从库的IP及端口
- syncedTo:目前的同步情况,延时了多久等信息
三、节点管理
3.1 开启从库客户模式才能读,secondary默认是不可读
rs1:SECONDARY> db.blog.find()
Error: error: {
"operationTime" : Timestamp(1643102404, 1),
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk",
"$clusterTime" : {
"clusterTime" : Timestamp(1643102404, 1),
"signature" : {
"hash" : BinData(0,"9PcQVaACwkxAYyp0Z+dd7YJBYY0="),
"keyId" : NumberLong("7057052573554966530")
}
}
}
// 在从库上执行,开启从库可读模式,默认secondary是不可读的
rs1:SECONDARY>db.getMongo().setSlaveOk()
rs1:SECONDARY> db.blog.find()
{ "_id" : ObjectId("61efbd0c91b49e592d29811c"), "title" : "My First Post", "content" : "hello word!", "date" : ISODate("2022-01-25T09:03:48.287Z") }
3.2 添加节点
增加方式:
- 方法1:通过oplog来增加节点
- 方法2:通过数据库快照(--fastsync)和oplog来增加节点
方法1优缺点:
- oplog直接进行增加节点操作,简单且无需人工过多干预,但是由于oplog是capped collection,采用的是循环方式进行日志处理。所以采用oplog方式进行增加节点,有可能导致数据的不一致。
方法1添加步骤:
1)配置并启动新节点
## 创建新节点数据目录
mkdir -p /data/mongodb/data2
## 配置新节点配置文件
vi mongodb2.conf
dbpath=/data/mongodb/data2/
logpath=/data/mongodb/logs/mongodb2.log
bind_ip=172.16.0.103
port=28017
fork=true
journal=true
logappend=true
replSet=rs1
keyFile=/data/mongodb/key/mongodb-keyfile
auth=true
## 启动新节点
/usr/local/mongodb4/bin/mongod -f /data/mongodb/config/mongodb2.conf --fork
## 开启防火墙
firewall-cmd --zone=public --add-port=28017/tcp --permanent
firewall-cmd --reload
2)将新节点加入副本集(主节点上执行)
rs1:PRIMARY> rs.add("172.16.0.103:28017")
{
"ok" : 1,
"operationTime" : Timestamp(1643175863, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1643175863, 1),
"signature" : {
"hash" : BinData(0,"T3yN+Cjh0odz7XqNvGysBkrdFRE="),
"keyId" : NumberLong("7057052573554966530")
}
}
}
3)查看进行初始化和数据同步信息
rs.status()
4)同步完成后,验证数据信息
// secondary上执行
rs.slaveOk()
use test
db.blog.findOne()
{
"_id" : ObjectId("61efbd0c91b49e592d29811c"),
"title" : "My First Post",
"content" : "hello word!",
"date" : ISODate("2022-01-25T09:03:48.287Z")
}
方法2添加节点
- 可以避免数据不一致
方法2添加节点步骤
1)拷贝某成员的物理文件作为初始化数据
cp -r /data/mongodb/data/ /data/mongodb/data2
2)配置节点信息
## 拷贝某节点的配置文件
cp /data/mongodb/config/mongodb.conf /data/mongodb/config/mongodb2.conf
## 修改对应的路径和端口等信息
vi mongodb2.conf
dbpath=/data/mongodb/data2/
logpath=/data/mongodb/logs/mongodb2.log
bind_ip=172.16.0.105
port=28017
fork=true
journal=true
auth=true
replSet=rs1
logappend=true
keyFile=/data/mongodb/key/mongodb-keyfile
2)在主节点上,插入一条测试数据进行标记,用于最后验证次更新是否正常同步
rs1:PRIMARY> use test
switched to db test
rs1:PRIMARY> db.test.insert({info:"test"})
WriteResult({ "nInserted" : 1 })
3)启动新节点服务
## 开通端口防火墙
firewall-cmd --zone=public --add-port=28017/tcp --permanent
firewall-cmd --reload
## 启动服务
/usr/local/mongodb4/bin/mongod -f /data/mongodb/config/mongodb2.conf --fork
4)将新节点加入副本集
rs1:PRIMARY> rs.add("172.16.0.105:28017")
{
"ok" : 1,
"operationTime" : Timestamp(1643176797, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1643176797, 1),
"signature" : {
"hash" : BinData(0,"nzUgoOvbcftL+hQRh31kR6OhiS0="),
"keyId" : NumberLong("7057052573554966530")
}
}
}
// 查看副本集信息
rs.status()
5)检验数据是否正常同步
rs1:SECONDARY> use test
switched to db test
rs1:SECONDARY> rs.slaveOk()
rs1:SECONDARY> db.test.find()
{ "_id" : ObjectId("61f0e2ca408edb6607456e81"), "info" : "test" }
3.3 删除节点
1)直接remove
rs1:PRIMARY> rs.remove("172.16.0.103:28018")
{
"ok" : 1,
"operationTime" : Timestamp(1643175909, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1643175909, 1),
"signature" : {
"hash" : BinData(0,"dHhka82X5TSVD7juBbhHCqUgpxk="),
"keyId" : NumberLong("7057052573554966530")
}
}
}
2)通过数组处理对象方法
- 执行rs.reconfig()不一定会造成副本集的重新选举,添加force参数同样如此
cfg = rs.conf()
// splice(数组下表,删除个数)
// 从下标2的节点开始,删除1个节点配置
cfg.members.splice(2,1)
rs.reconfig(cfg)
3.4替换副本集成员
cfg = rs.conf()
cfg.members[5].host = "172.16.0.103"
rs.reconfig(cfg)