简介
为了方便的继续往下继续学习 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.h
class 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.h
class StorageEngine {
public:
virtual std::vector<std::string> listDatabases() const = 0;
virtual KVEngine* getEngine() = 0;
virtual DurableCatalog* getCatalog() = 0;
}
// durable_catalog.h
class DurableCatalog {
public:
virtual RecordStore* getRecordStore() = 0;
virtual StatusWith<std::pair<RecordId, std::unique_ptr<RecordStore>>> createCollection(...) = 0;
virtual Status createIndex(...) = 0;
}
// record_store.h
class 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.cpp
Status _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
评论