MongoDB 存储引擎 mongorocks 原理解析
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个索引
- 用户插入文档时,底层引擎将文档内容存储,返回对应的位置信息,即 RecordId1
- 集合包含2个索引
- 插入 {_id: ObjectId1} ⇒ RecordId1 的索引
- 插入 {name: “rose”} ⇒ RecordId1 的索引
有了上述的数据,在根据_id访问时文档时 (根据其他索引字段类似)
- 根据文档的 _id 字段从底层KV引擎读取 RecordId
- 根据 RecordId 从底层KV引擎读取文档内容
mongorock 存储管理
mongorocks 存储数据时,每个key都会包含一个32位整型前缀
,实际存储时将整型转换为big endian格式存储。
- 所有的元数据的前缀都是
0000
- 每个集合、以及集合的每个索引都包含不同的前缀,集合及索引与前缀的关系存储在
0000metadata-*
为前缀的key里 -
_mdb_catalog
在mongorocks也是一个普通的集合,有单独的前缀
创建集合、写数据
- 创建集合或索引时,mongrocks会为其分配一个前缀,并将对应关系持久化,比如创建集合 bar(默认会创建_id字段的索引),mongorocks 会给集合和索引各分配一个前缀,如上图所示的
0002
,0003
,并将对应关系持久化。 - 接下来往bar集合里写的所有数据,都会带上
0002
前缀; - 往其_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
相关文章