您的位置:首页 > 数据库

leveldb源码分析四

2017-07-26 00:56 836 查看

前言

之前写了一些东西,整体了解了工程的编译脚本和一个test的工程结构,本来想按部就班,慢慢来解决可是咱们还是直接进入主题,直接啃这个数据库的测试程序,看看到底如何实现,

正文

我们直接进入
db_test.cpp
文件,找到第一个test类。

TEST(DBTest, Empty) {
do {
ASSERT_TRUE(db_ != NULL);
ASSERT_EQ("NOT_FOUND", Get("foo"));
} while (ChangeOptions());
}


这个测试其实很简单,就是简单的额测试真实的操作的
db_
代理对象是不是为空,和查找一个键值为k
foo
的值。这里我们必须要找到初始化的代码,看看如何获取代理对象,并且初始化这个类的。

DBTest() : option_config_(kDefault),
env_(new SpecialEnv(Env::Default())) {
filter_policy_ = NewBloomFilterPolicy(10);
dbname_ = test::TmpDir() + "/db_test";
DestroyDB(dbname_, Options());
db_ = NULL;
Reopen();
}


这里我们记住初始化了几个变量,这里暂时不详细介绍,关于
dbname_
,这个我们稍微介绍下,具体获取代码如下

virtual Status GetTestDirectory(std::string* result) {
const char* env = getenv("TEST_TMPDIR");
if (env && env[0] != '\0') {
*result = env;
} else {
char buf[100];
snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid()));
*result = buf;
}
// Directory may already exist
CreateDir(*result);
return Status::OK();
}

static void InitDefaultEnv() { default_env = new PosixEnv; }
Env* Env::Default() {
pthread_once(&once, InitDefaultEnv);
return default_env;
}

std::string TmpDir() {
std::string dir;
Status s = Env::Default()->GetTestDirectory(&dir);
ASSERT_TRUE(s.ok()) << s.ToString();
return dir;
}


这里的
getenv("TEST_TMPDIR")
为空,所以基本就是
/tmp/leveldbtest-%d
,这里根据uid得到唯一的一个数据库。不过觉得,因为单次,可以删除自己,所以肯定不会重合。
Env::Default()
这个其实是防止并发,这里返回时一个
PosixEnv
,这是一个类似代理模式的设计,这里就不详细介绍,其实就是为了得到一个唯一的文件目录。

关键是最后三句话,这才是初始化db_的语句,我们好好看看:

Status DestroyDB(const std::string& dbname, const Options& options) {
Env* env = options.env;
std::vector<std::string> filenames;
// Ignore error in case directory does not exist
env->GetChildren(dbname, &filenames);
if (filenames.empty()) {
return Status::OK();
}

FileLock* lock;
const std::string lockname = LockFileName(dbname);
Status result = env->LockFile(lockname, &lock);
if (result.ok()) {
uint64_t number;
FileType type;
for (size_t i = 0; i < filenames.size(); i++) {
if (ParseFileName(filenames[i], &number, &type) &&
type != kDBLockFile) {  // Lock file will be deleted at end
Status del = env->DeleteFile(dbname + "/" + filenames[i]);
if (result.ok() && !del.ok()) {
result = del;
}
}
}
env->UnlockFile(lock);  // Ignore error since state is already gone
env->DeleteFile(lockname);
env->DeleteDir(dbname);  // Ignore error in case dir contains other files
}
return result;
}


传入的参数是个Options的结构体。这里保存一个Env::Default()结构。关于这里删除必要的文件,这里加锁防止并发,并且具有保护措施,其实挺好的,这里就不详细介绍,有兴趣的可以自己阅读。

Status TryReopen(Options* options) {
delete db_;
db_ = NULL;
Options opts;
if (options != NULL) {
opts = *options;
} else {
opts = CurrentOptions();
opts.create_if_missing = true;
}
last_options_ = opts;

return DB::Open(opts, dbname_, &db_);
}

Status DB::Open(const Options& options, const std::string& dbname,
DB** dbptr) {
*dbptr = NULL;

//这里是常见一个工作类。
DBImpl* impl = new DBImpl(options, dbname);
impl->mutex_.Lock();
VersionEdit edit;
// Recover handles create_if_missing, error_if_exists
bool save_manifest = false;
Status s = impl->Recover(&edit, &save_manifest);
if (s.ok() && impl->mem_ == NULL) {
// Create new log and a corresponding memtable.
uint64_t new_log_number = impl->versions_->NewFileNumber();
WritableFile* lfile;
s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),
&lfile);
if (s.ok()) {
edit.SetLogNumber(new_log_number);
impl->logfile_ = lfile;
impl->logfile_number_ = new_log_number;
impl->log_ = new log::Writer(lfile);
impl->mem_ = new MemTable(impl->internal_comparator_);
impl->mem_->Ref();
}
}
if (s.ok() && save_manifest) {
edit.SetPrevLogNumber(0);  // No older logs needed after rec
c4f3
overy.
edit.SetLogNumber(impl->logfile_number_);
s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
}
if (s.ok()) {
impl->DeleteObsoleteFiles();
impl->MaybeScheduleCompaction();
}
impl->mutex_.Unlock();
if (s.ok()) {
assert(impl->mem_ != NULL);
*dbptr = impl;
} else {
delete impl;
}
return s;
}


这里其实就是调用了
Open
函数,这里才是初始化整个数据库的核心地方,这里因为我们还没有存入数据,所以这里仅仅初始化了一个log的文件,但是经过观察 ,貌似还没创建文件,这里就不详细介绍了。

也就是高了一个
DBImpl
类,然后就没有然后了。这里初始化基本完成了,还剩下一个最终的问题,

ASSERT_TRUE(db_ != NULL);
ASSERT_EQ("NOT_FOUND", Get("foo"));


这里很容易搞懂啦!第一个就不用多说,第二个,我们看下

std::string Get(const std::string& k, const Snapshot* snapshot = NULL) {
ReadOptions options;
options.snapshot = snapshot;
std::string result;
Status s = db_->Get(options, k, &result);
if (s.IsNotFound()) {
result = "NOT_FOUND";
} else if (!s.ok()) {
result = s.ToString();
}
return result;
}


这里我们很容易知道取得当然是空,我们这里不详细介绍获取的内容,因为我们还没阅读添加字段的代码,阅读这里觉得没太大意义。

后记

有是一天,又是半篇,进度总是没自己想的快,不过至少还在前行,希望快速阅读完这个代码,带着问题,去看下书,解决下自己的语法的问题,希望自己快速掌握c++。加油。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据库 源码