免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1273 | 回复: 0
打印 上一主题 下一主题

[MongoDB] MongoDB 如何保证 oplog 顺序? [复制链接]

求职 : Linux运维
论坛徽章:
203
拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:57:092015小元宵徽章
日期:2015-03-06 15:58:182015年亚洲杯之约旦
日期:2015-04-05 20:08:292015年亚洲杯之澳大利亚
日期:2015-04-09 09:25:552015年亚洲杯之约旦
日期:2015-04-10 17:34:102015年亚洲杯之巴勒斯坦
日期:2015-04-10 17:35:342015年亚洲杯之日本
日期:2015-04-16 16:28:552015年亚洲杯纪念徽章
日期:2015-04-27 23:29:17操作系统版块每日发帖之星
日期:2015-06-06 22:20:00操作系统版块每日发帖之星
日期:2015-06-09 22:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-08-29 17:04 |只看该作者 |倒序浏览
MongoDB 复制集里,主备节点间通过 oplog 来同步数据,Priamry 上写入数据时,会记录一条oplog,Secondary 从 Primary 节点拉取 oplog并重放,以保证最终存储相同的数据集。

oplog 主要特性

幂等性,每一条oplog,重放一次或多次,得到的结果是一样的;为实现幂等 mongodb 对很多操作进行来转换,比如将 insert 转换为 upsert、$inc 操作转换为 $set等等。
固定大小(capped collection),oplog 使用固定空间存储,当空间满了时,会自动删除最旧的文档。
oplog 按时间戳排序,并且在所有节点上顺序保持一致
本文主要介绍MongoDBD 如何保证 oplog 有序存储并读取,关于 oplog 扩展阅读

阿里云MongoDB数据库的自适应oplog管理
MongoDB 3.2删除优化策略
MongoDB Secondary同步慢问题分析
MongoDB Secondary同步慢问题分析(续)
MongoDB同步原理解析
并发写 oplog 时,如何加锁?

Primary 上写入文档时,首先对写入的 DB 加写意向锁,再对集合加写意向锁,然后调用底层引擎接口写入文档,对 local 数据库加写意向锁,对oplog.rs集合加写意向锁,写入 oplog。关于MongoDB 多层级意向锁的机制,参考官方文档。

Write1

DBLock("db1", MODE_IX);
CollectionLock("collection1", MODE_IX);
storageEngine.writeDocument(...);   
DBLock("local", MODEX_IX);
CollectionLock("oplog.rs", MODEX_IX);
storageEngine.writeOplog(...);
Write2

DBLock("db2", MODE_IX);
CollectionLock("collection2", MODE_IX);
storageEngine.writeDocument(...);   
DBLock("local", MODEX_IX);
CollectionLock("oplog.rs", MODEX_IX);
storageEngine.writeOplog(...);
如何保证Primar上oplog 顺序?

基于上述并发策略,在多个写并发的情况下,如何保证 oplog 顺序?

oplog是一个特殊的 capped collection,文档没有_id字段,但包含一个 ts(时间戳字段),所有 oplog 的文档按照 ts 顺序存储。如下是几条 oplog 的例子。

{ "ts" : Timestamp(1472117563, 1), "h" : NumberLong("2379337421696916806"), "v" : 2, "op" : "c", "ns" : "test.$cmd", "o" : { "create" : "sbtest" } }
{ "ts" : Timestamp(1472117563, 2), "h" : NumberLong("-3720974615875977602"), "v" : 2, "op" : "i", "ns" : "test.sbtest", "o" : { "_id" : ObjectId("57bebb3b082625de06020505"), "x" : "xkfjakfjdksakjf" } }
以 wiredtiger 为例,在写入 oplog 文档时,会以 oplog 的 ts 字段作为 key、文档内容作为 value,写入一条 KV 记录,wiredtiger 会保证存储(btree 或 lsm 的方式都能保证)的文档按 key 来排序,这样就解决『文档按 ts 字段顺序存储』的问题。但仍然存在并发乱序的问题,例如:

并发写入多条 oplog时,时间戳分别是ts1、ts2、ts3 (ts1 < ts2 < ts3 ),ts1、ts3先成功了,这时Secondary 拉取到这2条 oplog,然后 ts2才写成功,然后 Secondary 再拉取到ts2,也就是说 Secondary 看到的 oplog 顺序为ts1、ts3、ts2,就会出现 oplog 乱序的问题。

MongoDB(wiredtiger 引擎)的解决方案是通过在读取时进行限制,保证Secondary 节点看到一定是顺序的,具体实现机制如下:

写入 oplog前,会先加锁给 oplog 分配时间戳,并注册到未提交列表里

lock();
ts = getNextOpTime(); // 根据当前时间戳 + 计数器生成
_uncommittedRecordIds.insert(ts);
unlock();
正式写入 oplog,在写完后,将对应的 oplog 从未提交列表里移除

writeOplog(ts, oplogDocument);
lock();
_uncommittedRecordIds.erase(ts);
unlock();
在拉取 oplog 时

if (_uncommittedRecordIds.empty()) {
    // 所有 oplog 都可读
} else {
    // 只能到未提交列表最小值以前的 oplog
}
通过上述规则,最终保证Primary 上 oplog 按 ts 字段存储,并且 Secondary能按序读取所有 oplog。

如何保证 Secondary 上 oplog 顺序与Primary 一致?

Secondary 把 oplog 拉取到本地后,会多线程重放,最后在一个线程里将拉取到的 oplog原样写入本地的 local.oplog.rs集合,这样就保证 Secondary oplog 最终与 Primary 上完全相同。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP