MongoDB 存储引擎 mongorocks 原理解析

2020-05-22 00:00:00 索引 数据 文档 集合 前缀

mongorocks 是基于的开源KV数据库RocksDB)实现的一个MongoDB存储引擎,借助rocksdb的特性,mongorocks能很好的支持一些高并发随机写入、读取的应用场景。

MongoDB 与 mongorocks 的关系



mongodb 支持多种引擎,目前官方已经支持了mmapv1、wiredtiger、in-Memory等,而mongorocks则是第三方实现的存储引擎之一(对应上图红框的位置)。

MongoDB KV存储引擎模型

MongoDB 从 3.0 版本 开始,引入了存储引擎的概念,并开放了 StorageEngine 的API 接口,为了方便KV存储引擎接入作为 MongoDB 的存储引擎,MongoDB 又封装出一个 KVEngine 的API接口,比如官方的 wiredtiger 存储引擎就是实现了 KVEngine 的接口,本文介绍的 mongorocks 也是实现了KVEngine的接口。

KVEngine 主要需要支持如下接口

创建/删除集合

MongoDB 使用 KVEngine 时,将所有集合的元数据会存储到一个特殊的 _mdb_catalog 的集合里,创建、删除集合时,其实就是往这个特殊集合里添加、删除元数据。

_mdb_catalog 特殊的集合不需要支持索引,只需要能遍历读取集合数据即可,MongoDB在启动时,会遍历该集合,来加载所有集合的元数据信息。

数据存储及索引

插入新文档时,MongoDB 会调用底层KV引擎存储文档内容,并生成一个 RecordId 的作为文档的位置信息标识,通过 RecordId 就能在底层KV引擎读取到文档的内容。

如果插入的集合包含索引(MongoDB的集合默认会有_id索引),针对每项索引,还会往底层KV引擎插入一个新的 key-value,key 是索引的字段内容,value 为插入文档时生成的 RecordId,这样就能快速根据索引找到文档的位置信息。



如上图所示,集合包含{_id: 1}, {name: 1} 2个索引

  1. 用户插入文档时,底层引擎将文档内容存储,返回对应的位置信息,即 RecordId1
  2. 集合包含2个索引
  • 插入 {_id: ObjectId1} ⇒ RecordId1 的索引
  • 插入 {name: “rose”} ⇒ RecordId1 的索引

有了上述的数据,在根据_id访问时文档时 (根据其他索引字段类似)

  1. 根据文档的 _id 字段从底层KV引擎读取 RecordId
  2. 根据 RecordId 从底层KV引擎读取文档内容


mongorock 存储管理


mongorocks 存储数据时,每个key都会包含一个32位整型前缀,实际存储时将整型转换为big endian格式存储。

  • 所有的元数据的前缀都是0000
  • 每个集合、以及集合的每个索引都包含不同的前缀,集合及索引与前缀的关系存储在 0000metadata-*为前缀的key里
  • _mdb_catalog 在mongorocks也是一个普通的集合,有单独的前缀


创建集合、写数据

  1. 创建集合或索引时,mongrocks会为其分配一个前缀,并将对应关系持久化,比如创建集合 bar(默认会创建_id字段的索引),mongorocks 会给集合和索引各分配一个前缀,如上图所示的 0002, 0003,并将对应关系持久化。
  2. 接下来往bar集合里写的所有数据,都会带上 0002 前缀;
  3. 往其_id索引里写的数据都会带上前缀 0003

写索引时,有个比较有意思的设计,重点介绍下 (其他的key-value引擎,如wiredtiger也使用类似的机制)

MongoDB 支持复合索引,比如db.createIndex({a: 1, b, -1, c, 1}),这个索引要先按a字段升序、a相同的按b字段降序.. 依此类推,但KV引擎并没有这么强大的接口,如何实现对这种复合索引的支持呢?

MongoDB针对每个索引,会有一个位图来描述索引各个字段的排序方向,比如插入如下2条索引时 (key的部分会转换为BSON格式插入到底层)

{a: 100, b: 200, c: 300}  == > RecordId1

{a: 100, b: 300, c: 400}  ==> RecordId2  

相关文章