mongodb intro

一. 概览

插件式存储引擎, MongoDB 3.0引入了插件式存储引擎API,为第三方的存储引擎厂商加入MongoDB提供了方便
目前除了早期的MMAP存储引擎外,WiredTiger和RocksDB 均 已完成了对MongoDB的支持,

1.1 compare MMAPv1 and WiredTiger ; 以及WiredTiger 新特性

  • 文档级别并发控制
    多个客户端请求同时更新一个集合内存的多个文档,再也不需要在排队等待 库级别的写锁。
  • Snapshot Checkpoints(快照和检查点):mongoDB每60秒或日志文件【jonurnal】达到2G会创建一个检查点(产生一个snapshot[快照]),该Snapshot呈现的是内存中数据的一致性视图,当向Disk写入数据时,WiredTiger将Snapshot中的所有数据以一致性方式写入到数据文件(Disk Files)中
  • Journal(日志): 开启 journal 后,每次写入会记录一条操作日志(通过journal可以重新构造出写入的数据),一次写入,会对应数据、索引,oplog的修改,而这3个修改,会对应一条journal操作日志
  • 磁盘数据压缩
    WiredTiger 支持对所有集合和索引进行Block压缩和前缀压缩(如果数据库启用了journal,journal文件一样会压缩),
  • MemoryUse(内存使用)
    从MongoDB 3.2 版本开始,WiredTiger内部缓存的使用量,默认值是:1GB 或 60% of RAM - 1GB,取两值中的较大值;文件系统缓存的使用量不固定,MongoDB自动使用系统空闲的内存,这些内存不被WiredTiger缓存和其他进程使用,数据在文件系统缓存中是压缩存储的。

基础数据结构

  • 典型的B-Tree数据结构
    B-Tree是为磁盘或其它辅助存储设备而设计的一种数据结构,目的是为了在查找数据的过程中减少磁盘I/O的次数

从上往下 Root结点 -> 内部结点 -> 叶子结点,
每个结点就是一个Page,数据以Page为单位在内存和磁盘间进行调度,
每个Page的大小决定了相应结点的分支数量,每条索引记录会包含一个数据指针,指向一条数据记录所在文件的偏移量。

  • 磁盘上的基础数据结构 B-Tree
    对于WiredTiger存储引擎来说,集合所在的数据文件和相应的索引文件都是按B-Tree结构来组织的,
    不同之处在于数据文件对应的B-Tree叶子结点上除了存储键名外(keys),还会存储真正的集合数据(values)

WiredTiger有一个块设备管理的模块,用来为page分配block。
如果要定位某一行数据(key/value)的位置,可以先通过block的位置找到此page(相对于文件起始位置的偏移量),再通过page找到行数据的相对位置,最后可以得到行数据相对于文件起始位置的偏移量offsets。由于offsets是一个8字节大小的变量,所以WiredTiger磁盘文件的大小,其最大值可以非常大(264bit)。

  • 内存上的基础数据结构
    内存会构造相应的B-Tree来存储这些数据
    按需将磁盘的数据以page为单位加载到内存

为了高效的支撑CRUD等操作以及将内存里面发生变化的数据持久化到磁盘上,WiredTiger也会在内存里面维护其它几种数据结构,

存储引擎是如何将数据加载到内存,然后如何通过相应数据结构来支持查询、插入、修改操作的。

rootpage、internal page和leaf page,前两者包含指向其子页的page index指针,不包含集合中的真正数据,leaf page包含集合中的真正数据即keys/values和指向父页的home指针

WAL

  • what is wal ?
    预写式日志
    Write Ahead Log简称WAL,在分布式存储系统中的元数据更新中应用得十分广泛。WAL的主要意思是说在将元数据的变更操作写入到持久稳定的db之前,先预先写入到一个log中,然后再由另外的操作将log apply到外部的持久db里去。这种模式会减少掉每次的db写入操作,尤其当系统要处理大量的transaction操作的时候,WAL的方式相比较于实时同步db的方式有着更高的效率。
    WAL还有一点很重要的帮助是可以在disaster recovery过程中起到状态恢复的作用,系统在load完元数据db后,再把未来得及提交的WAL apply进来,就能恢复成和之前最终一致的状态。

WAL 的优点

  • 读和写可以完全地并发执行,不会互相阻塞(但是写之间仍然不能并发)。
  • WAL 在大多数情况下,拥有更好的性能(因为无需每次写入时都要写两个文件)。
  • 磁盘 I/O 行为更容易被预测。
  • 使用更少的 fsync()操作,减少系统脆弱的问题。

Checkpoint包含的关键信息

事务

一.索引

索引支持MongoDB中查询的高效执行。如果没有索引,MongoDB必须执行集合扫描

  • 介绍
    当你往某各个集合插入多个文档后,每个文档在经过底层的存储引擎持久化后,会有一个位置信息,通过这个位置信息,就能从存储引擎里读出该文档。
    mmapv1引擎里,位置信息是『文件id + 文件内offset 』,
    wiredtiger存储引擎(一个KV存储引擎)里,位置信息是wiredtiger在存储文档时生成的一个key,通过这个key能

  • 文本索引

使用 $text 查询操作符在一个有 text index 的集合上执行文本检索。

$text 将会使用空格和标点符号作为分隔符对检索字符串进行分词, 并且对检索字符串中所有的分词结果进行一个逻辑上的 OR 操作。
使用下面的查询语句来找到所有包含 “coffee”, “shop”, 以及 “java” 列表中任何词语的商店:

精确检索

您也可以通过将它们包括在双引号中来进行精确检索。例如,下列的命令将会找到所有包含”java” 或者 “coffee shop” 的文档:

词语排除

为了排除一个词语,您可以在前面加上一个 “-” 字符。例如,为了找到所有包含 “java” 或者 “shop” 但是不包含 “coffee” 的商店,使用下面的命令:

排序
MongoDB默认返回未经排序的结果。然而,文本检索查询将会对每个文档计算一个相关性分数,表明该文档与查询的匹配程度。

为了使用相关性分数进行排序,您必须显式地对 $meta textScore字段进行映射然后基于该字段进行排序。

二. 复制集

MongoDB选举的原理
MongoDB的节点分为三种类型,分别为标准节点(host)、被动节点(passive)和仲裁节点(arbiter)

只有标准节点才有可能被选举为活跃节点(主节点),拥有选举权。被动节点有完整副本,不可能成为活跃节点,具有选举权。仲裁节点不复制数据,不可能成为活跃节点,只有选举权。说白了就是只有标准节点才有可能被选举为主节点,即使在一个复制集中说有的标准节点都宕机,被动节点和仲裁节点也不会成为主节点。后续有示例演示验证。
标准节点与被动节点的区别:priority值高者是标准节点,低者则为被动节点
选举规则是票数高的获胜,priority是优先权 0~1000 的值,相当于额外增加 0~1000 的票数。选举结果:票数高者获胜;若票数相同,数据新者获胜。

如果启用复制集的话,在内存中会多一个OPLOG区域,是在节点之间进行同步的一个手段,它会把操作日志放到OPLOG中来,然后OPLOG会复制到从节点上。从节点接收并执行OPLOG中的操作日志来达到数据的同步操作。

  1. 客户端的数据进来;

  2. 数据操作写入到日志缓冲;

  3. 数据写入到数据缓冲;

  4. 把日志缓冲中的操作日志放到OPLOG中来;

  5. 返回操作结果到客户端(异步);

  6. 后台线程进行OPLOG复制到从节点,这个频率是非常高的,比日志刷盘频率还要高,从节点会一直监听主节点,OPLOG一有变化就会进行复制操作;

  7. 后台线程进行日志缓冲中的数据刷盘,非常频繁(默认100)毫秒,也可自行设置(30-60);

后台线程进行数据缓冲中的数据刷盘,默认是60秒;

Oplog的数据结构

ts:操作发生时的时间戳,这个时间戳包含两部分内容t和i,t是标准的时间戳(自1970年1月1日 00:00:00 GMT 以来的毫秒数)。而i是一个序号,目的是为了保证t与i组合出的Mongo时间戳ts可以唯一的确定一条操作记录。

h:此操作的独一无二的ID。

v:oplog的版本。

op:操作类型(insert、update、delete、db cmd、null),紧紧代表一个消息信息。

ns:操作所处的命名空间(db_name.coll_name)。

o:操作对应的文档,文档在更新前的状态(“msg” 表示信息)。

o2:仅update操作时有,更新操作的变更条件(只记录更改数据)。

Oplog大小及意义

当你第一次启动复制集中的节点时,MongoDB会用默认大小建立Oplog。这个默认大小取决于你的机器的操作系统。大多数情况下,默认的oplog大小是足够的。在 mongod 建立oplog之前,我们可以通过设置 oplogSizeMB 选项来设定其大小。但是,如果已经初始化过复制集,已经建立了Oplog了,我们需要通过修改Oplog大小中的方式来修改其大小。

OpLog的默认大小:

在64位Linux、Windows操作系统上为当前分区可用空间的5%,但最大不会超过50G。
在64位的OS X系统中,MongoDB默认分片183M大小给Oplog。
在32位的系统中,MongoDB分片48MB的空间给Oplog。

Oplog大小应随着实际使用压力而增加

如果我能够对我复制集的工作情况有一个很好地预估,如果可能会出现以下的情况,那么我们就可能需要创建一个比默认大小更大的oplog。相反的,如果我们的应用主要是读,而写操作很少,那么一个小一点的oplog就足够了。

下列情况我们可能需要更大的oplog。

  • 同时更新大量的文档。

Oplog为了保证 幂等性 会将多项更新(multi-updates)转换为一条条单条的操作记录。这就会在数据没有那么多变动的情况下大量的占用oplog空间。

  • 删除了与插入时相同大小的数据

如果我们删除了与我们插入时同样多的数据,数据库将不会在硬盘使用情况上有显著提升,但是oplog的增长情况会显著提升。

  • 大量In-Place更新

如果我们会有大量的in-place更新,数据库会记录下大量的操作记录,但此时硬盘中数据量不会有所变化。

  • Oplog幂等性

Oplog有一个非常重要的特性——幂等性(idempotent)。即对一个数据集合,使用oplog中记录的操作重放时,无论被重放多少次,其结果会是一样的。举例来说,如果oplog中记录的是一个插入操作,并不会因为你重放了两次,数据库中就得到两条相同的记录。

  • Oplog的状态信息

我们可以通过 rs.printReplicationInfo() 来查看oplog的状态,包括大小、存储的操作的时间范围。关于oplog的更多信息可以参考Check the Size of the Oplog。

复制集数据同步过程

Mongodb复制集里的Secondary会从Primary上同步数据,以保持副本集所有节点的数据保持一致,数据同步主要包含2个过程

initial sync

先通过initial sync同步全量数据,再通过replication不断重放Primary上的oplog同步增量数据。

  • initial sync

初始同步会将完整的数据集复制到各个节点上,Secondary启动后,如果满足以下条件之一,会先进行initial sync。

  1. Secondary上oplog为空,比如新加入的空节点。

  2. local.replset.minvalid集合里_initialSyncFlag标记被设置。当initial sync开始时,同步线程会设置该标记,当initial sync结束时清除该标记,故如果initial sync过程中途失败,节点重启后发现该标记被设置,就知道应该重新进行initial sync。

  3. BackgroundSync:: _initialSyncRequestedFlag被设置。当向节点发送resync命令时,该标记会被设置,此时会强制重新initial sync。

  • initial sync同步流程
  1. minValid集合设置_initialSyncFlag(db.replset.minvalid.find())。

  2. 获取同步源当前最新的oplog时间戳t0。

  3. 从同步源克隆所有的集合数据。

  4. 获取同步源最新的oplog时间戳t1。

  5. 同步t0~t1所有的oplog。

  6. 获取同步源最新的oplog时间戳t2。

  7. 同步t1~t2所有的oplog。

  8. 从同步源读取index信息,并建立索引(除了_id ,这个之前已经建立完成)。

  9. 获取同步源最新的oplog时间戳t3。

  10. 同步t2~t3所有的oplog。

  11. minValid集合清除_initialSyncFlag,initial sync结束。

当完成了所有操作后,该节点将会变为正常的状态secondary。

replication (sync oplog)

initial sync结束后,Secondary会建立到Primary上local.oplog.rs的tailable cursor,不断从Primary上获取新写入的oplog,并应用到自身。Tailable cursor每次会获取到一批oplog,Secondary采用多线程重放oplog以提高效率,通过将oplog按照所属的namespace进行分组,划分到多个线程里,保证同一个namespace的所有操作都由一个线程来replay,以保证统一namespace的操作时序跟primary上保持一致(如果引擎支持文档锁,只需保证同一个文档的操作时序与primary一致即可)。

Server Side Public License (SSPL)

13.提供程序即服务。
如果您将本程序或修改版的功能作为服务提供给第三方,则必须根据本许可的条款,通过网络下载免费向所有人提供服务源代码。

将本程序或修改版本的功能作为服务提供给第三方可以包括但不限于使第三方能够通过计算机网络与本程序或修改版本的功能进行远程交互,

从而提供其全部或全部价值的服务。主要来自程序或修订版的价值,或提供给用户以实现程序或修订版的主要目的的服务。

“服务源代码”是指本程序或经修改的版本的相应源,以及您用于使本程序或经修改的版本作为服务可用的所有程序的相应源,

包括但不限于管理软件,用户界面,应用程序界面,自动化软件,监视软件,备份软件,存储软件和托管软件,

所有这些使用户可以使用您提供的服务源代码运行服务实例。

使用MongoDB作为数据库的其他SaaS应用程序没有copyleft条件。
新许可证对使用MongoDB构建并作为服务(SaaS)提供的应用程序有什么影响?
仅当您向第三方提供MongoDB的功能或MongoDB的修改版本时,SSPL第13节的copyleft条件才适用。 使用MongoDB作为数据库的其他SaaS应用程序没有copyleft条件。

评论