levelDB的组件Comparator、Status、Env、Options
levelDB中有很多常用的组件,下面我们来对其进行介绍。
- Comparator : 定义了比较的规则,是一个虚基类。
- Status : 定义函数执行的结果信息。
- Env : 封装系统相关的调用,比如文件操作,线程操作。
- Options : 指定数据库选项。
Comparator
源码位置与说明:
utils/comparator.h
utils/comparator.cc
:
虚基类与BytewiseComparatorImpl类头文件与实现
db/dbformat.h
db/format.cc
:
InternalKeyComparator类头文件与实现
Comparator定义了比较规则,这个是一个虚基类,所以如果想要实现自定义的接口需要实现自己的类。
其中levelDB基于Comparator实现了一些内置比较类:BytewiseComparatorImpl
和InternalKeyComparator
,两者的作用是不同的。
- BytewiseComparatorImpl : 实现key的按二进制来进行比较。
- InternalKeyComparator :
用于内部的key比较器,基于BytewiseComparatorImpl。
由于levelDB是key-value的结构,其中key就是Slice对象。所以BytewiseComparatorImpl用于key之间的比较,这个key称为usr-key。
但是levelDB内部的存储的key结构并不是usr-key,所以需要默认定义两种比较类。
Comparator源码剖析
首先我们来看一下虚基类的定义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class LEVELDB_EXPORT Comparator { public: virtual ~Comparator(); virtual int Compare(const Slice &a, const Slice &b) const = 0;
virtual const char *Name() const = 0;
virtual void FindShortestSeparator(std::string *start, const Slice &limit) const = 0;
virtual void FindShortSuccessor(std::string *key) const = 0; };
|
其中FindShortestSeparator和FindShortSuccessor可能会令人感到疑惑,这两个方法的作用是缩短索引。因为levelDB内部是有序的,假如有这样的两个索引"Aaaaa"和"Acccc",其实我们只需要"Ab"就能将两者分开了;另外,假如一个表中的最后一项是"Yaaaa",我们只需要"Z"就能确定这个项的上界了。这就是这两个函数能减短索引的原理。
接下来我们来看一下BytewiseComparatorImpl类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| class BytewiseComparatorImpl : public Comparator { public: BytewiseComparatorImpl() = default; const char *Name() const override { return "leveldb.BytewiseComparator"; } int Compare(const Slice &a, const Slice &b) const override { return a.compare(b); }
void FindShortestSeparator(std::string *start, const Slice &limit) const override { size_t min_length = std::min(start->size(), limit.size()); size_t diff_index = 0; while ((diff_index < min_length) && ((*start)[diff_index] == limit[diff_index])) { diff_index++; }
if (diff_index >= min_length) { } else { uint8_t diff_byte = static_cast<uint8_t>((*start)[diff_index]); if (diff_byte < static_cast<uint8_t>(0xff) && diff_byte + 1 < static_cast<uint8_t>(limit[diff_index])) { (*start)[diff_index]++; start->resize(diff_index + 1); assert(Compare(*start, limit) < 0); } } }
void FindShortSuccessor(std::string *key) const override { size_t n = key->size(); for (size_t i = 0; i < n; i++) { const uint8_t byte = (*key)[i]; if (byte != static_cast<uint8_t>(0xff)) { (*key)[i] = byte + 1; key->resize(i + 1); return; } } } };
const Comparator *BytewiseComparator() { static NoDestructor<BytewiseComparatorImpl> singleton; return singleton.get(); }
|
这部分比较的规则其实就是我们前面看到的虚基类的具体化。需要注意它存在一个为了保证线程安全的方法,进一步了解可以看此处。
接下来我们来看一下InternalKeyComparator的头文件和实现文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
class InternalKeyComparator : public Comparator { private: const Comparator *user_comparator_;
public: explicit InternalKeyComparator(const Comparator *c) : user_comparator_(c) {} const char *Name() const override; int Compare(const Slice &a, const Slice &b) const override; void FindShortestSeparator(std::string *start, const Slice &limit) const override; void FindShortSuccessor(std::string *key) const override;
const Comparator *user_comparator() const { return user_comparator_; } int Compare(const InternalKey &a, const InternalKey &b) const; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
const char *InternalKeyComparator::Name() const { return "leveldb.InternalKeyComparator"; }
int InternalKeyComparator::Compare(const Slice &akey, const Slice &bkey) const { int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey)); if (r == 0) { const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8); const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8); if (anum > bnum) { r = -1; } else if (anum < bnum) { r = +1; } } return r; }
void InternalKeyComparator::FindShortestSeparator(std::string *start, const Slice &limit) const { Slice user_start = ExtractUserKey(*start); Slice user_limit = ExtractUserKey(limit); std::string tmp(user_start.data(), user_start.size()); user_comparator_->FindShortestSeparator(&tmp, user_limit); if (tmp.size() < user_start.size() && user_comparator_->Compare(user_start, tmp) < 0) { PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek)); assert(this->Compare(*start, tmp) < 0); assert(this->Compare(tmp, limit) < 0); start->swap(tmp); } }
void InternalKeyComparator::FindShortSuccessor(std::string *key) const { Slice user_key = ExtractUserKey(*key); std::string tmp(user_key.data(), user_key.size()); user_comparator_->FindShortSuccessor(&tmp); if (tmp.size() < user_key.size() && user_comparator_->Compare(user_key, tmp) < 0) { PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber, kValueTypeForSeek)); assert(this->Compare(*key, tmp) < 0); key->swap(tmp); } }
inline int InternalKeyComparator::Compare(const InternalKey &a, const InternalKey &b) const { return Compare(a.Encode(), b.Encode()); }
|
我们可以看到InternalKeyComparator的比较其实也是基于usr-key的,不过由于内部key与usr-key存在差异,剩余的代码主要是处理差异的,具体我们先不展开。
Status
源码位置: leveldb/include/status.h
util/status.cc
这个类主要定义了很多操作的返回码,很多操作需要通过返回的status来判断下一步的行为。
Status源码剖析
我们接下来来看一下它的头文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| class LEVELDB_EXPORT Status { private: enum Code { kOk = 0, kNotFound = 1, kCorruption = 2, kNotSupported = 3, kInvalidArgument = 4, kIOError = 5 };
Code code() const { return (state_ == nullptr) ? kOk : static_cast<Code>(state_[4]); } Status(Code code, const Slice &msg, const Slice &msg2); static const char *CopyState(const char *s);
const char *state_;
public: Status() noexcept : state_(nullptr) {} ~Status() { delete[] state_; } Status(const Status &rhs); Status &operator=(const Status &rhs); Status(Status &&rhs) noexcept : state_(rhs.state_) { rhs.state_ = nullptr; } Status &operator=(Status &&rhs) noexcept;
static Status OK() { return Status(); } static Status NotFound(const Slice &msg, const Slice &msg2 = Slice()) { return Status(kNotFound, msg, msg2); } static Status Corruption(const Slice &msg, const Slice &msg2 = Slice()) { return Status(kCorruption, msg, msg2); } static Status NotSupported(const Slice &msg, const Slice &msg2 = Slice()) { return Status(kNotSupported, msg, msg2); } static Status InvalidArgument(const Slice &msg, const Slice &msg2 = Slice()) { return Status(kInvalidArgument, msg, msg2); } static Status IOError(const Slice &msg, const Slice &msg2 = Slice()) { return Status(kIOError, msg, msg2); }
bool ok() const { return (state_ == nullptr); }
bool IsNotFound() const { return code() == kNotFound; } bool IsCorruption() const { return code() == kCorruption; } bool IsIOError() const { return code() == kIOError; } bool IsNotSupportedError() const { return code() == kNotSupported; } bool IsInvalidArgument() const { return code() == kInvalidArgument; }
std::string ToString() const; };
|
由于这个类主要就是处理错误类型的,所以其实现也是非常简单的,此处就不展开了。
不过实现内部有一个小技巧:用memcpy去进行赋值。这个似乎会更快一些,不过我觉得这个还是用于大数组会比较好吧,毕竟针对内存块有SIMD之类的优化。
Env
源码位置:
include/leveldb/env.h
: env相关的接口定义
util/env_posix.cc
util/posix_logger.h
:
Posix系统相关的封装,包括文件操作,文件锁,后台线程创建,Posix写日志
util/env_windows.cc
util/windows_logger.h
:
Windows相关的实现
LevelDB是一个数据库函数库,数据库总是需要操作文件和线程,这就需要做很多系统调用。各个操作系统的系统调用方式不一样,为了跨平台支持,LevelDB对这些系统调用做了一层封装,提供了统一的接口来操作,并且提供了Posix和Windows两种实现,如果需要实现其他的系统,只需要根据系统实现相应的Env即可。
不过由于这些代码实在是太多,而且也就是一些系统调用,所以此处不展开了。
里面感觉有用的就是关于多线程的一个锁的问题,但是也相对比较简单。
Options
源码位置:leveldb/include/options.h
util/options.cc
Options定义了操作数据库的选项,定义了3个struct来操作:
- Options定义打开数据库的选项
- ReadOptions定义读操作相关的选项
- WriteOptions定义写操作相关的选项
同样的,这部分源码也非常简单,也不展开了
总结
编程小技巧
- 可以定义虚基类来规定接口。
- 需要处理线程安全问题。
- memcpy进行数组赋值会更快。