258_Mongodb_集合_副本集2
MongoDB副本集模式
通过实时数据同步提高数据的可用性, 包括写入策略(write concern) 和 读取策略(read preference)来提升数据的读写性能
1 副本集角色
- 主节点(primary)
- 副节点(secondary)
- 仲裁节点(Arbiter)
一个副本集, 至少包括一个主节点(primary), 与一个副节点(secondary),通过不同的节点与数量,可以配置不同效果的副本集
主节点与副节点
主节点与副节点均属于数据节点,保存完整的数据
- 主节点: 接收数据写入需求, 然后通过同步机制同步到所有的副节点, 节点通过”心跳”机制进行沟通, 确保所有节点正常运行
- 仲裁节点 当主宕机后, 参与投票,选出新主,本身不存储数据, 这样在资源有限的情况下 给了副本集模式很大弹性
副本集的基本架构: 1个主节点 + 1个副节点 + 一个冲裁节点 (奇数 3个具有投票权的节点 可进行有效的投票)
2 高可用(节点故障转移)
在副本集中实现高可用 通过以下重要机制
- Oplog 同步
- 心跳机制(Hearbeat)
- 选举策略(vote)
- 副本集回滚(RollBack)
Oplog 同步
主节点的oplog异步同步到从节点,副本集可以在一个或多个成员失败的情况下继续运行 从数据库成员将这些操作复制并应用本身。所有副本集成员都将心跳(ping)发送给所有其他成员,任何从成员都可以从任何其他成员应用操作日志,如果副本集只剩一个节点,该节点会变成从节点(不能写)操作日志中的每个操作都是幂等的,也就是说oplog操作会产生相同的结果,无论是一次还是多次应用于目标数据集
- Oplog(操作日志)是一个特殊的capped集合,保持所有修改存储在数据库中的数据的操作的记录,类似于MySQL的Binlog
- Oplog 记录操作记录, 是副本集成员特有集合,默认固定大小(5% 且不超过50G)可以通过oplogSizeMB 进行设置, 此参数仅在集群初始化前配置有效, 一旦创建完成无法进行修改
- Oplog 本质是一个Capped Collections(环形队列), 新操作进来,会写入下一个位置(偏移量 & 时间戳)
- Oplog 会被记录在数据节点 local库中 oplog.rs的集合中
Oplog 初始化
新加入副本节点如果落后primary太多, 会从副本节点进行完整的数据复制(整个数据文件&oplog, 新节点用全备进行创建,然后构建主从)
Oplog 同步
节点同步会对比其它节点状态, 选择数据比自己更完整的节点作为同步源进行同步(可指定:use admin; db.adminCommad({replSetSyncFrom: “hostname<:port>”}))
设置同步源后三种情况会恢复默认同步机制:
- 此节点实例重新启动
- 此节点与同步源节点之间断开连接
- 同步源节点数据落后其它副本节点超过30S
Oplog状态查看
rs.printReplicationInfo() 查看oplog的状态,输出信息包括oplog日志大小,操作日志记录的起始时间
configured oplog size: oplog文件大小
log length start to end: oplog日志的启用时间段
oplog first event time: 第一个事务日志的产生时间
oplog last event time: 最后一个事务日志的产生时间
now: 现在的时间
db.getReplicationInfo() 可以用来查看oplog的状态、大小、存储的时间范围
在primary/secondary上查看从库落后信息
rs.printSlaveReplicationInfo()
查看slave状态
通过"db.printSlaveReplicationInfo()"可以查看slave的同步状态
副本节点中执行db.printSlaveReplicationInfo()命令可以查看同步状态信息
source——从库的IP及端口
syncedTo——当前的同步情况,延迟了多久等信息
bertram:PRIMARY> use local;
switched to db local
bertram:PRIMARY> show tables;
me
oplog.rs
replset.election
replset.minvalid
startup_log
system.profile
system.replset
local库下面的me集合保存了服务器名称
local库下面的replset.minvalid集合保存了数据库最新操作的时间戳
local库下面的startup_log集合记录这mongod每一次的启动信息
local库下面的system.indexes集合记录当前库的所有索引信息
local库下面的system.replset记录着复制集的成员配置信息rs.conf()读取这个集合
local库下面的oplog.rs集合记录着所有操作,MongoDB就是通过oplog.rs来实现数据同步的。当Primary节点插入一条数据后,oplog.rs集合中就会多一条记录
bertram:PRIMARY> db.oplog.rs.findOne();
{
"ts" : Timestamp(1465879171, 238),
"h" : NumberLong("-2275413922284641862"),
"v" : 2,
"op" : "u",
"ns" : "MyDB.SyncTable",
"o2" : {
"_id" : "bbf80260-3d58-49f1-9c8c-e093d5d57527"
},
"o" : {
"_id" : "bbf80260-3d58-49f1-9c8c-e093d5d57527",
"EntityId" : "362569",
"TypeName" : "Product",
"Times" : 14208,
"CreateTime" : ISODate("2014-11-15T14:35:51.916Z"),
"LastModified" : ISODate("2016-06-14T04:38:21.708Z"),
"LastOperationTime" : ISODate("2016-06-14T04:39:30.957Z")
}
}
字段含义
ts:8字节的 时间戳,由4字节unix timestamp + 4字节自增计数表示。在选举(如master宕机时)新primary时,会选择ts最大的那个secondary作为新primary。
op:1字节的操作类型,例如i表示insert,d表示delete。
ns:操作所在的namespace。
o:操作所对应的document,即当前操作的内容(比如更新操作时要更新的的字段和值)
o2: 在执行更新操作时的条件,仅限于update时才有该属性。
其中op,可以是如下几种情形之一:
"i": insert
"u": update
"d": delete
"c": db cmd
"db":声明当前数据库 (其中ns 被设置成为=>数据库名称+ '.')
"n": no op,即空操作,其会定期执行以确保时效性。修改配置,会产生 "n" 操作
同步过程
大致流程:
检查配置、初始化local库的各个集合、转换各个成员的状态(STARTUP -> STARTUP2 -> RECOVERING -> SECONDARY)、10秒后开始选举(SECONDARY -> PRIMARY)
初始化同步:
- 复制除local数据库外的所有数据库,mongod扫描每个源数据库中的每个集合,并将所有数据插入这些集合的自己的数据库中
- 版本3.4中更改:在为每个集合复制文档时,初始同步将构建所有集合索引(先同步索引,再同步数据) 在早期版本中,在此阶段仅构建_id索引(先同步数据,再同步索引)
- 版本3.4中更改:初始同步在数据复制期间提取新添加的操作日志记录。
- 确保目标成员在local数据库中具有足够的磁盘空间,以在此数据复制阶段持续时间临时存储这些操作日志记录。
- mongod使用源中的操作日志,将所有更改应用于数据集。
- 初始同步完成后,成员将从STARTUP2转换为SECONDARY。
初始化时,同步源的选择取决于mongod启动参数initialSyncSourceReadPreference的值(4.2.7中的新增功能)。
如果无法选择同步源,将记录错误并等待1秒钟,然后重新选择,从mongod最多可以重新初始同步源选择过程10次,然后错误退出。
复制:多线程
MongoDB使用多线程批量应用写入操作以提高并发性。 MongoDB按文档ID(WiredTiger)对批次进行分组,并同时使用不同的线程来应用每组操作,MongoDB始终以原始写入顺序对给定文档应用写入操作
副本集的完整配置信息和状态
副本集的完整配置信息和状态通过 rs.status & db.admincommand({replSetGetStatus : 1 }) 查看
https://docs.mongodb.com/manual/reference/command/replSetGetStatus/#std-label-rs-status-output
rs.status命令新增了一些可参考监控项,例如:
replSetGetStatus.optimes.lastCommittedOpTime:已写入大多数副本集成员的最新操作时间;
replSetGetStatus.optimes.appliedOpTime:已应用于副本集的该成员的最新操作时间;
replSetGetStatus.optimes.durableOpTime:已写入该副本集的该成员的journal日志的时间
MongoDB数据节点状态编号
状态值 |
状态名 |
状态说明 |
0 |
STARTUP |
节点刚启动未完成加载副本集,配置初期为该状态 |
1 |
PRIMARY |
有投票权主节点状态,唯一支持写的节点。有投票资格 |
2 |
SECONDARY |
有投票权 副本节点状态,可进行数据同步,拥有投票权 |
3 |
RECOVERING |
有投票权 特殊原因导致数据落后,节点自我检查/修复,无法读取数据 |
5 |
STARTUP2 |
有投票权 节点加载完副本集配置,并已经运行初始同步 |
6 |
UNKNOWN |
无投票权 从副本集的另一个成员的角度来看,该成员的状态尚不清楚 |
7 |
ARBITER |
有投票权 仲裁节点 |
8 |
DOWN |
无投票权 节从副本集的另一个成员的角度来看,该成员不可访问。例网络问题 |
9 |
ROLLBACK |
有投票权 执行回滚, 暂不能读取数据,回滚后进入recovering状态 |
10 |
REMOVED |
节点曾在副本集中, 但被移除 |
非投票成员的属性如
{
"_id" : <num>,
"host" : <hostname:port>,
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 0,
"tags" : {
},
"slaveDelay" : NumberLong(0),
"votes" : 0
}
rs.status()
{
"set" : "replset",
"date" : ISODate("2019-12-04T04:49:18.693Z"),
"myState" : 1,
"term" : NumberLong(3), # 代表副本集成员数量
"syncingTo" : "",
"syncSourceHost" : "", # 代表primary
"syncSourceId" : -1, # 代表primary
"heartbeatIntervalMillis" : NumberLong(2000),
"majorityVoteCount" : 2,
"writeMajorityCount" : 2,
"optimes" : {
"lastCommittedOpTime" : { #已写入大多数副本集成员的最新操作时间
"ts" : Timestamp(1575434954, 1),
"t" : NumberLong(3)
},
"lastCommittedWallTime" : ISODate("2019-12-04T04:49:14.378Z"),
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1575434954, 1),
"t" : NumberLong(3)
},
"readConcernMajorityWallTime" : ISODate("2019-12-04T04:49:14.378Z"),
"appliedOpTime" : { #已应用于副本集的该成员的最新操作时间
"ts" : Timestamp(1575434954, 1),
"t" : NumberLong(3)
},
"durableOpTime" : { #已写入该副本集的该成员的journal日志的时间
"ts" : Timestamp(1575434954, 1),
"t" : NumberLong(3)
},
"members" : [
{
"_id" : 0,
"name" : "m1.example.net:27017",
"ip" : "198.51.100.1",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 2019,
"optime" : {
"ts" : Timestamp(1575434954, 1),
"t" : NumberLong(3)
},
"optimeDate" : ISODate("2019-12-04T04:49:14Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1575434944, 1),
"electionDate" : ISODate("2019-12-04T04:49:04Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
3 选举和投票机制
选举(election)与投票(Vote)机制, MongoDB primary发生异常时, 促使副本集自动修复, 从有投票权中的secondary节点挑选出最适合的节点来接替primary工作
投票机制:大多数原则 (总节点数/2) +1 取整数; 例 5个节点的副本集,5/2+1=3; 至少得三票,最高可以容错2台机器down掉
4 回滚机制
Primary A写操作未同步给secondary节点,网络异常导致中断, secondary选取新主primary B,原A 扔写入数据 加入副本集后 成为secondary 这样数据就有了差异,需要回滚
回滚的前提
- 在同步源上没有查到比其更新的oplog(我们刚刚通过一系列麻烦的规则选出它作为同步源,但是我们的oplog却比它还新)
- 返回的的第一条oplog和其最新的oplog的OpTime和hash都不同,注意这里是比较整个OpTime,即除了时间戳之外还包括term,首先会比较term,如果term不同,那就不同
大概流程: 首先找到B与A的共同操作点,A将共同操作点后的操作写入 rollback目录下的BSON文件文件, 并撤销Oplog记录, 然后继续同步B节点数据
BSON 文件格式:<database>.<collection>.<timestamp>.bason
回滚的限制
- 4.0 之后版本,回滚数量无限制, 回滚时间默认24h (rollbackTimeLimitSecs)
- 4.0 之前版本, 数量不能超过300M 回滚时间不能超过30min
5 数据读写策略
5.1 写入策略(write Concern)
不同场景对应写入策略不同,要求最终一致性(日志业务),要求很高的响应时间(支付业务)
客户端 使用write concern来配置,通过写入响应(acknowledgement)层级,决定数据写入何时返给客户端
Write concern 包含字段 {w: <值>, J: <布尔值>, wtimeout:<数字>}
W字段 整数N 表示反馈完成操作必须同步到N个副本集上
- 1 表只写入主节点
- 0 不需要写入任何节点, 可能会出现未写入成功但未收到报错信息
- 大于1 代表必须写入大于1的节点数,不然会报错 “not enough data-bearing nodes at….”
- Majority: 写入大多数节点(半数+1 )
J 字段 表示写操作是否需要记录到日志文件中(journal) 若写入日志中,则服务意外时, 可通过日志恢复数据
Wtimeout 表示等待时间阈值,单位ms , 超过阈值代表写入失败
例
db.collection.insert({name:”alex”},writeConcern:{w:1}) # 代表写入主节点
5.2 读取策略
类似写入策略,可以配置读取策略 Read Concern & Read Preference 非互斥, 可配合使用
Read concern 的作用是让客户端指定什么样的数据可以被读取,如 数据写入primary A 后,A异常,集群重新选B, 则A要回滚,这时A的数据是不可取的
语法
db.collection.find().readConcern(<level>)
level参数配置如下
- local 从primary节点读取时的默认值, 在未写入大部分节点前就反数据(此做法会导致脏读)
- available 从secondary读取的默认值,在未写入大部分节点前就反数据(此做法会导致脏读)
- majority 数据操作必须更新至大多数节点才能被读取
- linearizable 只允许在主节点读取(readConcern(“majority”)配置时候 能保证读到已经确认的数据且查询结果为单个文档生效)
- snapshot 4.0 版本提供 多文档事物中使用
read Preference
配置该参数,可以让客户端驱动知道从哪个节点去读取数据,可以配置读写分离,就近读取数据的负载均衡效果
Mongo.setReadPref(<mode>,<tagset>)
Mode可设置
- Primary 主节点读取
- PrimaryPreferred 优先从主节点读取,若主节点无法读取, 从副节点读取
- Secondary 从副节点读取
- SecondaryPreferred 优先从副节点驱动,若无法读取,从primary读取
- Nearest 根据网络情况从最近的节点读取, 可搭配知道副本的tags限制读取来源 db.collection.find().readPref(“nearest”,[‘source’:’rpt’])
- 点赞
- 收藏
- 关注作者
评论(0)