写点什么

MongoDB 源码学习:catalog 与 storage

作者:云里有只猫
  • 2022-12-04
    广东
  • 本文字数:2989 字

    阅读完需:约 10 分钟

MongoDB源码学习:catalog与storage

简介

为了方便的继续往下继续学习 MongoDB 的源码,这里先了解 catalog 与 storage,因为无论是 Command,还是 Insert、Update 等,最终都是与 catalog 和 storage 两个或其中一个交互。

catalog

catalog 其翻译是目录的意思,目录在src/mongo/db/catalog。这个地方可以认为是一个为上面应用层和下面存储层连接的地方,里面封装了 MongoDB 的抽象对象,例如 DataBase、Collection 等。


下面简单看下 catalog 中 Database、Collection、CollectionCatalog 都定义了什么


// database.h 只列出了部分伪代码class Database : public Decorable<Database> {public:    virtual Status userCreateNS(...) const = 0;    virtual Collection* createCollection(...) const = 0;    virtual Status dropCollection(...) const = 0;    virtual Status renameCollection(...) const = 0;}

// collection.h 同样只展示部分伪代码class Collection : public DecorableCopyable<Collection> {public: virtual bool findDoc(...) const = 0; virtual void deleteDocument(...) const = 0; virtual Status insertDocuments(...) const = 0;}

// collection_catalog.hclass CollectionCatalog {public: CollectionPtr lookupCollectionByUUID(OperationContext* opCtx, CollectionUUID uuid) const;private: std::shared_ptr<Collection> _lookupCollectionByUUID(CollectionUUID uuid) const; CollectionCatalogMap _catalog; NamespaceCollectionMap _collections;}
复制代码


从上面的伪代码大概可以猜测出这 3 个对象的意思了:


  • Database - 数据库的抽象

  • Collection - 集合的抽象

  • CollectionCatalog - 集合目录的抽象(简单来讲就是 Collection 的集合)

storage

看名字就知道这个是持久化相关的,目录在src/mongo/db/storage。这个地方是对底层存储的一层抽象。下面看看几个具体的例子。


// storage_engine.hclass StorageEngine {public:    virtual std::vector<std::string> listDatabases() const = 0;        virtual KVEngine* getEngine() = 0;    virtual DurableCatalog* getCatalog() = 0;}
// durable_catalog.hclass DurableCatalog {public: virtual RecordStore* getRecordStore() = 0; virtual StatusWith<std::pair<RecordId, std::unique_ptr<RecordStore>>> createCollection(...) = 0; virtual Status createIndex(...) = 0;}
// record_store.hclass RecordStore : public Ident {public: virtual bool findRecord(OperationContext* opCtx, const RecordId& loc, RecordData* out) const virtual Status insertRecords(OperationContext* opCtx, std::vector<Record>* inOutRecords, const std::vector<Timestamp>& timestamps) = 0;}
复制代码


由上面伪代码不难看出:


  • StorageEngine - 作为底层存储引擎的抽象,提供一个 getCatalog 方法获取数据集合,另外还有提供一个 getEngine 的方法获取 KVEngine。

  • DurableCatalog - 持久化集合,由 StorageEngine::getCatalog 获得。封装了底层操作 Collection 集合的方法。

  • RecordStore - 行存储,封装了行记录的 CRUD 操作。

AutoGetDb

MongoDB 在创建 DB 的时候,其实只是在内存中创建了一个 Database 的数据结构,并没有对 storage 做实际的操作,这里讲一下 MongoDB 是如何“创建 Database”的。


// src/mongo/db/catalog/create_collection.cppStatus _createView(OperationContext* opCtx,                   const NamespaceString& nss,                   CollectionOptions&& collectionOptions) {                   // ..... 省略                   AutoGetDb autoDb(opCtx, nss.db(), MODE_IX);                   auto db = autoDb.ensureDbExists();                   // ..... 省略}
复制代码


上面是创建 view 的方法,其中看到通过一个 AutoGetDb 的对象来“创建 Database”,这里涉及到两个 catalog 层的角色,分别是 DatabaseHolderImpl 和 DatabaseImpl。

DatabaseHolderImpl

AutoGetDb 这个对象逻辑比较简单,主要是:


  • 调用 databaseHolder->getDb 从 databaseHolder 获取 Database

  • 如果获取到 Database 为 null,则调用 databaseHolder->openDb


因此我们主要看下 DatabaseHolderImpl::openDb 做了什么。


openDb 其实并没有 open 什么 DB,这一步骤并没有与 storage 层进行过交互,主要做了这些事情


  • 从自身维护的_dbs 查找 DB 是否存在,存在直接返回。

  • 不存在 DB 的时候,创建 DatabaseImpl 对象,调用 DatabaseImpl::init 初始化

  • 将 DatabaseImpl 添加到_dbs 中


下面看看详细逻辑


Database* DatabaseHolderImpl::openDb(OperationContext* opCtx, StringData ns, bool* justCreated) {    // 已经存在db的时候直接返回    if (auto db = _dbs[dbname])        return db;        // .....省略,这里使用锁,将_dbs[dbname]设置为null。    // 因为在创建DB的时候有一个逻辑确保dbname不再_dbs中已经存在,防止并发导致明明检查_dbs[dbname]不存在但是创建的时候发现dbname冲突。        // 这一步切实也是内存操作    if (CollectionCatalog::get(opCtx)->getAllCollectionUUIDsFromDb(dbname).empty()) {        audit::logCreateDatabase(opCtx->getClient(), dbname);        if (justCreated)            *justCreated = true;    }        auto newDb = std::make_unique<DatabaseImpl>(dbname);    newDb->init(opCtx);        auto it = _dbs.find(dbname);    it->second = newDb.release();    return it->second;}
复制代码

DatabaseImpl

在 DatabaseHolderImpl::openDb 中看到,“创建 Database”的时候会调用 DatabaseImpl::init,下面看看 DatabaseImpl 在 init 的时候做了什么。


void DatabaseImpl::init(OperationContext* const opCtx) const {    // 调用validateDBName检查dbname    Status status = validateDBName(_name);        // 从CollectionCatalog中通过dbname找到collections列表(同样内存查找)。如果是新的dbname,这个循环是不会执行的,因为getAllCollectionUUIDsFromDb得到是空列表    auto catalog = CollectionCatalog::get(opCtx);    for (const auto& uuid : catalog->getAllCollectionUUIDsFromDb(_name)) {        CollectionWriter collection(            opCtx,            uuid,            opCtx->lockState()->isW() ? CollectionCatalog::LifetimeMode::kInplace                                      : CollectionCatalog::LifetimeMode::kManagedInWriteUnitOfWork);        invariant(collection);        // If this is called from the repair path, the collection is already initialized.        if (!collection->isInitialized()) {            collection.getWritableCollection()->init(opCtx);        }    }        // ...... 省略,非从库的时候调用ViewCatalog::reload重新加载试图}
复制代码

总结

看到这里应该对 catalog 层和 storage 层有大概了解,后面会开始看一下一些 Command(例如 CreateCollection、CreateIndex)的逻辑。当中也会涉及到 AutoGetDb,由于与传统 Mysql 有区别(不需要创建 Database),因此本篇也讲述了 AutoGetDb。

to be continue

发布于: 刚刚阅读数: 6
用户头像

还未添加个人签名 2020-03-31 加入

还未添加个人简介

评论

发布
暂无评论
MongoDB源码学习:catalog与storage_mongodb_云里有只猫_InfoQ写作社区