简介
为了方便的继续往下继续学习 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 个对象的意思了:
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 这个对象逻辑比较简单,主要是:
因此我们主要看下 DatabaseHolderImpl::openDb 做了什么。
openDb 其实并没有 open 什么 DB,这一步骤并没有与 storage 层进行过交互,主要做了这些事情
下面看看详细逻辑
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
评论