摘要:本文尝试对Mongo的复制和分布式事务的原理进行描述,在必要的地方,对实现的正确性进行论证,希望能为MongoDB内核爱好者提供一些参考。

1.前言

  • MongoDB基于wiredTiger提供的泛化SI的功能,重构了readHistory(readMajority)的能力
  • 基于wiredTiger提供的AllCommittedTimestamp API,重构了前缀一致的主从复制(Prefix-Consistent-Replication)
  • 引入混合逻辑时钟(HLC),每个节点(Mongos/Mongod)的逻辑时钟维持在接近的值,基于此实现ChangeStream, 结合HLC与CLOCK-SI,实现分布式事务,HLC和泛化SI,CLOCK-SI两篇Paper可以作为理解MongoDB的设计的理论参考(这里并没有说MongoDB是Paper的实现)。

本文尝试对Mongo的复制和分布式事务的原理进行描述,在必要的地方,对实现的正确性进行论证,希望能为MongoDB内核爱好者提供一些参考。

2.MongoDB副本集事务介绍

  • MongoDB 副本集的事务
    • MongoDB副本集的复制是基于raft协议,相比于Paxos,raft协议实现简单,但是raft协议只支持single-master,对应的,MongoDB的副本集是主从架构,而且只有主节点支持写入操作。MongoDB副本集的事务管理,包括冲突检测,事务提交等关键操作,都只在主节点上完成。也就是说副本集的事务在事务管理方面,跟单节点逻辑基本一致。
    • MongoDB的事务,仍然是实现了 ACID 四个特性, MongoDB使用 SI 作为事务的隔离级别。

3.SI的简介

  • SI,即SnapshotIsolation,中文称为快照隔离,是一种mvcc的实现机制,它在1995年的A Critique of ANSI SQL Isolation Levels中被正式提出。因快照时间点的选取上的不同,又分为Conventional Si 和 Generalized SI。

CSI(Convensional SI)

  • CSI 选取当前最新的系统快照作为事务的读取快照
    • 就是在事务开始的时候,获得当前db最新的snapshot,作为事务的读取的snapshot,
    • snapshot(Ti) = start(Ti)
    • 可以减少写事务冲突发生的概率,并且提供读事务读取最新数据的能力
    • 一般我们说一个数据库支持SI隔离级别,其实默认是说支持CSI。比如RocksDB支持的SI就是CSI,WiredTiger在3.0版本之前支持的SI也是CSI。

GSI(Generalized SI)

  • GSI选择历史上的数据库快照作为事务的读取快照,因此CSI可以看作GSI的一个特例。
  • 在复制集的情况下,考虑 CSI, 对于主节点上的事务,每次事务的开始时间选取的系统 最新的 快照, 但是对于其他从节点来说, 并没有 统一的 “最新的” 快照这个概念。泛化的快照实际上是基于快照观测得到的,对于当前事务来说,我们通过选取合适的 更早时间的快照,可以让 从节点上的事务正确且无延迟的执行。
  • 举例如下:
    • 例如当前数据库的状态是, S={T1, T2, T3}, 现在要开始执行T4,
    • 如果我们知道T4要修改的值,在T3上没有被修改, 那么我们在执行T4的时候, 就可以按照 T2 commit后的snashot进行读取。
  • 如何选择更早的时间点,需要满足下面的规则,

  • 符号定义
  • Ti: 事务i
  • Xi: 被事务i修改过的X变量
  • snapshot(Ti): 事务i的选取的快照时间
  • start(Ti): 事务i的开始时间
  • commit(Ti): 事务i的提交时间
  • abort(Ti): the time when Ti is aborted.
  • end(Ti): the time when Ti is committed or aborted.

公式解释

读规则

    • G1.1, 如果变量X被本事务修改了值且读取到了新的值, 那么 读操作一定在写操作后面;
    • G1.2, 如果事务i读取了事务j更新的变量的X, 那么一定不会有事务i更新X的操作,在事务i读取了事务j更新的变量的X这个操作前面;
    • G1.3, 事务j的提交时间早于事务i的快照时间;
    • G1.4, 对于任意一个会更新变量X的事务k, 那么这个事务k一定满足, 要么事务k的提交时间小于事务j, 要么这个事务k的提交时间大于事务i。

写规则

    • G2, 对于任意已经在提交历史里的两个事务,Ci, Cj, 那么一定可以保证当 事务j的commit时间戳在 事务i的观测时间段内时(snapshot(Ti), commit(Ti)), 那么他们更新的变量交集一定为空。

PCSI(PREFIX-CONSISTENT SNAPSHOT ISOLATION SI)

    • GSI 只是定义了一个范围的range,都可以作为SI使用,并没有定义具体应该选择哪个SI。
    • PCSI 是为了复制集而设计的。对于一个事务Ti 要开S节点开始运行, 那么 S节点将必须包含这个事务所需要的所有前置事务都必须运行且提交。
    • 相比较于GSI, PCSI的读规则,额外增加了 P1.5 规则。

  • SI的提交时间戳设置,依据 A Critique of ANSI SQL Isolation Levels 中的描述, 提交时间戳的设置应该是单调递增的。新设置的时间戳,应该大于系统中已经存在的开始时间戳和提交时间戳。
  • SI 读取时间戳的设置,必须保证比当前系统中正在运行的事务的最小的提交时间戳还要小, 因为一旦大于当前系统中正在运行事务的最小的提交时间戳,那么这个读事务读取到的数据就是未定义的, 取决于读事务启动的时间,而不是snapshot的时间,这违背了 一致性的要求。举例如下
    • 当前已经完成的事务是T1,正在运行的事务是T2, 将要运行的读事务是T3, 如果 T3的读时间戳大于T2事务提交时间戳, 并且T2事务正在运行,等到T2事务执行完后。我们观察这个 database,就会发现 他违背了GSI,

事务执行顺序如下所示是:

T1 commited and commitTs(1) -> T2 start -> T2 set commitTs(2) -> T3 start -> T3 set snapshotTs(3) -> T3 commit -> pointA -> T2 commit -> pointB

那么可知, T3事务实际读取的值是 T1事务的值。但根据 pointB 点来看 GSI的读规则 1.4 的要求,会发现, 如果T3读到T1的事务的修改,那么必然要求, T3和T1之间没有空洞。但实际上 T2 是落在了 T3和T1之间的, 也就是说, 违反了 GSI 1.4的读规则。

    • 所以我们必须规定, SI 读取时间戳的设置,必须保证比当前系统中正在运行的事务的最小的提交时间戳还要小。

4.MongoDB副本集时间戳应用

MongoDB 4.0的复制也是利用时间戳特性解决了3.x系列MongoDB从节点复制造成从节点性能下降的关键方案。

  • MongoDB oplog 乱序问题
    • MongoDB主备节点的数据同步并不基于WiredTiger的wal日志来做的。相反,mongodb会将每次操作的数据变更写入到一个叫做oplog的集合里。
    • oplog这个集合,虽然名字带有log,但实际上,它是一个MongoDB的表, 对oplog的写入,并不是 append的方式修改的, 而是呈现出一种尾部乱序的方式。
    • 对于oplog来说, oplog的读取顺序是按照TS字段来排序的, 跟上层的提交顺序无关。所以存在后开始的事务,在oplog先读取的场景。
  • oplog 空洞
    • 因为出现了乱序,所以从节点在读取oplog的时候,就会在某些时间点出现空洞。举例如下:
    • 时间点1: oplog 顺序为: Ta -> Tb, 此时系统中还有一个事务Tc在运行
    • 时间点2: oplog 顺序为: Ta -> Tc -> Tb, 当Tc运行结束后, 因为ts的顺序, 看起来是将Tc插入到了Ta和Tb之间。
    • 那么当 从节点 在时间点1 reply 到 Tb的时候, 实际上是漏了 Tc的,这个就是oplog的空洞, 他产生的原因是因为,从节点如果每次读取oplog最新的数据,就有可能会得到一个不连续的数据, 例如 时间点1上 Ta-> Tb. 这就是oplog空洞。
    • 在具体复制逻辑中,我们必须想办法来从节点读取到含有空洞的oplog数据。这也是GSI的要求, snapshot的选取不能含有空洞。
    • 因为 oplog的Ts是mongo上层给的,我们很容易知道哪些事务有哪些ts, 我们再将这个ts 作为事务的commitTs 放到 oplog存储的事务里, 这样我们读取 oplog的顺序事务的可见性顺序相一致了,在这种情况下,我们就可以 根据 活跃事务列表, 就可以将oplog 分为两个部分,
    • 假设活跃commitTs列表的事务是 {T10, T11, T12}, 活跃事务列表是 {T10, T11, T12, T13, T14}, 那么意味着, 目前有 T10, T11, T12, T13, T14 再运行,并且 T10, T11, T12 已经设置了 commitTs, 又因为 上面讨论的 commitTs 是单调递增的, 那么我们可知, T13, T14 的commitTs 一定大于 maxCommitTs(T10, T11, T12), 而且我们还可知, minCommitTs(T10,T11,T12) 就是全局最小的 commitTs, 而小于这些的 commitTs的事务,因为不在 活跃事务列表里了, 表示已经提交了, 那么我们可以知道, oplog ts 在 全局最小的 commitTs 之前的, 就是都提交了的, oplog 按照 commitTs 排序后,如下所示

… Tx | minCommitTs(T10,T11,T12) | …

我们可以知道 T9, 或者说小于 minCommitTs(T10,T11,T12) 都是无空洞,因为系统不会再提交小于 minCommitTs(T10,T11,T12) 的事务到oplog里了, 所以从节点可以直接恢复这里的数据。

    • 上面说的oplog minCommitTs(T10,T11,T12) 在 mongodb里,就是特殊的timestamp, 这个后文会讲。
    • 通过上面的方案,我们可以解决空洞的问题。这个时候,从节点每次恢复数据的时候,将读取的snapshot,设置为上一次恢复的Ts(同样也是无空洞的Ts), 这样的话, 从节点的恢复数据和读取数据也就做到了互不冲突。从而解决了 3.x系列的 从节点同步数据造成节点性能下降的问题。

点击关注,第一时间了解华为云新鲜技术~

MongoDB 事务,复制和分片的关系的更多相关文章

  1. MySQL Cluster 与 MongoDB 复制群集分片设计及原理

    分布式数据库计算涉及到分布式事务.数据分布.数据收敛计算等等要求 分布式数据库能实现高安全.高性能.高可用等特征,当然也带来了高成本(固定成本及运营成本),我们通过MongoDB及MySQL Clus ...

  2. 【2016-11-1】【坚持学习】【Day16】【MongoDB】【复制集 分片】

    Mongodb 两种集群方式 复制集 通常是一主一从,一主多从 mongodb的复制至少需要两个节点.其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据. mongodb各 ...

  3. Mongodb主从复制/ 副本集/分片集群介绍

    前面的文章介绍了Mongodb的安装使用,在 MongoDB 中,有两种数据冗余方式,一种 是 Master-Slave 模式(主从复制),一种是 Replica Sets 模式(副本集). Mong ...

  4. Mongodb 基础 复制集原理和搭建

    数据复制原理 开启复制集后,主节点会在local库下生成一个集合叫 oplog.rs,这是一个有限的集合,即大小固定.这个集合记入了整个mongod实例一段时间内数据库的所有变更操作(如:增/删/改) ...

  5. mongodb副本集加分片集群安全认证使用账号密码登录

    mongodb副本集加分片集群搭建网上资料有很多.粘贴一个写的比较好的.副本集加分片搭建 对于搭建好的mongodb副本集加分片集群,为了安全,启动安全认证,使用账号密码登录. 默认的mongodb是 ...

  6. 事务复制5: Transaction and Command

    事务复制使用 dbo.msrepl_transactions 和 dbo.MSrepl_commands 存储用于数据同步的Transaction和Command.在replication中,每个co ...

  7. SQL Server提高事务复制效率优化(一)总体概述

      随着公司业务的发展,数据量增长迅速,在解决Scale Out的同时,还要考虑到主从的复制延迟问题,尽量降到1s以内满足线上业务,如果不调整,SQL Server默认的配置可能平均要3s左右.生产的 ...

  8. Replication的犄角旮旯(四)--关于事务复制的监控

    <Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...

  9. 一个事务复制的bug--更新丢失

    有两种情况会造成更新丢失,第一种是不正确的设置,例如外键或触发器的“Not For Replication” (NFR)属性没有开启.详情请参考http://blogs.msdn.com/b/apgc ...

随机推荐

  1. C# 9.0 终于来了, Top-level programs 和 Partial Methods 两大新特性探究

    一:背景 1. 讲故事 .NET 5 终于在 6月25日 发布了第六个预览版,随之而来的是更多的新特性加入到了 C# 9 Preview 中,这个系列也可以继续往下写了,废话不多说,今天来看一下 To ...

  2. bugku 一个神奇的登录框

    一个登录界面,填个admin,123试试,提示try again 抓包看看. 在admin后加个’提示try again 看来是被过滤了,试试” 报错了,加上# 报错没有了,说明存在注入点. 先来判断 ...

  3. Git安装及配置SSH-Key

    下载Git 打开 https://git-scm.com/downloads 选择windows, 下载并安装. 配置全局用户名及邮箱 配置用户名 git config --global user.n ...

  4. Codeforces Round #652 (Div. 2) 总结

    A:问正n边形的一条边和x轴平行的时候有没有一条边和y轴重合,直接判断n是否是4的倍数 #include <iostream> #include <cstdio> #inclu ...

  5. Flask-install-python2.6

    命令: # 安装virtualenv $ sudo yum install python-setuptools $ sudo easy_install virtualenv OR sudo pip i ...

  6. 基于AOP和ThreadLocal实现日志记录

    基于AOP和ThreadLocal实现的一个日志记录的例子 主要功能实现 : 在API每次被请求时,可以在整个方法调用链路中记录一条唯一的API请求日志,可以记录请求中绝大部分关键内容.并且可以自定义 ...

  7. jupyter lab最强代码补全插件

    1 简介 提起kite相信不少朋友都有印象,它是一个功能非常强大的代码补全工具,目前可用于Python与javascript,为许多知名的编辑器譬如Vs Code.Pycharm提供对应的插件. 图1 ...

  8. 【实践】如何利用tensorflow的object_detection api开源框架训练基于自己数据集的模型(Windows10系统)

    如何利用tensorflow的object_detection api开源框架训练基于自己数据集的模型(Windows10系统) 一.环境配置 1. Python3.7.x(注:我用的是3.7.3.安 ...

  9. 一.8.django权限管理/drf权限管理

    1.什么是权限管理: .权限管理,一般指根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源 .权限管理好比如钥匙,有了钥匙就能把门打开,但是权限设置是有级别之分的,假如这个系 ...

  10. MySQL常规操作

    数据库的相关概念 一.数据库的好处 1.可以持久化数据到本地 2.结构化查询 二.数据库的常见概念 ★ 1.DB:数据库,存储数据的容器 2.DBMS:数据库管理系统,又称为数据库软件或数据库产品,用 ...