imtoken唯一官网|leveldb

作者: imtoken唯一官网
2024-03-07 19:37:40

Leveldb 基本介绍和使用指南 - 知乎

Leveldb 基本介绍和使用指南 - 知乎首发于分布式系统之美切换模式写文章登录/注册Leveldb 基本介绍和使用指南多颗糖leveldb 是一个持久化的 key/value 存储,key 和 value 都是任意的字节数组(byte arrays),并且在存储时,key 值根据用户指定的 comparator 函数进行排序。作者是大名鼎鼎的 Jeff Dean 和 Sanjay Ghemawat.1. 基本介绍1.1 特性keys 和 values 是任意的字节数组。数据按 key 值排序存储。调用者可以提供一个自定义的比较函数来重写排序顺序。提供基本的 Put(key,value),Get(key),Delete(key) 操作。多个更改可以在一个原子批处理中生效。用户可以创建一个瞬时快照(snapshot),以获得数据的一致性视图。在数据上支持向前和向后迭代。使用 Snappy 压缩库对数据进行自动压缩与外部交互的操作都被抽象成了接口(如文件系统操作等),因此用户可以根据接口自定义的操作系统交互。1.2 局限性这不是一个 SQL 数据库,它没有关系数据模型,不支持 SQL 查询,也不支持索引。同时只能有一个进程(可能是具有多线程的进程)访问一个特定的数据库。该程序库没有内置的 client-server 支持,有需要的用户必须自己封装。1.3 性能下面是运行 db_bench 程序的性能报告。结果有一些噪声(noisy),但足以得到一个大概的性能估计。1.3.1 配置我们使用的是一个有一百万个条目的数据库,其中每个条目的 key 是 16 字节,value 是 100 字节,value 压缩后大约是原始大小的一半,测试配置如下:LevelDB: version 1.1

Date: Sun May 1 12:11:26 2011

CPU: 4 x Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz

CPUCache: 4096 KB

Keys: 16 bytes each

Values: 100 bytes each (50 bytes after compression)

Entries: 1000000

Raw Size: 110.6 MB (estimated)

File Size: 62.9 MB (estimated)

1.3.2 写性能“fill” 基准测试创建了一个全新的数据库,以顺序(下面的 “fillseq”)或者随机(下面的 “fillrandom”)方式写入。“fillsync” 基准测试每次写操作都将数据从操作系统刷到磁盘; 其它的操作会将数据保存在系统中一段时间。“overwrite” 基准测试做随机写,会更新数据库中已有的 key。fillseq : 1.765 micros/op; 62.7 MB/s

fillsync : 268.409 micros/op; 0.4 MB/s (10000 ops)

fillrandom : 2.460 micros/op; 45.0 MB/s

overwrite : 2.380 micros/op; 46.5 MB/s

上述每个 “op” 对应一个 key/value 对的写操作。即,一个随机写基准测试每秒约四十万次写操作(1,000,000/2.46)。每个 “fillsync” 操作耗时(大约 0.3 毫秒)少于一次磁盘搜索(大约 10 毫秒)。我们怀疑这是因为磁盘本身将更新操作缓存到了内存,并在数据落盘之前返回响应。这可能是安全的,也可能是不安全的,取决于硬盘是否有足够的电力在断电时保存其内存。1.3.3 读性能我们列出了正向顺序读、反向顺序读以及随机查询的性能。注意,基础测试创建的数据库很小,因此该性能报告描述的是 leveldb 的全部数据集能放入到内存的场景,如果数据不在操作系统缓存中,读取的性能消耗主要在于一到两次的磁盘搜索,写性能基本不会受数据集是否能放入内存的影响。readrandom : 16.677 micros/op; (approximately 60,000 reads per second)

readseq : 0.476 micros/op; 232.3 MB/s

readreverse : 0.724 micros/op; 152.9 MB/s

leveldb 会在后台 compact 其底层存储的数据来改善读性能。上面列出的结果是在大量随机写操作后得出的,经过 compact 后的性能指标(通常是指动出发的)会更好:readrandom : 11.602 micros/op; (approximately 85,000 reads per second)

readseq : 0.423 micros/op; 261.8 MB/s

readreverse : 0.663 micros/op; 166.9 MB/s

读操作消耗高的成本一部分来自于重复解压从磁盘读取的数据库,如果我们能够提供足够的缓存给 leveldb 来将解压后的数据保存在内存中,读性能会进一步改善:readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction)

readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction)

1.4 编译项目支持 Cmake 开箱即用。编译非常简单:git clone --recurse-submodules https://github.com/google/leveldb.git

mkdir -p build && cd build

cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .1.5 头文件介绍leveldb 对外暴露的接口都在 include/*.h 中,用户不应该依赖任何其它目录下的头文件,这些内部 API 可能会在没有警告的情况下被改变。include/leveldb/db.h:主要的 DB 接口,从这开始。include/leveldb/options.h: 控制数据库的行为,也控制当个读和写的行为。include/leveldb/comparator.h: 比较函数的抽象。如果你只想对 key 逐字节比较,可以直接使用默认的比较器。如果你想要自定义排序(例如处理不同的字符编码、解码等),可以实现自己的比较器。include/leveldb/iterator.h:迭代数据的接口,你可以从一个 DB 对象获取到一个迭代器。include/leveldb/write_batch.h:原子地将多个操作应用到数据库。include/leveldb/slice.h:类似 string,维护着指向字节数组的指针和对应的长度。include/leveldb/status.h:许多公共接口都会返回 Status,用于报告成功或各种错误。include/leveldb/env.h:操作系统环境的抽象,该接口的 posix 实现位于 util/env_posix.cc 中.include/leveldb/table.h, include/leveldb/table_builder.h:底层的模块,大多数用户可能不会直接用到。2 使用编译以后我们可以使用 cmake 来小试牛刀,首先在 leveldb 目录创建文件夹 app/ 来单独存放我们的练习文件,然后创建一个文件例如:main.cc,接着我们修改 CMakeLists.txt 文件,增加一行: 347 if(NOT BUILD_SHARED_LIBS)

+ 348 leveldb_test("app/main.cc")

349 leveldb_test("db/autocompact_test.cc")

编写完代码后,只需回到 build/ 目录执行:cmake .. && cmake --build .即可编译出 main 可执行文件。2.1 打开一个数据库leveldb 数据库都有一个名字,该名字对应文件系统上的一个目录,该数据库内容全都存在该目录下。下面的例子显示了如何打开一个数据库,必要时创建数据库:#include

#include "leveldb/db.h"

int main() {

leveldb::DB* db;

leveldb::Options options;

options.create_if_missing = true;

leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);

assert(status.ok());

}

如果你想在数据库已存在的时候触发一个异常,将下面这行配置加到 leveldb::DB::Open 调用之前:options.error_if_exists = true;

2.2 Status你也许注意到上面的 leveldb::Status 返回类型,leveldb 中大部分方法在遇到错误的时候会返回该类型的值,你可以检查它是否 ok,然后打印相关的错误信息:leveldb::Status s = ...;

if (!s.ok()) cerr << s.ToString() << endl;

尝试输出数据库已存在的错误信息吧!2.3 关闭数据库当数据库不再使用的时候,像下面这样直接删除数据库对象就可以了:delete db;

是不是很简单?后面我们具体源码分析时会看到,DB 类是基于 RAII 实现的,在 delete 时会触发析构函数自动清理。2.4 数据库读写leveldb 提供了 Put、Delete 和 Get 方法来修改/查询数据库,下面的代码展示了将 key1 对应的 value 移动到 key2 下。std::string value;

leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);

if (s.ok()) s = db->Put(leveldb::WriteOptions(), key2, value);

if (s.ok()) s = db->Delete(leveldb::WriteOptions(), key1);

2.5 原子更新需要注意的是,在上一小节中如果进程在 Put key2 后 Delete key1 之前挂了,那么同样的 value 将被存储在多个 key 下。可以通过使用 WriteBatch 原子地应用一组操作来避免类似的问题。#include "leveldb/write_batch.h"

...

std::string value;

leveldb::Status s = db->Get(leveldb::ReadOptions(), key1, &value);

if (s.ok()) {

leveldb::WriteBatch batch;

batch.Delete(key1);

batch.Put(key2, value);

s = db->Write(leveldb::WriteOptions(), &batch);

}

WriteBatch 保存着一系列将被应用到数据库的操作,这些操作会按照添加的顺序依次被执行。注意,我们先执行 Delete 后执行 Put,这样如果 key1 和 key2 一样的情况下我们也不会错误地丢失数据。除了原子性,WriteBatch 也能加速更新过程,因为可以把一大批独立的操作添加到同一个 batch 中然后一次性执行。2.6 同步写操作默认情况下,leveldb 每个写操作都是异步的:进程把要写的内容丢给操作系统后立即返回,从操作系统内存到底层持久化存储的传输是异步进行的。可以为某个特定的写操作打开同步标识:write_options.sync = true,以等到数据真正被记录到持久化存储后再返回(在 Posix 系统上,这是通过在写操作返回前调用 fsync(...) 或 fdatasync(...) 或 msync(..., MS_SYNC) 来实现的)。leveldb::WriteOptions write_options;

write_options.sync = true;

db->Put(write_options, ...);

异步写通常比同步写快 1000 倍。异步写的缺点是,一旦机器崩溃可能会导致最后几个更新操作丢失。注意,仅仅是写进程崩溃(而非机器重启)不会造成任何损失,因为哪怕 sync 标识为 false,在进程退出之前,写操作也已经从进程内存推到了操作系统。异步写通常可以安全使用。比如你要将大量的数据写入数据库,如果丢失了最后几个更新操作,也可以重做整个写过程。如果数据量非常大,一个优化点是采用混合方案,每进行 N 个异步写操作则进行一次同步写,如果期间发生了崩溃,重启从上一个成功的同步写操作开始即可。(同步写操作可以同时更新一个标识,描述崩溃时重启的位置)WriteBatch 可以作为异步写操作的替代品,多个更新操作可以放到同一个 WriteBatch 中然后通过一次同步写(即 write_options.sync = true)一起落盘。2.7 并发一个数据库同时只能被一个进程打开。leveldb 会从操作系统获取一把锁来防止多进程同时打开同一个数据库。在单个进程中,同一个 leveldb::DB 对象可以被多个并发线程安全地使用,也就是说,不同的线程可以在不需要任何外部同步原语的情况下,写入、获取迭代器或者调用 Get(leveldb 实现会确保所需的同步)。但是其它对象,比如 Iterator 或者 WriteBatch 需要外部自己提供同步保证,如果两个线程共享此类对象,需要使用自己的锁进行互斥访问。具体见对应的头文件。2.8 迭代数据库下面的用例展示了如何打印数据库中全部的 (key, value) 对。leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());

for (it->SeekToFirst(); it->Valid(); it->Next()) {

cout << it->key().ToString() << ": " << it->value().ToString() << endl;

}

assert(it->status().ok()); // Check for any errors found during the scan

delete it;

下面的用例展示了如何打印 [start, limit) 范围内的数据:for (it->Seek(start);

it->Valid() && it->key().ToString() < limit;

it->Next()) {

...

}

当然你也可以反向遍历(注意,反向遍历可能要比正向遍历慢一些,具体见前面的读性能基准测试):for (it->SeekToLast(); it->Valid(); it->Prev()) {

...

}

2.9 快照快照提供了针对整个 KV 存储的一致性只读视图(consistent read-only views)。ReadOptions::snapshot 不为 null 表示读操作应该作用在 DB 的某个特定版本上;若为 null,则读操作将会作用在当前版本的一个隐式的快照上。快照通过调用 DB::GetSnapshot() 方法创建:leveldb::ReadOptions options;

options.snapshot = db->GetSnapshot();

... apply some updates to db ...

leveldb::Iterator* iter = db->NewIterator(options);

... read using iter to view the state when the snapshot was created ...

delete iter;

db->ReleaseSnapshot(options.snapshot);

注意,当一个快照不再使用的时候,应该通过 DB::ReleaseSnapshot 接口进行释放。2.10 Sliceit->key() 和 it->value() 调用返回的值是 leveldb::Slice 类型。熟悉 Go 的同学应该对 Slice 不陌生。Slice 是一个简单的数据结构,包含一个长度和一个指向外部字节数组的指针,返回一个 Slice 比返回一个 std::string 更高效,因为不需要隐式地拷贝大量的 keys 和 values。另外,leveldb 方法不返回 \0 截止符结尾的 C 风格字符串,因为 leveldb 的 keys 和 values 允许包含 \0 字节。C++ 风格的 string 和 C 风格的空字符结尾的字符串很容易转换为一个 Slice:leveldb::Slice s1 = "hello";

std::string str("world");

leveldb::Slice s2 = str;

一个 Slice 也很容易转换回 C++ 风格的字符串:std::string str = s1.ToString();

assert(str == std::string("hello"));

在使用 Slice 时要小心,要由调用者来确保 Slice 指向的外部字节数组有效。例如,下面的代码就有 bug :leveldb::Slice slice;

if (...) {

std::string str = ...;

slice = str;

}

Use(slice);

当 if 语句结束的时候,str 将会被销毁,Slice 的指向也随之消失,后面再用就会出问题。2.11 比较器(Comparator)前面的例子中用的都是默认的比较函数,即逐字节按字典序比较。你可以自定义比较函数,然后在打开数据库的时候传入,只需要继承 leveldb::Comparator 然后定义相关逻辑即可,下面是一个例子:class TwoPartComparator : public leveldb::Comparator {

public:

// Three-way comparison function:

// if a < b: negative result

// if a > b: positive result

// else: zero result

int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const {

int a1, a2, b1, b2;

ParseKey(a, &a1, &a2);

ParseKey(b, &b1, &b2);

if (a1 < b1) return -1;

if (a1 > b1) return +1;

if (a2 < b2) return -1;

if (a2 > b2) return +1;

return 0;

}

// Ignore the following methods for now:

const char* Name() const { return "TwoPartComparator"; }

void FindShortestSeparator(std::string*, const leveldb::Slice&) const {}

void FindShortSuccessor(std::string*) const {}

};

在打开数据库的时候,传入上面定义的比较器:// 实例化比较器

TwoPartComparator cmp;

leveldb::DB* db;

leveldb::Options options;

options.create_if_missing = true;

// 将比较器赋值给 options.comparator

options.comparator = &cmp;

// 打开数据库

leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);

...

2.12 向后兼容性比较器 Name() 方法返回的结果在创建数据库时会被绑定到数据库上,后续每次打开都会进行检查,如果名称改了,对 leveldb::DB::Open 的调用就会失败。因此,当且仅当在新的 key 格式和比较函数与已有的数据库不兼容而且已有数据不再被需要的时候再修改比较器名称。总而言之,一个数据库只能对应一个比较器,而且比较器由名字唯一确定,一旦修改名称或比较器逻辑,数据库的操作逻辑统统会出错,毕竟 leveldb 是一个有序的 KV 存储。如果非要修改比较逻辑呢?你可以根据预先规划一点一点的演进你的 key 格式,注意,事先的演进规划非常重要。比如,你可以在每个 key 的结尾存储一个版本号(大多数场景,一个字节足矣),当你想要切换到新的 key 格式的时候(比如上面的例子 TwoPartComparator处理的 keys 中),那么需要做的是:保持相同的比较器名称递增新 keys 的版本号修改比较器函数以让其使用版本号来决定如何进行排序2.13 性能调优通过修改 include/leveldb/options.h 中定义的类型的默认值来对 leveldb 的性能进行调优。2.13.1 Block 大小leveldb 把相邻的 keys 组织在同一个 block 中(具体见后续文章针对 sstable 文件格式的描述),block 是数据在内存和持久化存储传输之间的基本单位。默认的未压缩 block 大小大约为 4KB,经常批量扫描大量数据的应用可能希望把这个值调大,而针对数据只做“点读”的应用则可能希望这个值小一些。但是,没有证据表明该值小于 1KB 或者大于几个 MB 的时候性能会表现得更好。另外要注意的是,使用较大的 block size,压缩效率会更高效。2.13.2 压缩每个 block 在写入持久化存储之前都会被单独压缩。压缩默认是开启的,因为默认的压缩算法非常快,而且对于不可压缩的数据会自动关闭压缩功能,极少有场景会让用户想要完全关闭压缩功能,除非基准测试显示关闭压缩会显著改善性能。按照下面方式即可关闭压缩功能:leveldb::Options options;

options.compression = leveldb::kNoCompression;

... leveldb::DB::Open(options, name, ...) ....

2.13.3 缓存数据库的内容存储在文件系统中的一组文件中,每个文件都存储了一系列压缩后的 blocks,如果 options.block_cache 是非 NULL,则用于缓存经常使用的已解压缩 block 内容。#include "leveldb/cache.h"

leveldb::Options options;

options.block_cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache

leveldb::DB* db;

leveldb::DB::Open(options, name, &db);

... use the db ...

delete db

delete options.block_cache;

注意 cache 保存的是未压缩的数据,因此应该根据应用程序所需的数据大小来设置它的大小。(已压缩数据的缓存工作交给操作系统的 buffer cache 或者用户自定义的 Env 实现去干。)当执行一个大块数据读操作时,应用程序可能想要取消缓存功能,这样读进来的大块数据就不会导致当前 cache 中的大部分数据被置换出去,我们可以为它提供一个单独的 iterator 来达到该目的:leveldb::ReadOptions options;

options.fill_cache = false;

leveldb::Iterator* it = db->NewIterator(options);

for (it->SeekToFirst(); it->Valid(); it->Next()) {

...

}

2.13.4 Key 的布局注意,磁盘传输和缓存的单位都是一个 block,相邻的 keys(已排序)总在同一个 block 中,因此应用可以通过把需要一起访问的 keys 放在一起,同时把不经常使用的 keys 放到一个独立的键空间区域来提升性能。举个例子,假设我们正基于 leveldb 实现一个简单的文件系统。我们打算存储到这个文件系统的数据类型如下:filename -> permission-bits, length, list of file_block_ids

file_block_id -> data

我们可以给上面表示 filename 的 key 增加一个字符前缀,例如 '/',然后给表示 file_block_id 的 key 增加另一个不同的前缀,例如 '0',这样这些不同用途的 key 就具有了各自独立的键空间区域,扫描元数据的时候我们就不用读取和缓存大块文件内容数据了。2.13.5 过滤器鉴于 leveldb 数据在磁盘上的组织形式,一次 Get() 调用可能涉及多次磁盘读操作,可配置的 FilterPolicy 机制可以用来大幅减少磁盘读次数。leveldb::Options options;

// 设置启用基于布隆过滤器的过滤策略

options.filter_policy = NewBloomFilterPolicy(10);

leveldb::DB* db;

// 用该设置打开数据库

leveldb::DB::Open(options, "/tmp/testdb", &db);

... use the database ...

delete db;

delete options.filter_policy;

上述代码将一个基于布隆过滤器的过滤策略与数据库进行了关联,基于布隆过滤器的过滤方式依赖于如下事实,在内存中保存每个 key 的部分位(在上面例子中是 10 位,因为我们传给 NewBloomFilterPolicy 的参数是 10),这个过滤器将会使得 Get() 调用中非必须的磁盘读操作大约减少 100 倍,每个 key 用于过滤器的位数增加将会进一步减少读磁盘次数,当然也会占用更多内存空间。我们推荐数据集无法全部放入内存同时又存在大量随机读的应用设置一个过滤器策略。如果你在使用自定义的比较器,应该确保你在用的过滤器策略与你的比较器兼容。举个例子,如果一个比较器在比较 key 的时候忽略结尾的空格,那么 NewBloomFilterPolicy 一定不能与此比较器共存。相反,应用应该提供一个自定义的过滤器策略,而且它也应该忽略 key 的尾部空格,示例如下:class CustomFilterPolicy : public leveldb::FilterPolicy {

private:

FilterPolicy* builtin_policy_;

public:

CustomFilterPolicy() : builtin_policy_(NewBloomFilterPolicy(10)) {}

~CustomFilterPolicy() { delete builtin_policy_; }

const char* Name() const { return "IgnoreTrailingSpacesFilter"; }

void CreateFilter(const Slice* keys, int n, std::string* dst) const {

// Use builtin bloom filter code after removing trailing spaces

std::vector trimmed(n);

for (int i = 0; i < n; i++) {

trimmed[i] = RemoveTrailingSpaces(keys[i]);

}

return builtin_policy_->CreateFilter(&trimmed[i], n, dst);

}

};

当然也可以自己提供非基于布隆过滤器的过滤器策略,具体见 leveldb/filter_policy.h。2.14 校验和(Checksums)leveldb 将校验和与它存储在文件系统中的所有数据进行关联,对于这些校验和,有两个独立的控制:ReadOptions::verify_checksums 可以设置为 true,以强制对所有从文件系统读取的数据进行校验。默认为 false,即,不会进行这样的校验。Options::paranoid_checks 在数据库打开之前设置为 true ,以使得数据库一旦检测到数据损毁立即报错。根据数据库损坏的部位,报错可能是在打开数据库后,也可能是在后续执行某个操作的时候。该配置默认是关闭状态,即,持久化存储部分损坏数据库也能继续使用。如果数据库损坏了(当开启 Options::paranoid_checks 的时候可能就打不开了),leveldb::RepairDB() 函数可以用于对尽可能多的数据进行修复。2.15 近似空间大小GetApproximateSizes 方法用于获取一个或多个键区间占据的文件系统近似大小(单位, 字节)leveldb::Range ranges[2];

ranges[0] = leveldb::Range("a", "c");

ranges[1] = leveldb::Range("x", "z");

uint64_t sizes[2];

db->GetApproximateSizes(ranges, 2, sizes);

上述代码结果是,size[0] 保存 [a..c) 区间对应的文件系统大致字节数。size[1] 保存 [x..z) 键区间对应的文件系统大致字节数。2.16 环境变量由 leveldb 发起的全部文件操作以及其它的操作系统调用最后都会被路由给一个 leveldb::Env 对象。用户也可以提供自己的 Env 实现以达到更好的控制。比如,如果应用程序想要针对 leveldb 的文件 IO 引入一个人工延迟以限制 leveldb 对同一个系统中其它应用的影响:// 定制自己的 Env

class SlowEnv : public leveldb::Env {

... implementation of the Env interface ...

};

SlowEnv env;

leveldb::Options options;

// 用定制的 Env 打开数据库

options.env = &env;

Status s = leveldb::DB::Open(options, ...);

2.17 可移植如果某个特定平台提供 leveldb/port/port.h 导出的类型/方法/函数实现,那么 leveldb 可以被移植到该平台上,更多细节见 leveldb/port/port_example.h。另外,新平台可能还需要一个新的默认的 leveldb::Env 实现。具体可参考 leveldb/util/env_posix.h 实现。编辑于 2020-12-01 13:48数据库LevelDB文件系统​赞同 87​​4 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录分布式系统之美分布式系统爱好者的

既生 Redis 何生 LevelDB ? - 知乎

既生 Redis 何生 LevelDB ? - 知乎首发于码洞切换模式写文章登录/注册既生 Redis 何生 LevelDB ?老钱Good news everyone!了解 Redis 的同学都知道它是一个纯内存的数据库,凭借优秀的并发和易用性打下了互联网项的半壁江山。Redis 之所以高性能是因为它的纯内存访问特性,而这也成了它致命的弱点 —— 内存的成本太高。所以在绝大多数场合,它比较适合用来做缓存,长期不被访问的冷数据被淘汰掉,只有热的数据缓存在内存中,这样就不会浪费太多昂贵的内存空间。但是 Redis 的诱惑太大了,用它来做持久存储使用起来太方便了。要是内存的价格低廉,真恨不得把所有的数据都堆到 Redis 中,但是技术的选择总是要考虑到现实世界的成本问题。那如何才能享受到 Redis 作为持久层易用性的同时还可以节省内存成本呢?LevelDB 来了!它是 Google 开源的 NOSQL 存储引擎库,是现代分布式存储领域的一枚原子弹。在它的基础之上,Facebook 开发出了另一个 NOSQL 存储引擎库 RocksDB,沿用了 LevelDB 的先进技术架构的同时还解决了 LevelDB 的一些短板。你可以将 RocksDB 比喻成氢弹,它比 LevelDB 的威力更大一些。现代开源市场上有很多数据库都在使用 RocksDB 作为底层存储引擎,比如大名鼎鼎的 TiDB。但是为什么我要讲 LevelDB 而不是 RocksDB 呢?其原因在于 LevelDB 技术架构更加简单清晰易于理解。如果我们先把 LevelDB 吃透了再去啃一啃 RocksDB 就会非常好懂了,RocksDB 也只是在 LevelDB 的基础上添砖加瓦进行了一系列优化而已。等到我们攻破了 RocksDB 这颗氢弹,TiDB 核动力宇宙飞船已经在前方不远处等着我们了。Redis 缓存有什么问题?当我们将 Redis 拿来做缓存用时,背后肯定还有一个持久层数据库记录了全量的冷热数据。Redis 和持久层数据库之间的数据一致性是由应用程序自己来控制的。应用程序会优先去缓存中获取数据,当缓存中没有数据时,应用程序需要从持久层加载数据,然后再放进缓存中。当数据更新发生时,需要将缓存置为失效。function getUser(String userId) User {

User user = redis.get(userId);

if user == null {

user = db.get(userId);

if user != null {

redis.set(userId, user);

}

}

return user;

}

function updateUser(String userId, User user) {

db.update(userId, user);

redis.expire(userId);

}

有过这方面开发经验的朋友们就知道写这样的代码还是挺繁琐的,所有的涉及到缓存的业务代码都需要加上这一部分逻辑。严格来说我们还需要仔细考虑缓存一致性问题,比如在 updateUser 方法中,数据库正确执行了更新,但是缓存 redis 因为网络抖动等原因置为失效没有成功,那么缓存中的数据就成了过期数据。如果你将设置缓存和更新持久存的先后顺序反过来,也还是会有其它问题,这个读者可以自行思考一下。在多进程高并发场合也会导致缓存不一致,比如一个进程对某个 userId 调用 getUser() 方法,因为缓存里没有,它需要从数据库里加载。结果刚刚加载出来,正准备要设置缓存,这时候发生了内存 fullgc 代码暂停了一会,而正在此时另一个进程调用了 updateUser 方法更新了数据库,将缓存置为失效(其实缓存里本来就没有数据)。然后前面那个进程终于 fullgc 结束要开始设置缓存了,这时候进缓存的就是过期的数据。LevelDB 是如何解决的?LevelDB 将 Redis 缓存和持久层合二为一,一次性帮你搞定缓存和持久层。有了 LevelDB,你的代码可以简化成下面这样function getUser(String userId) User {

return leveldb.get(userId);

}

function updateUser(String userId, User user) {

leveldb.set(userId, user);

}

而且你再也不用当心缓存一致性问题了,LevelDB 的数据更新要么成功要么不成功,不存在中间薛定谔状态。LevelDB 的内部已经内置了内存缓存和持久层的磁盘文件,用户完全不用操心内部是数据如何保持一致的。LevelDB 具体是什么?前面我们说道它是一个 NOSQL 存储引擎,它和 Redis 不是一个概念。Redis 是一个完备的数据库,而 LevelDB 它只是一个引擎。如果将数据库比喻成一辆高级跑车,那么存储引擎就是它的发动机,是核心是心脏。有了这个发动机,我们再给它包装上一系列的配件和装饰,就可以成为数据库。不过也不要小瞧了配件和装饰,做到极致那也是非常困难,将 LevelDB 包装成一个简单易用的数据库需要加上太多太多精致的配件。LevelDB 和 RocksDB 出来这么多年,能够在它的基础上做出非常一个完备的生产级数据库寥寥无几。在使用 LevelDB 时,我们还可以将它看成一个 Key/Value 内存数据库。它提供了基础的 Get/Set API,我们在代码里可以通过这个 API 来读写数据。你还可以将它看成一个无限大小的高级 HashMap,我们可以往里面塞入无限条 Key/Value 数据,只要磁盘可以装下。正是因为它只能算作一个内存数据库,它里面装的数据无法跨进程跨机器共享。在分布式领域,LevelDB 要如何大显身手呢?这就需要靠包装技术了,在 LevelDB 内存数据库的基础上包装一层网络 API。当不同机器上不同的进程要来访问它时,都统一走网络 API 接口。这样就形成了一个简易的数据库。如果在网络层我们使用 Redis 协议来包装,那么使用 Redis 的客户端就可以读写这个数据库了。如果要考虑数据库的高可用性,我们在上面这个单机数据库的基础上再加上主从复制功能就可以变身成为一个主从结构的分布式 NOSQL 数据库。在主从数据库前面加一层转发代理(负载均衡器如 LVS、F5 等),就可以实现主从的实时切换。如果你需要的数据容量特别大以至于单个机器的硬盘都容不下,这时候就需要数据分片机制将整个数据库的数据分散到多台机器上,每台机器只负责一部分数据的读写工作。数据分片的方案非常多,可以像 Codis 那样通过转发代理来分片,也可以像 Redis-Cluster 那样使用客户端转发机制来分片,还可以使用 TiDB 的 Raft 分布式一致性算法来分组管理分片。最简单最易于理解的还是要数 Codis 的转发代理分片。当数据量继续增长需要新增节点时,就必须将老节点上的数据部分迁移到新节点上,管理数据的均衡和迁移的又是一个新的高级配件 —— 数据均衡器。看到这里读者应该可以从整体上理解了分布式数据库中 LevelDB 所处的地位。下一节我们开始全面了解一下 LevelDB 的内存数据库特性。编辑于 2019-01-11 12:56LevelDBRocksDBRedis​赞同 744​​56 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录码洞码出

[LevelDB] 开篇:初学乍练——LevelDB初识 - 知乎

[LevelDB] 开篇:初学乍练——LevelDB初识 - 知乎首发于LevelDB源码剖析切换模式写文章登录/注册[LevelDB] 开篇:初学乍练——LevelDB初识zw Huang开篇:初学乍练——LevelDB初识LevelDB是一个键值对数据库,是一个持久化的有序的Map,由Google传奇工程师Jeff Dean和Sanjay Ghemawat开发并开源。LevelDB实际上不是一个功能完备的数据库,而只是一个数据库函数库,提供了接口来操作数据库。LevelDB的代码只有1万多行,精致优雅,可读性高,是LSM Tree的一种实现方式,值得学习。LevelDB非常简单,实际工程中使用不多,一般会选择RocksDB,它对LevelDB进行了改进,比LevelDB更加强大,比如TiDB就使用了RocksDB作为了底层存储引擎。但是LevelDB技术架构更加简单清晰易于理解,非常适合学习使用。本系列将介绍LevelDB的内部实现,包括源码分析、工作原理,通过这个系列的学习,可以了解LSM Tree的一些概念,一种实现方式,了解LevelDB的工作原理。本系列分为4个部分:概述部分:介绍LevelDB有哪些功能,基本工作原理,编译安装等;基础部分:介绍LevelDB用到了哪些基础组件,这些组件的实现方式;存储部分:介绍LevelDB存储数据相关,包括SSTable、MemTable、WAL和Iterator等,重点阐述数据的存储方式;数据库部分:介绍数据插入、删除和更新接口的实现,以及版本管理和Compaction的实现。LevelDB的特点作为一个函数库,LevelDB有以下特点:键和值都是任意的字节数组;数据以键的顺序存储;可以使用定制的比较器定义排序的方式;基本操作是Put(key,value)、 Get(key)、Delete(key);一个原子的Batch中可以做多个改变;支持Snapshot,用户可以建立一个一致性的视图;可以正向和反向迭代;支持snappy压缩数据;操作系统相关操作被抽象,跨平台时可以定制这些接口;同一时间只能由一个进程访问数据库,但是支持多线程访问。编译安装函数库要使用LevelDB,必须先要编译安装函数库。LevelDB使用了cmake,需要安装cmake,可以根据需要,安装snappy、crc32c和tcmalloc。通过执行以下几个步骤,就可以安装函数库了:mkdir -p build && cd build

cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .

make install写个测试:// hello.cc

#include

#include

#include

#include

using namespace leveldb;

int main(){

leveldb::DB* db;

leveldb::Options options;

options.create_if_missing = true;

// 打开一个数据库,不存在就创建

leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db);

assert(status.ok());

// 插入一个键值对

status = db->Put(leveldb::WriteOptions(), "hello", "LevelDB");

assert(status.ok());

// 读取键值对

std::string value;

status = db->Get(leveldb::ReadOptions(), "hello", &value);

assert(status.ok());

std::cout << value << std::endl;

delete db;

return 0;

}编译执行:g++ hello.cc -o hello -L. -I./include -lpthread -lleveldb

./hello用Python来操作LevelDBLevelDB是一个C++函数库,测试的时候,需要使用C++编码读取和写入,非常不方便。我们使用一般的数据库的时候,都有shell来操作数据库。好在有Python库可以用来操作LevelDB,达到和shell相同的效果,可以交互式地使用LevelDB。我们使用plyvel,可以使用pip来安装。>>> import plyvel

>>> db = plyvel.DB('/tmp/testdb/', create_if_missing=True)

>>> db.put(b'key', b'value')

>>> db.get(b'key')

'value'这样可以像使用其它数据库一样,交互式地使用LevelDB,非常方便。项目结构项目根目录下面有几个主要的目录:include: 函数库的头文件

port: 可移植性相关的功能

util: 项目用到的一些功能函数

table: SSTable的实现

db: 数据库实现,版本管理,Compaction,WAL和MemTable实现LevelDB作为函数库,对外提供的接口文件及功能如下:cache.h: 缓存接口,提供了默认的LRU缓存,也可以自己实现缓存

comparator.h: 定以数据库比较器的接口,用来比较键,可以使用默认的基于字节的比较,可以定义自己的比较器

dumpfile.h: 以可读文本形式导出一个文件,调试使用

export.h: 可移植性相关

iterator.h: 迭代器接口

slice.h: 实现一个字符串,存储指针和长度,指向字符串

table_builder.h: 构造一个SSTable

write_batch.h: 实现批量写入的接口

c.h: 实现C语言相关的接口

db.h: 操作数据库的主要接口

env.h: 定义操作系统相关的功能,如读写文件之类的

filter_policy.h: 定义布隆过滤器接口

options.h: 配置选项

status.h: 定义数据库操作的返回状态

table.h: SSTable相关的接口小结这一篇我们简单介绍了LevelDB的特点、编译方式、使用方式和项目结构。项目结构非常清晰,基本可以望文生义,大家可以先翻翻源代码目录熟悉一下项目的布局以及每个文件的用处,方便以后快速找到源代码。下一节将会介绍LevelDB的功能特性。发布于 2020-08-28 21:56LevelDB键值数据库NoSQL​赞同 53​​5 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录LevelDB源码剖析LevelDB内部源

基本概念 — leveldb-handbook 文档

基本概念 — leveldb-handbook 文档

leveldb-handbook

latest

内容:

基本概念

整体架构

memtable

immutable memtable

log

sstable

manifest

current

读写操作

日志

内存数据库

sstable

缓存系统

布隆过滤器

compaction

版本控制

leveldb-handbook

Docs »

基本概念

Edit on GitHub

基本概念¶

leveldb是一个写性能十分优秀的存储引擎,是典型的LSM树(Log Structured-Merge Tree)实现。LSM树的核心思想就是放弃部分读的性能,换取最大的写入能力。

LSM树写性能极高的原理,简单地来说就是尽量减少随机写的次数。对于每次写入操作,并不是直接将最新的数据驻留在磁盘中,而是将其拆分成(1)一次日志文件的顺序写(2)一次内存中的数据插入。leveldb正是实践了这种思想,将数据首先更新在内存中,当内存中的数据达到一定的阈值,将这部分数据真正刷新到磁盘文件中,因而获得了极高的写性能(顺序写60MB/s,

随机写45MB/s)。

在本文中,将介绍一下leveldb的基本架构、概念。

整体架构¶

leveldb中主要由以下几个重要的部件构成:

memtable

immutable memtable

log(journal)

sstable

manifest

current

memtable¶

之前提到,leveldb的一次写入操作并不是直接将数据刷新到磁盘文件,而是首先写入到内存中作为代替,memtable就是一个在内存中进行数据组织与维护的结构。memtable中,所有的数据按用户定义的排序方法排序之后按序存储,等到其存储内容的容量达到阈值时(默认为4MB),便将其转换成一个不可修改的memtable,与此同时创建一个新的memtable,供用户继续进行读写操作。memtable底层使用了一种跳表数据结构,这种数据结构效率可以比拟二叉查找树,绝大多数操作的时间复杂度为O(log

n)。

immutable memtable¶

memtable的容量到达阈值时,便会转换成一个不可修改的memtable,也称为immutable

memtable。这两者的结构定义完全一样,区别只是immutable

memtable是只读的。当一个immutable

memtable被创建时,leveldb的后台压缩进程便会将利用其中的内容,创建一个sstable,持久化到磁盘文件中。

log¶

leveldb的写操作并不是直接写入磁盘的,而是首先写入到内存。假设写入到内存的数据还未来得及持久化,leveldb进程发生了异常,抑或是宿主机器发生了宕机,会造成用户的写入发生丢失。因此leveldb在写内存之前会首先将所有的写操作写到日志文件中,也就是log文件。当以下异常情况发生时,均可以通过日志文件进行恢复:

写log期间进程异常;

写log完成,写内存未完成;

write动作完成(即log、内存写入都完成)后,进程异常;

Immutable memtable持久化过程中进程异常;

其他压缩异常(较为复杂,首先不在这里介绍);

当第一类情况发生时,数据库重启读取log时,发现异常日志数据,抛弃该条日志数据,即视作这次用户写入失败,保障了数据库的一致性;

当第二类,第三类,第四类情况发生了,均可以通过redo日志文件中记录的写入操作完成数据库的恢复。

每次日志的写操作都是一次顺序写,因此写效率高,整体写入性能较好。

此外,leveldb的用户写操作的原子性同样通过日志来实现。

sstable¶

虽然leveldb采用了先写内存的方式来提高写入效率,但是内存中数据不可能无限增长,且日志中记录的写入操作过多,会导致异常发生时,恢复时间过长。因此内存中的数据达到一定容量,就需要将数据持久化到磁盘中。除了某些元数据文件,leveldb的数据主要都是通过sstable来进行存储。

虽然在内存中,所有的数据都是按序排列的,但是当多个memetable数据持久化到磁盘后,对应的不同的sstable之间是存在交集的,在读操作时,需要对所有的sstable文件进行遍历,严重影响了读取效率。因此leveldb后台会“定期“整合这些sstable文件,该过程也称为compaction。随着compaction的进行,sstable文件在逻辑上被分成若干层,由内存数据直接dump出来的文件称为level

0层文件,后期整合而成的文件为level i

层文件,这也是leveldb这个名字的由来。

注意,所有的sstable文件本身的内容是不可修改的,这种设计哲学为leveldb带来了许多优势,简化了很多设计。具体将在接下来的文章中具体解释。

manifest¶

leveldb中有个版本的概念,一个版本中主要记录了每一层中所有文件的元数据,元数据包括(1)文件大小(2)最大key值(3)最小key值。该版本信息十分关键,除了在查找数据时,利用维护的每个文件的最大/小key值来加快查找,还在其中维护了一些进行compaction的统计值,来控制compaction的进行。

以goleveldb为例,一个文件的元数据主要包括了最大最小key,文件大小等信息;

// tFile holds basic information about a table.

type tFile struct {

fd storage.FileDesc

seekLeft int32

size int64

imin, imax internalKey

}

一个版本信息主要维护了每一层所有文件的元数据。

type version struct {

s *session // session - version

levels []tFiles // file meta

// Level that should be compacted next and its compaction score.

// Score < 1 means compaction is not strictly needed. These fields

// are initialized by computeCompaction()

cLevel int // next level

cScore float64 // current score

cSeek unsafe.Pointer

closing bool

ref int

released bool

}

当每次compaction完成(或者换一种更容易理解的说法,当每次sstable文件有新增或者减少),leveldb都会创建一个新的version,创建的规则是:

versionNew = versionOld + versionEdit

versionEdit指代的是基于旧版本的基础上,变化的内容(例如新增或删除了某些sstable文件)。

manifest文件就是用来记录这些versionEdit信息的。一个versionEdit数据,会被编码成一条记录,写入manifest文件中。例如下图便是一个manifest文件的示意图,其中包含了3条versionEdit记录,每条记录包括(1)新增哪些sst文件(2)删除哪些sst文件(3)当前compaction的下标(4)日志文件编号(5)操作seqNumber等信息。通过这些信息,leveldb便可以在启动时,基于一个空的version,不断apply这些记录,最终得到一个上次运行结束时的版本信息。

current¶

这个文件的内容只有一个信息,就是记载当前的manifest文件名。

因为每次leveldb启动时,都会创建一个新的Manifest文件。因此数据目录可能会存在多个Manifest文件。Current则用来指出哪个Manifest文件才是我们关心的那个Manifest文件。

Next

Previous

© Copyright 2017, Gary Rong

Revision f7d099ee.

Built with Sphinx using a theme provided by Read the Docs.

Read the Docs

v: latest

Versions

latest

Downloads

pdf

html

epub

On Read the Docs

Project Home

Builds

Free document hosting provided by Read the Docs.

GitHub - google/leveldb: LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.

GitHub - google/leveldb: LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.

Skip to content

Toggle navigation

Sign in

Product

Actions

Automate any workflow

Packages

Host and manage packages

Security

Find and fix vulnerabilities

Codespaces

Instant dev environments

Copilot

Write better code with AI

Code review

Manage code changes

Issues

Plan and track work

Discussions

Collaborate outside of code

Explore

All features

Documentation

GitHub Skills

Blog

Solutions

For

Enterprise

Teams

Startups

Education

By Solution

CI/CD & Automation

DevOps

DevSecOps

Resources

Learning Pathways

White papers, Ebooks, Webinars

Customer Stories

Partners

Open Source

GitHub Sponsors

Fund open source developers

The ReadME Project

GitHub community articles

Repositories

Topics

Trending

Collections

Pricing

Search or jump to...

Search code, repositories, users, issues, pull requests...

Search

Clear

Search syntax tips

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

Sign up

You signed in with another tab or window. Reload to refresh your session.

You signed out in another tab or window. Reload to refresh your session.

You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

google

/

leveldb

Public

Notifications

Fork

7.6k

Star

34.7k

LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.

License

BSD-3-Clause license

34.7k

stars

7.6k

forks

Branches

Tags

Activity

Star

Notifications

Code

Issues

209

Pull requests

99

Actions

Security

Insights

Additional navigation options

Code

Issues

Pull requests

Actions

Security

Insights

google/leveldb

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

 mainBranchesTagsGo to fileCodeFolders and filesNameNameLast commit messageLast commit dateLatest commit History434 Commits.github/workflows.github/workflows  benchmarksbenchmarks  cmakecmake  dbdb  docdoc  helpers/memenvhelpers/memenv  include/leveldbinclude/leveldb  issuesissues  portport  tabletable  third_partythird_party  utilutil  .clang-format.clang-format  .gitignore.gitignore  .gitmodules.gitmodules  AUTHORSAUTHORS  CMakeLists.txtCMakeLists.txt  CONTRIBUTING.mdCONTRIBUTING.md  LICENSELICENSE  NEWSNEWS  README.mdREADME.md  TODOTODO  View all filesRepository files navigationREADMECode of conductBSD-3-Clause licenseSecurityLevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.

This repository is receiving very limited maintenance. We will only review the following types of changes.

Fixes for critical bugs, such as data loss or memory corruption

Changes absolutely needed by internally supported leveldb clients. These typically fix breakage introduced by a language/standard library/OS update

Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)

Features

Keys and values are arbitrary byte arrays.

Data is stored sorted by key.

Callers can provide a custom comparison function to override the sort order.

The basic operations are Put(key,value), Get(key), Delete(key).

Multiple changes can be made in one atomic batch.

Users can create a transient snapshot to get a consistent view of data.

Forward and backward iteration is supported over the data.

Data is automatically compressed using the Snappy compression library, but Zstd compression is also supported.

External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.

Documentation

LevelDB library documentation is online and bundled with the source code.

Limitations

This is not a SQL database. It does not have a relational data model, it does not support SQL queries, and it has no support for indexes.

Only a single process (possibly multi-threaded) can access a particular database at a time.

There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library.

Getting the Source

git clone --recurse-submodules https://github.com/google/leveldb.git

Building

This project supports CMake out of the box.

Build for POSIX

Quick start:

mkdir -p build && cd build

cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .

Building for Windows

First generate the Visual Studio 2017 project/solution files:

mkdir build

cd build

cmake -G "Visual Studio 15" ..

The default default will build for x86. For 64-bit run:

cmake -G "Visual Studio 15 Win64" ..

To compile the Windows solution from the command-line:

devenv /build Debug leveldb.sln

or open leveldb.sln in Visual Studio and build from within.

Please see the CMake documentation and CMakeLists.txt for more advanced usage.

Contributing to the leveldb Project

This repository is receiving very limited maintenance. We will only review the following types of changes.

Bug fixes

Changes absolutely needed by internally supported leveldb clients. These typically fix breakage introduced by a language/standard library/OS update

The leveldb project welcomes contributions. leveldb's primary goal is to be

a reliable and fast key/value store. Changes that are in line with the

features/limitations outlined above, and meet the requirements below,

will be considered.

Contribution requirements:

Tested platforms only. We generally will only accept changes for

platforms that are compiled and tested. This means POSIX (for Linux and

macOS) or Windows. Very small changes will sometimes be accepted, but

consider that more of an exception than the rule.

Stable API. We strive very hard to maintain a stable API. Changes that

require changes for projects using leveldb might be rejected without

sufficient benefit to the project.

Tests: All changes must be accompanied by a new (or changed) test, or

a sufficient explanation as to why a new (or changed) test is not required.

Consistent Style: This project conforms to the

Google C++ Style Guide.

To ensure your changes are properly formatted please run:

clang-format -i --style=file

We are unlikely to accept contributions to the build configuration files, such

as CMakeLists.txt. We are focused on maintaining a build configuration that

allows us to test that the project works in a few supported configurations

inside Google. We are not currently interested in supporting other requirements,

such as different operating systems, compilers, or build systems.

Submitting a Pull Request

Before any pull request will be accepted the author must first sign a

Contributor License Agreement (CLA) at https://cla.developers.google.com/.

In order to keep the commit timeline linear

squash

your changes down to a single commit and rebase

on google/leveldb/main. This keeps the commit timeline linear and more easily sync'ed

with the internal repository at Google. More information at GitHub's

About Git rebase page.

Performance

Here is a performance report (with explanations) from the run of the

included db_bench program. The results are somewhat noisy, but should

be enough to get a ballpark performance estimate.

Setup

We use a database with a million entries. Each entry has a 16 byte

key, and a 100 byte value. Values used by the benchmark compress to

about half their original size.

LevelDB: version 1.1

Date: Sun May 1 12:11:26 2011

CPU: 4 x Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz

CPUCache: 4096 KB

Keys: 16 bytes each

Values: 100 bytes each (50 bytes after compression)

Entries: 1000000

Raw Size: 110.6 MB (estimated)

File Size: 62.9 MB (estimated)

Write performance

The "fill" benchmarks create a brand new database, in either

sequential, or random order. The "fillsync" benchmark flushes data

from the operating system to the disk after every operation; the other

write operations leave the data sitting in the operating system buffer

cache for a while. The "overwrite" benchmark does random writes that

update existing keys in the database.

fillseq : 1.765 micros/op; 62.7 MB/s

fillsync : 268.409 micros/op; 0.4 MB/s (10000 ops)

fillrandom : 2.460 micros/op; 45.0 MB/s

overwrite : 2.380 micros/op; 46.5 MB/s

Each "op" above corresponds to a write of a single key/value pair.

I.e., a random write benchmark goes at approximately 400,000 writes per second.

Each "fillsync" operation costs much less (0.3 millisecond)

than a disk seek (typically 10 milliseconds). We suspect that this is

because the hard disk itself is buffering the update in its memory and

responding before the data has been written to the platter. This may

or may not be safe based on whether or not the hard disk has enough

power to save its memory in the event of a power failure.

Read performance

We list the performance of reading sequentially in both the forward

and reverse direction, and also the performance of a random lookup.

Note that the database created by the benchmark is quite small.

Therefore the report characterizes the performance of leveldb when the

working set fits in memory. The cost of reading a piece of data that

is not present in the operating system buffer cache will be dominated

by the one or two disk seeks needed to fetch the data from disk.

Write performance will be mostly unaffected by whether or not the

working set fits in memory.

readrandom : 16.677 micros/op; (approximately 60,000 reads per second)

readseq : 0.476 micros/op; 232.3 MB/s

readreverse : 0.724 micros/op; 152.9 MB/s

LevelDB compacts its underlying storage data in the background to

improve read performance. The results listed above were done

immediately after a lot of random writes. The results after

compactions (which are usually triggered automatically) are better.

readrandom : 11.602 micros/op; (approximately 85,000 reads per second)

readseq : 0.423 micros/op; 261.8 MB/s

readreverse : 0.663 micros/op; 166.9 MB/s

Some of the high cost of reads comes from repeated decompression of blocks

read from disk. If we supply enough cache to the leveldb so it can hold the

uncompressed blocks in memory, the read performance improves again:

readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction)

readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction)

Repository contents

See doc/index.md for more explanation. See

doc/impl.md for a brief overview of the implementation.

The public interface is in include/leveldb/*.h. Callers should not include or

rely on the details of any other header files in this package. Those

internal APIs may be changed without warning.

Guide to header files:

include/leveldb/db.h: Main interface to the DB: Start here.

include/leveldb/options.h: Control over the behavior of an entire database,

and also control over the behavior of individual reads and writes.

include/leveldb/comparator.h: Abstraction for user-specified comparison function.

If you want just bytewise comparison of keys, you can use the default

comparator, but clients can write their own comparator implementations if they

want custom ordering (e.g. to handle different character encodings, etc.).

include/leveldb/iterator.h: Interface for iterating over data. You can get

an iterator from a DB object.

include/leveldb/write_batch.h: Interface for atomically applying multiple

updates to a database.

include/leveldb/slice.h: A simple module for maintaining a pointer and a

length into some other byte array.

include/leveldb/status.h: Status is returned from many of the public interfaces

and is used to report success and various kinds of errors.

include/leveldb/env.h:

Abstraction of the OS environment. A posix implementation of this interface is

in util/env_posix.cc.

include/leveldb/table.h, include/leveldb/table_builder.h: Lower-level modules that most

clients probably won't use directly.

About

LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.

Resources

Readme

License

BSD-3-Clause license

Code of conduct

Code of conduct

Security policy

Security policy

Activity

Custom properties

Stars

34.7k

stars

Watchers

1.3k

watching

Forks

7.6k

forks

Report repository

Releases

21

1.23

Latest

Feb 23, 2021

+ 20 releases

Packages

0

No packages published

Contributors

56

+ 42 contributors

Languages

C++

95.1%

C

3.1%

CMake

1.8%

Footer

© 2024 GitHub, Inc.

Footer navigation

Terms

Privacy

Security

Status

Docs

Contact

Manage cookies

Do not share my personal information

You can’t perform that action at this time.

【深度知识】LevelDB从入门到原理详解-腾讯云开发者社区-腾讯云

识】LevelDB从入门到原理详解-腾讯云开发者社区-腾讯云辉哥【深度知识】LevelDB从入门到原理详解关注作者腾讯云开发者社区文档建议反馈控制台首页学习活动专区工具TVP最新优惠活动文章/答案/技术大牛搜索搜索关闭发布登录/注册首页学习活动专区工具TVP最新优惠活动返回腾讯云官网辉哥首页学习活动专区工具TVP最新优惠活动返回腾讯云官网社区首页 >专栏 >【深度知识】LevelDB从入门到原理详解【深度知识】LevelDB从入门到原理详解辉哥关注发布于 2020-03-20 14:29:385.8K1发布于 2020-03-20 14:29:38举报文章被收录于专栏:区块链入门区块链入门1.摘要本文介绍LevelDB的介绍,性能,框架,核心构件原理,基本操作接口样例。2. LevelDB概述LevelDB是Google开源的持久化KV单机数据库,具有很高的随机写,顺序读/写性能,但是随机读的性能很一般,也就是说,LevelDB很适合应用在查询较少,而写很多的场景。LevelDB应用了LSM (Log Structured Merge) 策略,lsm_tree对索引变更进行延迟及批量处理,并通过一种类似于归并排序的方式高效地将更新迁移到磁盘,降低索引插入开销。2.1 特点1、key和value都是任意长度的字节数组;

2、entry(即一条K-V记录)默认是按照key的字典顺序存储的,当然开发者也可以重载这个排序函数;

3、提供的基本操作接口:Put()、Delete()、Get()、Batch();

4、支持批量操作以原子操作进行;

5、可以创建数据全景的snapshot(快照),并允许在快照中查找数据;

6、可以通过前向(或后向)迭代器遍历数据(迭代器会隐含的创建一个snapshot);

7、自动使用Snappy压缩数据;

8、可移植性;2.2 限制1、非关系型数据模型(NoSQL),不支持sql语句,也不支持索引;

2、一次只允许一个进程访问一个特定的数据库;

3、没有内置的C/S架构,但开发者可以使用LevelDB库自己封装一个server;2.3 性能LevelDB 是单进程的服务,性能非常之高,在一台4核Q6600的CPU机器上,每秒钟写数据超过40w,而随机读的性能每秒钟超过10w。下面的性能测试报告基于db_bench程序,结果存在一定干扰,但做为性能评估是足够的。1.3.1 验证场景我们使用一个数百万记录的数据库,每条记录的key大小为16字节,value大小为100字节。基准测试的Value大小压缩为原始大小的一半。LevelDB: version 1.1

Date: Sun May 1 12:11:26 2011

CPU: 4 x Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz

CPUCache: 4096 KB

Keys: 16 bytes each

Values: 100 bytes each (50 bytes after compression)

Entries: 1000000

Raw Size: 110.6 MB (estimated)

File Size: 62.9 MB (estimated)复制1.3.2 写性能“填充”基准测试以顺序或随机顺序创建一个全新的数据库。 每次操作后,“fillsync”基准将数据从操作系统刷新到磁盘(顺序操作); 其他写入操作则利用操作系统缓冲区高速缓存中一段时间。 “覆盖”基准做随机写入,更新数据库中的现有key值记录。fillseq : 1.765 micros/op; 62.7 MB/s; 56.6W+ OP/S

fillsync : 268.409 micros/op; 0.4 MB/s (10000 ops); 0.37W OP/S

fillrandom : 2.460 micros/op; 45.0 MB/s; 40W+ OP/S

overwrite : 2.380 micros/op; 46.5 MB/s; 42W+ OP/S复制"op"是指一次写入一个key/value对。随机写性能大约在40W op/s,“fillsync”操作时间(0.3 millisecond)甚至少于磁盘seek时间(典型时间为10millisecond)。我们怀疑这可能是因为硬盘自身存在缓存(memory),并在数据真正落盘前进行响应。这样做是否安全取决于出现电力故障时,是否来得及将缓存中的数据写入磁盘。1.3.3 读性能我们列出了正向和反向方向上顺序读取的性能,以及随机查找的性能。 请注意,基准数据库创建的数据库相当小, 报告中的leveldb性能数据基本是基于内存操作的。 读取操作系统高速缓冲区中不存在的数据时,将额外执行1-2次磁盘查找动作。 写性能基本不受数据是否在内存中的影响。readrandom : 16.677 micros/op; (approximately 60,000 reads per second)

readseq : 0.476 micros/op; 232.3 MB/s; 210W+ op/s

readreverse : 0.724 micros/op; 152.9 MB/s; 138W+ op/s复制LevelDB在后台压缩其底层存储数据,以提高读取性能。 上面列出的结果是在大量随机写作之后立即进行的。 压缩后的结果(通常自动触发)更好。readrandom : 11.602 micros/op; (approximately 85,000 reads per second)

readseq : 0.423 micros/op; 261.8 MB/s; 236W+ op/s

readreverse : 0.663 micros/op; 166.9 MB/s; 150W+ op/s复制其中,某些读操作由于重复读取磁盘并解压缩以致耗时较长。我们为leveldb提供足够的缓存,以便可以将未压缩的块保存在内存中,则读取性能再次提高:readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction)

readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction)复制对于典型的数据库操作,基本为随机读、写,整理下上述关键性能数据:fillrandom : 2.460 micros/op; 45.0 MB/s; 40W+ OP/S

readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction)

readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction)复制随机写的性能大致为40W+ op/s,读在数据压缩完成后性能最佳为19W+ op/s,运行时的实时读性能大约为10W+ op/s。3. LevelDB框架上图简单展示了 LevelDB 的整体架构。LevelDB 的静态结构主要由六个部分组成:

MemTable(wTable):内存数据结构,具体实现是 SkipList。 接受用户的读写请求,新的数据修改会首先在这里写入。

Immutable MemTable(rTable):当 MemTable 的大小达到设定的阈值时,会变成 Immutable MemTable,只接受读操作,不再接受写操作,后续由后台线程 Flush 到磁盘上。

SST Files:Sorted String Table Files,磁盘数据存储文件。分为 Level0 到 LevelN 多层,每一层包含多个 SST 文件,文件内数据有序。Level0 直接由 Immutable Memtable Flush 得到,其它每一层的数据由上一层进行 Compaction 得到。

Manifest Files:Manifest 文件中记录 SST 文件在不同 Level 的分布,单个 SST 文件的最大、最小 key,以及其他一些 LevelDB 需要的元信息。由于 LevelDB 支持 snapshot,需要维护多版本,因此可能同时存在多个 Manifest 文件。

Current File:由于 Manifest 文件可能存在多个,Current 记录的是当前的 Manifest 文件名。

Log Files (WAL):用于防止 MemTable 丢数据的日志文件。粗箭头表示写入数据的流动方向:

(1) 先写入 MemTable。

(2) MemTable 的大小达到设定阈值的时候,转换成 Immutable MemTable。

(3) Immutable Table 由后台线程异步 Flush 到磁盘上,成为 Level0 上的一个 sst 文件。

(4) 在某些条件下,会触发后台线程对 Level0 ~ LevelN 的文件进行 Compaction。读操作的流动方向和写操作类似:

(1) 读 MemTable,如果存在,返回。

(2) 读 Immutable MemTable,如果存在,返回。

(3) 按顺序读 Level0 ~ Leveln,如果存在,返回。

(4) 返回不存在。全程的操作流程如下描述:

MemTable(wTable)的大小达到一个阈值,LevelDB 将它凝固成只读的Immutable MemTable(rTable),同时生成一个新的 wtable 继续接受写操作。rtable 将会被异步线程刷到磁盘中。Get 操作会优先查询 wtable,如果找不到就去 rtable 中去找,rtable 如果还找不到,再去磁盘文件里去找。因为 wtable 要支持多线程读写,所以访问它是需要加锁控制。而 rtable 是只读的,它就不需要,但是它的存在时间很短,rtable 一旦生成,很快就会被异步线程序列化到磁盘上,然后就会被置空。但是异步线程序列化也需要耗费一定的时间,如果 wtable 增长过快,很快就被写满了,这时候 rtable 还没有完成序列化,而wtable 急需变身怎么办?这时写线程就会阻塞等待异步线程序列化完成,这是 LevelDB 的卡顿点之一,也是未来 RocksDB 的优化点。图中还有个日志文件,记录了近期的写操作日志。如果 LevelDB 遇到突发停机事故,没有持久化的 wtable 和 rtable 数据就会丢失。这时就必须通过重放日志文件中的指令数据来恢复丢失的数据。注意到日志文件也是有两份的,它和内存的跳跃列表正好对应起来。当 wtable 要变身时,日志文件也会跟着变身。待 rtable 落盘成功之后,只读日志文件就可以被删除了。4. LevelDB基础部件4.1 跳跃表SkipListLevelDB 的内存中维护了 2 个跳跃列表,一个是只读的Immutable MemTable(rTable) ,一个是可修改的MemTable(wTable)。简单理解,跳跃列表就是一个 Key 有序的 Set 集合,排序规则由全局的「比较器」决定,默认是字典序。跳跃列表的查找和更新操作时间复杂度都是 Log(n)。跳跃表是平衡树的一种替代的数据结构,但是和红黑树不相同的是,跳跃表对于树的平衡的实现是基于一种随机化的算法的,这样也就是说跳跃表的插入和删除的工作是比较简单的。(1)跳跃表的核心思想先从链表开始,如果是一个简单的链表,那么我们知道在链表中查找一个元素I的话,需要将整个链表遍历一次。如果是说链表是排序的,并且节点中还存储了指向前面第二个节点的指针的话,那么在查找一个节点时,仅仅需要遍历N/2个节点即可。这基本上就是跳跃表的核心思想,其实也是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。(2)跳跃表的数据存储模型

我们定义:

如果一个基点存在k个向前的指针的话,那么该节点是k层的节点。

一个跳跃表的层MaxLevel义为跳跃表中所有节点中最大的层数。

下面给出一个完整的跳跃表的图示:那么我们该如何将该数据结构使用二进制存储呢?通过上面的跳跃表很容易设计这样的数据结构,定义每个节点类型:// 这里仅仅是一个指针

typedef struct nodeStructure *node;

typedef struct nodeStructure

{

keyType key; // key值

valueType value; // value值

// 向前指针数组,根据该节点层数的

// 不同指向不同大小的数组

node forward[1];

};复制上面的每个结构体对应着图中的每个节点,如果一个节点是一层的节点的话(如7,12等节点),那么对应的forward将指向一个只含一个元素的数组,以此类推。// 定义跳跃表数据类型

typedef struct listStructure{

int level; /* Maximum level of the list

(1 more than the number of levels in the list) */

struct nodeStructure * header; /* pointer to header */

} * list; 复制跳跃表数据类型中包含了维护跳跃表的必要信息,level表明跳跃表的层数,header如下所示:定义辅助变量:定义上图中的NIL变量:node NIL;

#define MaxNumberOfLevels 16

#define MaxLevel (MaxNumberOfLevels-1) 复制定义辅助方法:// newNodeOfLevel生成一个nodeStructure结构体,同时生成l个node *数组指针

#define newNodeOfLevel(l) (node)malloc(sizeof(struct nodeStructure)+(l)*sizeof(node *))复制好的基本的数据结构定义已经完成,接下来来分析对于跳跃表的一个操作。(3)跳跃表初始化

初始化的过程很简单,仅仅是生成下图中红线区域内的部分,也就是跳跃表的基础结构:

list newList()

{

list l;

int i;

// 申请list类型大小的内存

l = (list)malloc(sizeof(struct listStructure));

// 设置跳跃表的层level,初始的层为0层(数组从0开始)

l->level = 0;

// 生成header部分

l->header = newNodeOfLevel(MaxNumberOfLevels);

// 将header的forward数组清空

for(i=0;iheader->forward[i] = NIL;

return(l);

}; 复制(4)插入操作

由于跳跃表数据结构整体上是有序的,所以在插入时,需要首先查找到合适的位置,然后就是修改指针(和链表中操作类似),然后更新跳跃表的level变量。(5)删除某个节点

和插入是相同的,首先查找需要删除的节点,如果找到了该节点的话,那么只需要更新指针域,如果跳跃表的level需要更新的话,进行更新。4.2 SSTableLevelDB 的键值对内容都存储在扩展名为 sst 的 SSTable 文件中。SSTable 文件的内容分为 5 个部分,Footer、IndexBlock、MetaIndexBlock、FilterBlock 和 DataBlock。其中存储了键值对内容的就是 DataBlock,存储了布隆过滤器二进制数据的是 FilterBlock,DataBlock 有多个,FilterBlock 也可以有多个,但是通常最多只有 1 个,之所以设计成多个是考虑到扩展性,也许未来会支持其它类型的过滤器。另外 3 个部分为管理块,其中 IndexBlock 记录了 DataBlock 相关的元信息,MetaIndexBlock 记录了过滤器相关的元信息,而 Footer 则指出 IndexBlock 和 MetaIndexBlock 在文件中的偏移量信息,它是元信息的元信息,它位于 sstable 文件的尾部。4.2.1 Footer 结构它的占用空间很小只有 48 字节,内部只存了几个字段。下面我们用伪代码来描述一下它的结构// 定义了数据块的位置和大小

struct BlockHandler {

varint offset;

varint size;

}

struct Footer {

BlockHandler metaIndexHandler; // MetaIndexBlock的文件偏移量和长度

BlockHandler indexHandler; // IndexBlock的文件偏移量和长度

byte[n] padding; // 内存垫片

int32 magicHighBits; // 魔数后32位

int32 magicLowBits; // 魔数前32位

}复制Footer 结构的中间部分增加了内存垫片,其作用就是将 Footer 的空间撑到 48 字节。结构的尾部还有一个 64位的魔术数字 0xdb4775248b80fb57,如果文件尾部的 8 字节不是这个数字说明文件已经损坏。这个魔术数字的来源很有意思,它是下面返回的字符串的前64bit。$ echo http://code.google.com/p/leveldb/ | sha1sumdb4775248b80fb57d0ce0768d85bcee39c230b61复制IndexBlock 和 MetaIndexBlock 都只有唯一的一个,所以分别使用一个 BlockHandler 结构来存储偏移量和长度。4.2.2 Block 结构除了 Footer 之外,其它部分都是 Block 结构,在名称上也都是以 Block 结尾。所谓的 Block 结构是指除了内部的有效数据外,还会有额外的压缩类型字段和校验码字段。struct Block { byte[] data; int8 compressType; int32 crcValue;}复制每一个 Block 尾部都会有压缩类型和循环冗余校验码(crcValue),这会要占去 5 字节。如果是压缩类型,块内的数据 data 会被压缩。校验码会针对压缩和的数据和压缩类型字段一起计算循环冗余校验和。压缩算法默认是 snappy ,校验算法是 crc32。crcValue = crc32(data, compressType)复制在下面介绍的所有 Block 结构中,我们不再提及压缩和校验码。4.2.3 DataBlock 结构DataBlock 的大小默认是 4K 字节(压缩前),里面存储了一系列键值对。前面提到 sst 文件里面的 Key 是有序的,这意味着相邻的 Key 会有很大的概率有共同的前缀部分。正是考虑到这一点,DataBlock 在结构上做了优化,这个优化可以显著减少存储空间。Key = sharedKey + unsharedKey复制Key 会划分为两个部分,一个是 sharedKey,一个是 unsharedKey。前者表示相对基准 Key 的共同前缀内容,后者表示相对基准 Key 的不同后缀部分。比如基准 Key 是 helloworld,那么 hellouniverse 这个 Key 相对于基准 Key 来说,它的 sharedKey 就是 hello,unsharedKey 就是 universe。DataBlock 中存储的是连续的一系列键值对,它会每隔若干个 Key 设置一个基准 Key。基准 Key 的特点就是它的 sharedKey 部分是空串。基准 Key 的位置,也就是它在块中的偏移量我们称之为「重启点」RestartPoint,在 DataBlock 中会记录所有「重启点」位置。第一个「重启点」的位置是零,也就是 DataBlock 中的第一个 Key。struct Entry {

varint sharedKeyLength;

varint unsharedKeyLength;

varint valueLength;

byte[] unsharedKeyContent;

byte[] valueContent;

}

struct DataBlock {

Entry[] entries;

int32 [] restartPointOffsets;

int32 restartPointCount;

}复制DataBlock 中基准 Key 是默认每隔 16 个 Key 设置一个。从节省空间的角度来说,这并不是一个智能的策略。比如连续 26 个 Key 仅仅是最后一个字母不同,DataBlock 却每隔 16 个 Key 强制「重启」,这明显不是最优的。这同时也意味着 sharedKey 是空串的 Key 未必就是基准 Key。一个 DataBlock 的默认大小只有 4K 字节,所以里面包含的键值对数量通常只有几十个。如果单个键值对的内容太大一个 DataBlock 装不下咋整?这里就必须纠正一下,DataBlock 的大小是 4K 字节,并不是说它的严格大小,而是在追加完最后一条记录之后发现超出了 4K 字节,这时就会再开启一个 DataBlock。这意味着一个 DataBlock 可以大于 4K 字节,如果 value 值非常大,那么相应的 DataBlock 也会非常大。DataBlock 并不会将同一个 Value 值分块存储。4.2.4 FilterBlock 结构如果没有开启布隆过滤器,FilterBlock 这个块就是不存在的。FilterBlock 在一个 SSTable 文件中可以存在多个,每个块存放一个过滤器数据。不过就目前 LevelDB 的实现来说它最多只能有一个过滤器,那就是布隆过滤器。布隆过滤器用于加快 SSTable 磁盘文件的 Key 定位效率。如果没有布隆过滤器,它需要对 SSTable 进行二分查找,Key 如果不在里面,就需要进行多次 IO 读才能确定,查完了才发现原来是一场空。布隆过滤器的作用就是避免在 Key 不存在的时候浪费 IO 操作。通过查询布隆过滤器可以一次性知道 Key 有没有可能在里面。单个布隆过滤器中存放的是一个定长的位图数组,该位图数组中存放了若干个 Key 的指纹信息。这若干个 Key 来源于 DataBlock 中连续的一个范围。FilterBlock 块中存在多个连续的布隆过滤器位图数组,每个数组负责指纹化 SSTable 中的一部分数据。struct FilterEntry {

byte[] rawbits;

}

struct FilterBlock {

FilterEntry[n] filterEntries;

int32[n] filterEntryOffsets;

int32 offsetArrayOffset;

int8 baseLg; // 分割系数

}复制其中 baseLg 默认 11,表示每隔 2K 字节(2<<11)的 DataBlock 数据(压缩后),就开启一个布隆过滤器来容纳这一段数据中 Key 值的指纹。如果某个 Value 值过大,以至于超出了 2K 字节,那么相应的布隆过滤器里面就只有 1 个 Key 值的指纹。每个 Key 对应的指纹空间在打开数据库时指定。// 每个 Key 占用 10bit 存放指纹信息

options.SetFilterPolicy(levigo.NewBloomFilter(10))复制这里的 2K 字节的间隔是严格的间隔,这样才可以通过 DataBlock 的偏移量和大小来快速定位到相应的布隆过滤器的位置 FilterOffset,再进一步获得相应的布隆过滤器位图数据。4.4.5 MetaIndexBlock 结构MetaIndexBlock 存储了前面一系列 FilterBlock 的元信息,它在结构上和 DataBlock 是一样的,只不过里面 Entry 存储的 Key 是带固定前缀的过滤器名称,Value 是对应的 FilterBlock 在文件中的偏移量和长度。key = "filter." + filterName// value 定义了数据块的位置和大小

struct BlockHandler { varint offset; varint size;}复制就目前的 LevelDB,这里面最多只有一个 Entry,那么它的结构非常简单,如下图所示4.4.6 IndexBlock 结构它和 MetaIndexBlock 结构一样,也存储了一系列键值对,每一个键值对存储的是 DataBlock 的元信息,SSTable 中有几个 DataBlock,IndexBlock 中就有几个键值对。键值对的 Key 是对应 DataBlock 内部最大的 Key,Value 是 DataBlock 的偏移量和长度。不考虑 Key 之间的前缀共享,不考虑「重启点」,它的结构如下图所示4.5 日志文件(LOG)"LOG文件在LevelDb中的主要作用是系统故障恢复时,能够保证不会丢失数据。因为在将记录写入内存的Memtable之前,会先写入Log文件,这样即使系统发生故障,Memtable中的数据没有来得及Dump到磁盘的SSTable文件,LevelDB也可以根据log文件恢复内存的Memtable数据结构内容,不会造成系统丢失数据,在这点上LevelDb和Bigtable是一致的。"4.5.1 日志数据结构日志中的每条记录由Record Header + Record Content组成,其中Header大小为kHeaderSize(7字节),由CRC(4字节) + Size(2字节) + Type(1字节)三部分组成。除此之外才是content的真正内容:日志文件的基础部件很简单,只需要能够创建文件、追加操作、实时刷新数据即可。为了做到跨平台、解耦,LevelDB还是对此做了封装。Leveldb命名空间下,有一个名为log的子命名空间,其下有Writer、Reader两个实现类。按前几节的命名规则,Writer其实是一个Builder,它对外提供了唯一的AddRecord方法用于追加操作记录。4.5.2 Writer在log命名空间中,包含一个Writer用于日志操作,其只有一个Append方法,这和日志的定位相同,定义如下:class Writer

{

explicit Writer(WritableFile *dest);

Writer(WritableFile *dest, uint64_t dest_length);

...

Status AddRecord(const Slice &slice);

...

};复制外部创建一个WritableFile,通过构造函数传递给Writer。AddRecord按上述的结构完善record并添加到日志文件中。Status Writer::AddRecord(const Slice& slice) {

const char* ptr = slice.data();

size_t left = slice.size();

// Fragment the record if necessary and emit it. Note that if slice

// is empty, we still want to iterate once to emit a single

// zero-length record

Status s;

bool begin = true;

do {

//1. 当前块剩余大小

const int leftover = kBlockSize - block_offset_;

assert(leftover >= 0);

//2. 剩余大小不足,占位

if (leftover < kHeaderSize)

{

// Switch to a new block

if (leftover > 0)

{

// Fill the trailer (literal below relies on kHeaderSize being 7)

assert(kHeaderSize == 7);

dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));

}

block_offset_ = 0;

}

// Invariant: we never leave < kHeaderSize bytes in a block.

assert(kBlockSize - block_offset_ - kHeaderSize >= 0);

const size_t avail = kBlockSize - block_offset_ - kHeaderSize;

//3. 当前块存储的空间大小

const size_t fragment_length = (left < avail) ? left : avail;

//4. Record Type

RecordType type;

const bool end = (left == fragment_length);

if (begin && end) {

type = kFullType;

}

else if (begin) {

type = kFirstType;

}

else if (end) {

type = kLastType;

}

else {

type = kMiddleType;

}

//5. 写入文件

s = EmitPhysicalRecord(type, ptr, fragment_length);

ptr += fragment_length;

left -= fragment_length;

begin = false;

} while (s.ok() && left > 0);

return s;

}复制当前Block剩余大小不足以填充Record Header时,以"\x00\x00\x00\x00\x00\x00"占位。

当Block无法完整记录一条Record时,通过type信息标识该record在当前block中的区块信息,以便读取时可根据type拼接出完整的record。

EmitPhysicalRecord向Block中插入Record数据,每条记录append之后会执行一次flush。4.5.3 创建日志的时机在LevelDB中,日志文件和memtable是配对的,在任何数据写入Memtable之前都会先写入日志文件。除此之外,日志文件别无它用。因此,日志文件的创建时和Memtable的创建时机也必然一致,这点对于我们理解日志文件至关重要。那么,Memtable在何时会创建呢?4.5.3.1 数据库启动如果我们创建了一个新数据库,或者数据库上次运行的所有日志都已经归档到Level0状态。此时,需要为本次数据库进程创建新的Memtable以及日志文件,代码逻辑如下: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)

{

//创建新的memtable及日志文件

// 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;

//文件交由log::Writer做追加操作

impl->log_ = new log::Writer(lfile);

//创建MemTable

impl->mem_ = new MemTable(impl->internal_comparator_);

impl->mem_->Ref();

}

}

......

}复制创建日志文件前,需要先给日志文件起一个名字,此处使用日志编号及数据库名称拼接而成,例如: 数据库名称为AiDb,编号为324时,日志文件名称为AiDb000324.log

4.5.3.2 插入数据如果插入数据时,当前的memtable容量达到设定的options_.write_buffer_size,此时触发新的memtable创建,并将之前的memtable转为imm,同时构建新的日志文件。 uint64_t new_log_number = versions_->NewFileNumber();

WritableFile *lfile = NULL;

s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);

if (!s.ok())

{

// Avoid chewing through file number space in a tight loop.

versions_->ReuseFileNumber(new_log_number);

break;

}

delete log_;

delete logfile_;

logfile_ = lfile;

logfile_number_ = new_log_number;

//创建日志文件

log_ = new log::Writer(lfile);

imm_ = mem_;

has_imm_.Release_Store(imm_);

//创建memtable

mem_ = new MemTable(internal_comparator_);

mem_->Ref();

force = false; // Do not force another compaction if have room

MaybeScheduleCompaction();复制4.5.3.3 数据库恢复数据库启动时首先完成数据库状态恢复,日志恢复过程中,如果为最后一个日志文件,且配置为日志重用模式(options_.reuse_logs=true)时,创建新的日志文件。但和其他场景不同的是,这里的日志文件是“继承性”的,也就是说部分内容是上次遗留下来的。来看实现: // See if we should keep reusing the last log file.

if (status.ok() && options_.reuse_logs && last_log && compactions == 0)

{

assert(logfile_ == NULL);

assert(log_ == NULL);

assert(mem_ == NULL);

uint64_t lfile_size;

if (env_->GetFileSize(fname, &lfile_size).ok() &&

env_->NewAppendableFile(fname, &logfile_).ok())

{

Log(options_.info_log, "Reusing old log %s \n", fname.c_str());

log_ = new log::Writer(logfile_, lfile_size);

logfile_number_ = log_number;

if (mem != NULL)

{

mem_ = mem;

mem = NULL;

}

else

{

// mem can be NULL if lognum exists but was empty.

mem_ = new MemTable(internal_comparator_);

mem_->Ref();

}

}

}复制4.6 版本控制LevelDB如何能够知道每一层有哪些SST文件;如何快速的定位某条数据所在的SST文件;重启后又是如何恢复到之前的状态的,等等这些关键的问题都需要依赖元信息管理模块。对其维护的信息及所起的作用简要概括如下:记录Compaction相关信息,使得Compaction过程能在需要的时候被触发;维护SST文件索引信息及层次信息,为整个LevelDB的读、写、Compaction提供数据结构支持;负责元信息数据的持久化,使得整个库可以从进程重启或机器宕机中恢复到正确的状态;记录LogNumber,Sequence,下一个SST文件编号等状态信息;

以版本的方式维护元信息,使得Leveldb内部或外部用户可以以快照的方式使用文件和数据。LeveDB用Version表示一个版本的元信息,Version中主要包括一个FileMetaData指针的二维数组,分层记录了所有的SST文件信息。FileMetaData数据结构用来维护一个文件的元信息,包括文件大小,文件编号,最大最小值,引用计数等,其中引用计数记录了被不同的Version引用的个数,保证被引用中的文件不会被删除。除此之外,Version中还记录了触发Compaction相关的状态信息,这些信息会在读写请求或Compaction过程中被更新。可以知道在CompactMemTable和BackgroundCompaction过程中会导致新文件的产生和旧文件的删除。每当这个时候都会有一个新的对应的Version生成,并插入VersionSet链表头部。VersionSet是一个Version构成的双向链表,这些Version按时间顺序先后产生,记录了当时的元信息,链表头指向当前最新的Version,同时维护了每个Version的引用计数,被引用中的Version不会被删除,其对应的SST文件也因此得以保留,通过这种方式,使得LevelDB可以在一个稳定的快照视图上访问文件。VersionSet中除了Version的双向链表外还会记录一些如LogNumber,Sequence,下一个SST文件编号的状态信息。通过上面的描述可以看出,相邻Version之间的不同仅仅是一些文件被删除另一些文件被删除。也就是说将文件变动应用在旧的Version上可以得到新的Version,这也就是Version产生的方式。LevelDB用VersionEdit来表示这种相邻Version的差值。为了避免进程崩溃或机器宕机导致的数据丢失,LevelDB需要将元信息数据持久化到磁盘,承担这个任务的就是Manifest文件。可以看出每当有新的Version产生都需要更新Manifest,很自然的发现这个新增数据正好对应于VersionEdit内容,也就是说Manifest文件记录的是一组VersionEdit值,在Manifest中的一次增量内容称作一个Block,其内容如下:Manifest Block := N * Item

Item := [kComparator] comparator

or [kLogNumber] 64位log_number

or [kPrevLogNumber] 64位pre_log_number

or [kNextFileNumber] 64位next_file_number_

or [kLastSequence] 64位last_sequence_

or [kCompactPointer] 32位level + 变长的key

or [kDeletedFile] 32位level + 64位文件号

or [kNewFile] 32位level + 64位 文件号 + 64位文件长度 + smallest key + largest key复制可以看出恢复元信息的过程也变成了依次应用VersionEdit的过程,这个过程中有大量的中间Version产生,但这些并不是我们所需要的。LevelDB引入VersionSet::Builder来避免这种中间变量,方法是先将所有的VersoinEdit内容整理到VersionBuilder中,然后一次应用产生最终的Version,这种实现上的优化如下图所示:image在这一节中,我们依次看到了LevelDB版本控制中比较重要的几个角色:Version、FileMetaData、VersionSet、VersionEdit、Manifest和Version::Builder。版本信息有什么用?先来简要说明三个类的具体用途:

Version:代表了某一时刻的数据库版本信息,版本信息的主要内容是当前各个Level的SSTable数据文件列表。

VersionSet:维护了一份Version列表,包含当前Alive的所有Version信息,列表中第一个代表数据库的当前版本。

VersionEdit:表示Version之间的变化,相当于delta 增量,表示有增加了多少文件,删除了文件。Version0 +VersionEdit-->Version1。VersionEdit会保存到MANIFEST文件中,当做数据恢复时就会从MANIFEST文件中读出来重建数据。那么,何时会触发版本变迁呢?Compaction。具体而言:

Version版本信息记录了运行期一组编号信息,该信息被序列化到Manifest文件中,当数据库再次打开时可恢复至上一次的运行状态。

版本信息记录了SSTable信息,包括每个文件所属的层级、大小、编号(名称等);

VersionSet类组提供了查询SSTable信息功能,如每层文件的列表、数量;同时数据库的Get方法中如需通过文件查找key值数据时,也由Version类组完成。最后,SSTable的缓存机制也有Version类组提供。

版本信息提供了Compaction支持。

每个LevelDB有一个Current File,Current File内唯一的信息为:当前数据库的Manifest文件名。Manifest中包含了上次运行后全部的版本信息,LevelDB通过Manifest文件恢复版本信息。LevelDB的版本信息为富语义功能组,它所包含的信息已经大大超出了版本定义本身。如果将Version类封装为结构体、VersionSet仅仅为Version列表、VersionEdit也是单纯的结构数据,再为上述结构提供多套功能类应该更为合理。目前来看,这应当算作LevelDB实现的一处臭味。5. LevelDB基本操作接口5.1 例子#include

#include

#include "leveldb/db.h"

#include "leveldb/write_batch.h"

int main()

{

// Open a database.

leveldb::DB* db;

leveldb::Options opts;

opts.create_if_missing = true;

leveldb::Status status = leveldb::DB::Open(opts, "./testdb", &db);

assert(status.ok());

// Write data.

status = db->Put(leveldb::WriteOptions(), "name", "jinhelin");

assert(status.ok());

// Read data.

std::string val;

status = db->Get(leveldb::ReadOptions(), "name", &val);

assert(status.ok());

std::cout << val << std::endl;

// Batch atomic write.

leveldb::WriteBatch batch;

batch.Delete("name");

batch.Put("name0", "jinhelin0");

batch.Put("name1", "jinhelin1");

batch.Put("name2", "jinhelin2");

batch.Put("name3", "jinhelin3");

batch.Put("name4", "jinhelin4");

batch.Put("name5", "jinhelin5");

batch.Put("name6", "jinhelin6");

batch.Put("name7", "jinhelin7");

batch.Put("name8", "jinhelin8");

batch.Put("name9", "jinhelin9");

status = db->Write(leveldb::WriteOptions(), &batch);

assert(status.ok());

// Scan database.

leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());

for (it->SeekToFirst(); it->Valid(); it->Next()) {

std::cout << it->key().ToString() << ": " <<

it->value().ToString() << std::endl;

}

assert(it->status().ok());

// Range scan, example: [name3, name8)

for (it->Seek("name3");

it->Valid() && it->key().ToString() < "name8";

it->Next()) {

std::cout << it->key().ToString() << ": " <<

it->value().ToString() << std::endl;

}

// Close a database.

delete db;

}复制这个例子简单介绍了 LevelDB 的基本用法,包括: 打开数据库。写入一条数据。读取一条数据。批量原子操作。范围查找。关闭数据库。5.2 打开数据库 // Open a database.

leveldb::DB* db;

leveldb::Options opts;

opts.create_if_missing = true;

leveldb::Status status = leveldb::DB::Open(opts, "./testdb", &db);

assert(status.ok());复制打开 LevelDB 数据库需要三个参数:**leveldb::Options **:控制 DB 行为的一些参数,具体可以参考链接指向的代码。在这里 create_if_missing 为 true 表示如果数据库./testdb 存在就直接打开,不存在就创建。** ./testdb** :LevelDB 数据库的根目录。一个 LevelDB 数据库存放在一个目录下。&db :用来返回一个 LevelDB 实例。leveldb::Status :封装了 leveldb 接口返回的详细信息。5.3 写入一条数据 // Write data.

status = db->Put(leveldb::WriteOptions(), "name", "jinhelin");

assert(status.ok());复制Put 接口的三个参数: leveldb::WriteOptions :目前里面只有一个 sync 成员。表示写完 WAL 后是否需要 flush。另外两个参数分别是本次写入数据的 Key 和 Value。5.4 读取一条数据 // Read data.

std::string val;

status = db->Get(leveldb::ReadOptions(), "name", &val);

assert(status.ok());

std::cout << val << std::endl;复制Get 接口和 Put 接口比较像,除了leveldb::ReadOptions参数是用来控制读操作的,具体见链接指向的代码。5.5 批量原子修改 // Batch atomic write.

leveldb::WriteBatch batch;

batch.Delete("name");

batch.Put("name0", "jinhelin0");

batch.Put("name1", "jinhelin1");

batch.Put("name2", "jinhelin2");

batch.Put("name3", "jinhelin3");

batch.Put("name4", "jinhelin4");

batch.Put("name5", "jinhelin5");

batch.Put("name6", "jinhelin6");

batch.Put("name7", "jinhelin7");

batch.Put("name8", "jinhelin8");

batch.Put("name9", "jinhelin9");

status = db->Write(leveldb::WriteOptions(), &batch);

assert(status.ok());复制LevelDB 的 Write 接口支持原子地修改多条数据,主要参数是leveldb::WriteBatch5.6 范围查找 // Scan database.

leveldb::Iterator* it = db->NewIterator(leveldb::ReadOptions());

for (it->SeekToFirst(); it->Valid(); it->Next()) {

std::cout << it->key().ToString() << ": " <<

it->value().ToString() << std::endl;

}

assert(it->status().ok());

// Range scan, example: [name3, name8)

for (it->Seek("name3");

it->Valid() && it->key().ToString() < "name8";

it->Next()) {

std::cout << it->key().ToString() << ": " <<

it->value().ToString() << std::endl;

}复制LevelDB 通过提供 leveldb::Iterator 来实现范围查找。5.7 关闭数据库 ...

// Close a database.

delete db;

...复制最后,关闭数据库时需要删除掉创建的数据库实例,让其调用析构函数处理一些收尾工作。5.8 SnapshotLevelDB 还提供了快照(Snapshot)的功能,让应用可以获得数据库在某一时刻的只读的一致性数据。可以利用 LevelDB 的 Snapshot 功能实现类似 MySQL 的 MVCC。6. 参考(1)LevelDB官网

https://github.com/google/leveldb

帮助文档

https://github.com/google/leveldb/blob/master/doc/index.md(1)既生 Redis 何生 LevelDB ?

https://mp.weixin.qq.com/s/j9a0T1DMVmhd7HTxbJJ6Aw(2)LevelDB 入门 —— 全面了解 LevelDB 的功能特性

https://mp.weixin.qq.com/s/GIVPGs9J90VBPpGcbMp5dw(2)LevelDB库简介

https://www.cnblogs.com/chenny7/p/4026447.html(2)LevelDB、TreeDB、SQLite3性能对比测试

https://www.oschina.net/question/12_25944(2)1. LevelDB源码剖析之关于LevelDB

https://www.jianshu.com/p/9b5945cb6e70(2)LevelDB

https://baike.baidu.com/item/LevelDB/6416354?fr=aladdin(3)鸿篇巨制 —— LevelDB 的整体架构【质量高】

https://mp.weixin.qq.com/s/rN6HX2VzsRi3_EKXYKuJAA

(3)LevelDB:整体架构

https://www.jianshu.com/p/6e49aa5182f0(4)跳表SkipList

https://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html(4)5. LevelDB源码剖析之基础部件-SkipList

https://www.jianshu.com/p/6624befde844(5)深入 LevelDB 数据文件 SSTable 的结构

https://mp.weixin.qq.com/s/npFr16_RnxZVnJdaKpPu8w(5)8. LevelDB源码剖析之日志文件

https://www.jianshu.com/p/d1bb2e2ceb4c(6)9. LevelDB源码剖析之Current文件\Manifest文件\版本信息

https://www.jianshu.com/p/27e48eae656d

(6)庖丁解LevelDB之版本控制

https://www.jianshu.com/p/9bd10f32e38c(7)LevelDB 代码撸起来!

https://mp.weixin.qq.com/s/HzHxt7PgH2Cwv4PlIaz5WA

(7)LevelDB:使用介绍

https://www.jianshu.com/p/573c5706da0a本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。如有侵权请联系 cloudcommunity@tencent.com 删除前往查看https网络安全数据库sql编程算法本文分享自 作者个人站点/博客 前往查看如有侵权,请联系 cloudcommunity@tencent.com 删除。本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!https网络安全数据库sql编程算法评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0目录1.摘要2. LevelDB概述2.1 特点2.2 限制2.3 性能1.3.1 验证场景1.3.2 写性能1.3.3 读性能3. LevelDB框架4. LevelDB基础部件4.1 跳跃表SkipList4.2 SSTable4.2.1 Footer 结构4.2.2 Block 结构4.2.3 DataBlock 结构4.2.4 FilterBlock 结构4.4.5 MetaIndexBlock 结构4.4.6 IndexBlock 结构4.5 日志文件(LOG)4.5.1 日志数据结构4.5.2 Writer4.5.3 创建日志的时机4.6 版本控制5. LevelDB基本操作接口5.1 例子5.2 打开数据库5.3 写入一条数据5.4 读取一条数据5.5 批量原子修改5.6 范围查找5.7 关闭数据库5.8 Snapshot6. 参考相关产品与服务数据库云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!产品介绍2024新春采购节领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论100

LevelDB:轻量级高性能键-值存储引擎-腾讯云开发者社区-腾讯云

lDB:轻量级高性能键-值存储引擎-腾讯云开发者社区-腾讯云孟斯特LevelDB:轻量级高性能键-值存储引擎关注作者腾讯云开发者社区文档建议反馈控制台首页学习活动专区工具TVP最新优惠活动文章/答案/技术大牛搜索搜索关闭发布登录/注册首页学习活动专区工具TVP最新优惠活动返回腾讯云官网孟斯特首页学习活动专区工具TVP最新优惠活动返回腾讯云官网社区首页 >专栏 >LevelDB:轻量级高性能键-值存储引擎LevelDB:轻量级高性能键-值存储引擎孟斯特关注发布于 2023-10-19 16:56:114550发布于 2023-10-19 16:56:11举报文章被收录于专栏:code人生code人生LevelDB是一种快速的键-值存储库,由Google开发,用于提供高性能的数据持久性存储。它通常被用作支持各种应用程序的底层数据库引擎,包括分布式数据库、区块链、分布式文件系统等。在本文中,我们将深入介绍LevelDB的核心概念、用途、特点以及如何在Go编程语言中使用LevelDB。LevelDB的核心概念键-值存储LevelDB是一个键-值存储引擎,它允许你将数据存储为键值对的形式。每个键都是唯一的,与一个值相关联。这种简单的数据结构使得LevelDB在存储和检索数据时非常高效。LSM树LevelDB使用了一种称为LSM(Log-Structured Merge)树的数据结构来组织数据。LSM树的关键思想是将写入操作追加到一个日志文件(log file)中,然后以一种分层的方式将数据排序和合并到不同的存储层中。这种方式可以加快写入速度,并提供快速的检索性能。数据持久性LevelDB通过将数据写入磁盘文件来实现数据持久性。这意味着数据在关闭数据库后仍然可用,并且可以在系统重新启动后进行检索。LevelDB还支持数据的快照(snapshot),允许你在不中断数据库写入的情况下创建数据库的一致快照。LevelDB的用途LevelDB适用于各种应用程序,尤其适用于需要高性能键-值存储的场景,例如:1.分布式数据库: LevelDB可用作分布式数据库的存储引擎,用于存储和检索分布式系统中的数据。2.区块链: 许多区块链项目使用LevelDB来存储区块链的交易数据和状态信息。3.分布式文件系统: 分布式文件系统可以使用LevelDB来维护文件和元数据的索引。4.缓存: LevelDB可用作缓存层,用于存储频繁访问的数据,以减少对主存储的访问压力。5.日志记录系统: LevelDB的日志结构使其非常适合用于构建日志记录系统,以便快速记录和检索日志数据。LevelDB的特点LevelDB具有许多特点,使其成为流行的键-值存储引擎之一:1.高性能: LevelDB被设计成高性能的键-值存储引擎,适用于各种负载和访问模式。2.轻量级: LevelDB的代码库相对较小,易于集成到各种应用程序中。3.开源: LevelDB是一个开源项目,可以在许多不同的平台上使用,并且有大量的社区支持。4.数据持久性: LevelDB支持数据的持久性存储,确保数据不会丢失。5.并发支持: LevelDB支持多线程并发读取,但需要额外的同步机制来支持并发写入。6.快照: LevelDB允许创建数据的快照,以便在不中断写入操作的情况下进行检索。在Go中使用LevelDB在Go中使用LevelDB通常需要导入适当的LevelDB库,例如github.com/syndtr/goleveldb/leveldb。以下是一个使用LevelDB的简单示例:package main

import (

"fmt"

"log"

"github.com/syndtr/goleveldb/leveldb"

)

func main() {

// 打开或创建一个LevelDB数据库

db, err := leveldb.OpenFile("/path/to/db", nil)

if err != nil {

log.Fatal(err)

}

defer db.Close()

// 写入数据

err = db.Put([]byte("key1"), []byte("value1"), nil)

if err != nil {

log.Fatal(err)

}

// 读取数据

data, err := db.Get([]byte("key1"), nil)

if err != nil {

log.Fatal(err)

}

fmt.Printf("Value: %s\n", data)

}复制这只是一个简单的示例,LevelDB还提供了更多高级功能,如迭代器、批量写入等。你可以查阅LevelDB的官方文档和Go LevelDB库的文档以获取更多详细信息和示例。声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)[1]进行许可,使用时请注明出处。

Author: mengbin[2]

blog: mengbin[3]

Github: mengbin92[4]

cnblogs: 恋水无意[5]References[1] 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0): https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh

[2] mengbin: mengbin1992@outlook.com

[3] mengbin: https://mengbin.top

[4] mengbin92: https://mengbin92.github.io/

[5] 恋水无意: https://www.cnblogs.com/lianshuiwuyi/本文参与 腾讯云自媒体分享计划,分享自微信公众号。原始发表:2023-09-04,如有侵权请联系 cloudcommunity@tencent.com 删除数据库存储leveldb高性能数据本文分享自 孟斯特 微信公众号,前往查看如有侵权,请联系 cloudcommunity@tencent.com 删除。本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!数据库存储leveldb高性能数据评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0目录LevelDB的核心概念键-值存储LSM树数据持久性LevelDB的用途LevelDB的特点在Go中使用LevelDBReferences相关产品与服务对象存储对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。免费体验产品介绍产品文档COS新用户专享存储包低至1元,新老同享存储容量低至0.02元/GB/月,立即选购!

领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论00

谷歌系列-开源项目/leveldb

谷歌系列-开源项目/leveldb

登录

注册

开源

企业版

高校版

搜索

帮助中心

使用条款

关于我们

开源

企业版

高校版

私有云

Gitee AI

NEW

我知道了

查看详情

登录

注册

代码拉取完成,页面将自动刷新

捐赠

捐赠前请先登录

取消

前往登录

扫描微信二维码支付

取消

支付完成

支付提示

将跳转至支付宝完成支付

确定

取消

Watch

不关注

关注所有动态

仅关注版本发行动态

关注但不提醒动态

1

Star

1

Fork

0

谷歌系列-开源项目 / leveldb

代码

Issues

0

Pull Requests

0

Wiki

统计

流水线

服务

Gitee Pages

JavaDoc

PHPDoc

质量分析

Jenkins for Gitee

腾讯云托管

腾讯云 Serverless

悬镜安全

阿里云 SAE

Codeblitz

我知道了,不再自动展开

加入 Gitee

与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)

免费加入

已有帐号?

立即登录

返回

master

管理

管理

分支 (4)

标签 (21)

master

env_rename

doc-testing

reuse-manifest

1.23

1.22

1.21

v1.20

v1.19

v1.18

v1.17

v1.16

v1.15

v1.14

v1.13

v1.12

v1.11

v1.10

v1.9

v1.8

v1.7

v1.6

v1.5

v1.4

克隆/下载

克隆/下载

HTTPS

SSH

SVN

SVN+SSH

下载ZIP

登录提示

该操作需登录 Gitee 帐号,请先登录后再操作。

立即登录

没有帐号,去注册

提示

下载代码请复制以下命令到终端执行

为确保你提交的代码身份被 Gitee 正确识别,请执行以下命令完成配置

git config --global user.name userName

git config --global user.email userEmail

初次使用 SSH 协议进行代码克隆、推送等操作时,需按下述提示完成 SSH 配置

1

生成 RSA 密钥

2

获取 RSA 公钥内容,并配置到 SSH公钥 中

在 Gitee 上使用 SVN,请访问 使用指南

使用 HTTPS 协议时,命令行会出现如下账号密码验证步骤。基于安全考虑,Gitee 建议 配置并使用私人令牌 替代登录密码进行克隆、推送等操作

Username for 'https://gitee.com': userName

Password for 'https://userName@gitee.com':

#

私人令牌

新建文件

新建 Diagram 文件

新建子模块

上传文件

分支 4

标签 21

贡献代码

同步代码

创建 Pull Request

了解更多

对比差异

通过 Pull Request 同步

同步更新到分支

通过 Pull Request 同步

将会在向当前分支创建一个 Pull Request,合入后将完成同步

leveldb Team

Remove the / prefix from the recovery_te...

8e62cc5

363 次提交

提交

取消

提示:

由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件

benchmarks

保存

取消

cmake

保存

取消

db

保存

取消

doc

保存

取消

helpers/memenv

保存

取消

include/leveldb

保存

取消

issues

保存

取消

port

保存

取消

table

保存

取消

third_party

保存

取消

util

保存

取消

.appveyor.yml

保存

取消

.clang-format

保存

取消

.gitignore

保存

取消

.gitmodules

保存

取消

.travis.yml

保存

取消

AUTHORS

保存

取消

CMakeLists.txt

保存

取消

CONTRIBUTING.md

保存

取消

LICENSE

保存

取消

NEWS

保存

取消

README.md

保存

取消

TODO

保存

取消

Loading...

README

BSD-3-Clause

LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.

Authors: Sanjay Ghemawat (sanjay@google.com) and Jeff Dean (jeff@google.com)

Features

Keys and values are arbitrary byte arrays.

Data is stored sorted by key.

Callers can provide a custom comparison function to override the sort order.

The basic operations are Put(key,value), Get(key), Delete(key).

Multiple changes can be made in one atomic batch.

Users can create a transient snapshot to get a consistent view of data.

Forward and backward iteration is supported over the data.

Data is automatically compressed using the Snappy compression library.

External activity (file system operations etc.) is relayed through a virtual interface so users can customize the operating system interactions.

Documentation

LevelDB library documentation is online and bundled with the source code.

Limitations

This is not a SQL database. It does not have a relational data model, it does not support SQL queries, and it has no support for indexes.

Only a single process (possibly multi-threaded) can access a particular database at a time.

There is no client-server support builtin to the library. An application that needs such support will have to wrap their own server around the library.

Getting the Source

git clone --recurse-submodules https://github.com/google/leveldb.git

Building

This project supports CMake out of the box.

Build for POSIX

Quick start:

mkdir -p build && cd build

cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .

Building for Windows

First generate the Visual Studio 2017 project/solution files:

mkdir build

cd build

cmake -G "Visual Studio 15" ..

The default default will build for x86. For 64-bit run:

cmake -G "Visual Studio 15 Win64" ..

To compile the Windows solution from the command-line:

devenv /build Debug leveldb.sln

or open leveldb.sln in Visual Studio and build from within.

Please see the CMake documentation and CMakeLists.txt for more advanced usage.

Contributing to the leveldb Project

The leveldb project welcomes contributions. leveldb's primary goal is to be

a reliable and fast key/value store. Changes that are in line with the

features/limitations outlined above, and meet the requirements below,

will be considered.

Contribution requirements:

Tested platforms only. We generally will only accept changes for

platforms that are compiled and tested. This means POSIX (for Linux and

macOS) or Windows. Very small changes will sometimes be accepted, but

consider that more of an exception than the rule.

Stable API. We strive very hard to maintain a stable API. Changes that

require changes for projects using leveldb might be rejected without

sufficient benefit to the project.

Tests: All changes must be accompanied by a new (or changed) test, or

a sufficient explanation as to why a new (or changed) test is not required.

Consistent Style: This project conforms to the

Google C++ Style Guide.

To ensure your changes are properly formatted please run:

clang-format -i --style=file

Submitting a Pull Request

Before any pull request will be accepted the author must first sign a

Contributor License Agreement (CLA) at https://cla.developers.google.com/.

In order to keep the commit timeline linear

squash

your changes down to a single commit and rebase

on google/leveldb/master. This keeps the commit timeline linear and more easily sync'ed

with the internal repository at Google. More information at GitHub's

About Git rebase page.

Performance

Here is a performance report (with explanations) from the run of the

included db_bench program. The results are somewhat noisy, but should

be enough to get a ballpark performance estimate.

Setup

We use a database with a million entries. Each entry has a 16 byte

key, and a 100 byte value. Values used by the benchmark compress to

about half their original size.

LevelDB: version 1.1

Date: Sun May 1 12:11:26 2011

CPU: 4 x Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz

CPUCache: 4096 KB

Keys: 16 bytes each

Values: 100 bytes each (50 bytes after compression)

Entries: 1000000

Raw Size: 110.6 MB (estimated)

File Size: 62.9 MB (estimated)

Write performance

The "fill" benchmarks create a brand new database, in either

sequential, or random order. The "fillsync" benchmark flushes data

from the operating system to the disk after every operation; the other

write operations leave the data sitting in the operating system buffer

cache for a while. The "overwrite" benchmark does random writes that

update existing keys in the database.

fillseq : 1.765 micros/op; 62.7 MB/s

fillsync : 268.409 micros/op; 0.4 MB/s (10000 ops)

fillrandom : 2.460 micros/op; 45.0 MB/s

overwrite : 2.380 micros/op; 46.5 MB/s

Each "op" above corresponds to a write of a single key/value pair.

I.e., a random write benchmark goes at approximately 400,000 writes per second.

Each "fillsync" operation costs much less (0.3 millisecond)

than a disk seek (typically 10 milliseconds). We suspect that this is

because the hard disk itself is buffering the update in its memory and

responding before the data has been written to the platter. This may

or may not be safe based on whether or not the hard disk has enough

power to save its memory in the event of a power failure.

Read performance

We list the performance of reading sequentially in both the forward

and reverse direction, and also the performance of a random lookup.

Note that the database created by the benchmark is quite small.

Therefore the report characterizes the performance of leveldb when the

working set fits in memory. The cost of reading a piece of data that

is not present in the operating system buffer cache will be dominated

by the one or two disk seeks needed to fetch the data from disk.

Write performance will be mostly unaffected by whether or not the

working set fits in memory.

readrandom : 16.677 micros/op; (approximately 60,000 reads per second)

readseq : 0.476 micros/op; 232.3 MB/s

readreverse : 0.724 micros/op; 152.9 MB/s

LevelDB compacts its underlying storage data in the background to

improve read performance. The results listed above were done

immediately after a lot of random writes. The results after

compactions (which are usually triggered automatically) are better.

readrandom : 11.602 micros/op; (approximately 85,000 reads per second)

readseq : 0.423 micros/op; 261.8 MB/s

readreverse : 0.663 micros/op; 166.9 MB/s

Some of the high cost of reads comes from repeated decompression of blocks

read from disk. If we supply enough cache to the leveldb so it can hold the

uncompressed blocks in memory, the read performance improves again:

readrandom : 9.775 micros/op; (approximately 100,000 reads per second before compaction)

readrandom : 5.215 micros/op; (approximately 190,000 reads per second after compaction)

Repository contents

See doc/index.md for more explanation. See

doc/impl.md for a brief overview of the implementation.

The public interface is in include/leveldb/*.h. Callers should not include or

rely on the details of any other header files in this package. Those

internal APIs may be changed without warning.

Guide to header files:

include/leveldb/db.h: Main interface to the DB: Start here.

include/leveldb/options.h: Control over the behavior of an entire database,

and also control over the behavior of individual reads and writes.

include/leveldb/comparator.h: Abstraction for user-specified comparison function.

If you want just bytewise comparison of keys, you can use the default

comparator, but clients can write their own comparator implementations if they

want custom ordering (e.g. to handle different character encodings, etc.).

include/leveldb/iterator.h: Interface for iterating over data. You can get

an iterator from a DB object.

include/leveldb/write_batch.h: Interface for atomically applying multiple

updates to a database.

include/leveldb/slice.h: A simple module for maintaining a pointer and a

length into some other byte array.

include/leveldb/status.h: Status is returned from many of the public interfaces

and is used to report success and various kinds of errors.

include/leveldb/env.h:

Abstraction of the OS environment. A posix implementation of this interface is

in util/env_posix.cc.

include/leveldb/table.h, include/leveldb/table_builder.h: Lower-level modules that most

clients probably won't use directly.

Copyright (c) 2011 The LevelDB Authors. All rights reserved.

Redistribution and use in source and binary forms, with or without

modification, are permitted provided that the following conditions are

met:

* Redistributions of source code must retain the above copyright

notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above

copyright notice, this list of conditions and the following disclaimer

in the documentation and/or other materials provided with the

distribution.

* Neither the name of Google Inc. nor the names of its

contributors may be used to endorse or promote products derived from

this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT

OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT

LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY

THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE

OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Starred

1

Star

1

Fork

0

捐赠

0 人次

举报

举报成功

我们将于2个工作日内通过站内信反馈结果给你!

请认真填写举报原因,尽可能描述详细。

举报类型

请选择举报类型

举报原因

取消

发送

误判申诉

此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。

如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。

取消

提交

简介

暂无描述

展开

收起

暂无标签

/google-projects/leveldb

C++

等 3 种语言

C++

95.1%

C

3.1%

CMake

1.8%

BSD-3-Clause

使用 BSD-3-Clause 开源许可协议

保存更改

取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多

不能加载更多了

编辑仓库简介

简介内容

主页

取消

保存更改

1

https://gitee.com/google-projects/leveldb.git

git@gitee.com:google-projects/leveldb.git

google-projects

leveldb

leveldb

master

深圳市奥思网络科技有限公司版权所有

Git 大全

Git 命令学习

CopyCat 代码克隆检测

APP与插件下载

Gitee Reward

Gitee 封面人物

GVP 项目

Gitee 博客

Gitee 公益计划

Gitee 持续集成

OpenAPI

帮助文档

在线自助服务

更新日志

关于我们

加入我们

使用条款

意见建议

合作伙伴

售前咨询客服

技术交流QQ群

微信服务号

client#oschina.cn

企业版在线使用:400-606-0201

专业版私有部署:

13670252304

13352947997

开放原子开源基金会

合作代码托管平台

违法和不良信息举报中心

粤ICP备12009483号

简 体

/

繁 體

/

English

点此查找更多帮助

搜索帮助

Git 命令在线学习

如何在 Gitee 导入 GitHub 仓库

Git 仓库基础操作

企业版和社区版功能对比

SSH 公钥设置

如何处理代码冲突

仓库体积过大,如何减小?

如何找回被删除的仓库数据

Gitee 产品配额说明

GitHub仓库快速导入Gitee及同步更新

什么是 Release(发行版)

将 PHP 项目自动发布到 packagist.org

评论

仓库举报

回到顶部

登录提示

该操作需登录 Gitee 帐号,请先登录后再操作。

立即登录

没有帐号,去注册

Releases · google/leveldb · GitHub

Releases · google/leveldb · GitHub

Skip to content

Toggle navigation

Sign in

Product

Actions

Automate any workflow

Packages

Host and manage packages

Security

Find and fix vulnerabilities

Codespaces

Instant dev environments

Copilot

Write better code with AI

Code review

Manage code changes

Issues

Plan and track work

Discussions

Collaborate outside of code

Explore

All features

Documentation

GitHub Skills

Blog

Solutions

For

Enterprise

Teams

Startups

Education

By Solution

CI/CD & Automation

DevOps

DevSecOps

Resources

Learning Pathways

White papers, Ebooks, Webinars

Customer Stories

Partners

Open Source

GitHub Sponsors

Fund open source developers

The ReadME Project

GitHub community articles

Repositories

Topics

Trending

Collections

Pricing

Search or jump to...

Search code, repositories, users, issues, pull requests...

Search

Clear

Search syntax tips

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

Sign up

You signed in with another tab or window. Reload to refresh your session.

You signed out in another tab or window. Reload to refresh your session.

You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

google

/

leveldb

Public

Notifications

Fork

7.6k

Star

34.7k

Code

Issues

209

Pull requests

99

Actions

Security

Insights

Additional navigation options

Code

Issues

Pull requests

Actions

Security

Insights

Releases: google/leveldb

Releases

Tags

Releases · google/leveldb

1.23

23 Feb 21:51

cmumford

1.23

99b3c03

Compare

Choose a tag to compare

View all tags

1.23

Latest

Latest

Sync MANIFEST before closing in db_impl when creating a new DB. Add logging with debugging information when failing to load a version set.

Optimize leveldb block seeks to utilize the current iterator location. This is beneficial when iterators are reused and seeks are not random but increasing. It is additionally beneficial with larger block sizes and keys with common prefixes.

Merge pull request #862 from rex4539:https

Documentation fixes

Merge pull request #855 from cmumford/submodule-fix

(test) Merge pull request #853 from cmumford:benchmark

Merge pull request #854 from cmumford:printf-fix

(cmumford/printf-fix) Fixed fprintf of 64-bit value.

(cmumford/benchmark) Added google/benchmark submodule.

Internal test cleanup

Internal cleanup migrating StatusOr.

Merge pull request #822 from jl0x61:bugFix

Merge pull request #819 from wzk784533:master

avoid unnecessary memory copy

Merge pull request #798 from lntotk:master

Fix accidental double std:: qualifiers.

Add some std:: qualifiers to types and functions.

Switch from C headers to C++ headers.

change const to constexpr

remove unnessary status judge

Remove leveldb::port::kLittleEndian.

Remove Windows workarounds in some tests.

Add Env::Remove{File,Dir} which obsolete Env::Delete{File,Dir}.

Defend against inclusion of windows.h in tests that invoke Env::DeleteFile.

Add WITHOUT ROWID to SQLite benchmark.

Merge pull request #756 from pwnall/third_party_2

Switch testing harness to googletest.

Move CI to Visual Studio 2019.

Allow different C/C++ standards when this is used as a subproject.

Align CMake configuration with related projects.

Remove redundant PROJECT_SOURCE_DIR usage from CMake config.

Fix installed target definition.

Added return in Version::Get::State::Match to quiet warning.

Using CMake's check_cxx_compiler_flag to check support for -Wthread-safety.

Fix tsan problem in env_test.

Merge pull request #698 from neal-zhu:master

Simplify unlocking in DeleteObsoleteFiles.

Add "leveldb" subdirectory to public include paths.

Align EnvPosix and EnvWindows.

Disable exceptions and RTTI in CMake configuration.

cache Saver in State object

fix bug(uninitialized options pointer in State)

remove TODO in Version::ForEachOverlapping

use ForEachOverlapping to impl Get

Merge pull request #386 from ivanabc:master

unsigned char -> uint8_t

Add explicit typecasts to avoid compiler warning.

Guard DBImpl::versions_ by mutex_.

Converted two for-loops to while-loops.

Switch to using C++ 11 override specifier.

Added unit test for InternalKey::DecodeFrom with empty string.

Merge pull request #411 from proller:assert1

Using std::ostringstream in key DebugString.

Merge pull request #457 from jellor:patch-2

Fix EnvPosix tests on Travis CI.

Merge pull request #624 from adam-azarchs:master

Clean up util/coding.{h,cc}.

Initialize Stats::start_ before first use in Stats::Start().

Merge pull request #365 from allangj:c-strict-prototypes

Add argument definition for void c functions.

Consolidate benchmark code to benchmarks/.

Convert missed virtual -> override in db_test.cc.

Merge pull request #679 from smartxworks:optimize-readseq

Merge pull request #278 from wankai:master

don't check current key in DBIter::Next()

Add O_CLOEXEC to open calls.

broken db: fix assertion in leveldb::InternalKey::Encode, mark base as corrupt

set const property

reduce lock's range in DeleteObsoleteFiles

block_builder header file dependency fixed

Assets

2

26

ichengzi, mujiatong, venjin, nmarandi, Mita1w, chuyue86, Luke-Hiwalker4, zhangm365, lyingnet, maryitueq, and 16 more reacted with thumbs up emoji

❤️

3

CN-Scars, mannonov, and Kandyduh1 reacted with heart emoji

8

UtkarshChandel, THATGUYCULA, changleicn, Roob0910, CN-Scars, mannonov, tasshuu, and mosa1010mmmm reacted with rocket emoji

All reactions

26 reactions

❤️

3 reactions

8 reactions

31 people reacted

Release 1.22

03 May 21:16

cmumford

1.22

78b39d6

Compare

Choose a tag to compare

View all tags

Release 1.22

Corrected formatting to be compliant with the Google C++ Style Guide.

Specifically export the WriteBatch::Handler inner class for Windows link.

Merge pull request #665 from cheng-chang:coding.

Merge pull request #669 from pavel-pimenov:fix-readme-windows-mkdir.

Merge pull request #472 from zhoudayang:patch-1.

Merge pull request #339 from richcole-at-amazon:master.

Restore soname versioning with CMake build.

Other miscellaneous cleanups, fixes, and improvements.

Assets

2

All reactions

Release 1.21

29 Mar 22:01

cmumford

1.21

56178dd

Compare

Choose a tag to compare

View all tags

Release 1.21

Switched to using Copybara for project synchronization.

Minor cleanup in ports.

Silence unused argument warnings in MSVC.

Add tests for empty keys and values.

Switch corruption_test to use InMemEnv.

Replace AtomicPointer with std::atomic.

Make InMemoryEnv more consistent with filesystem based Env's.

Align windows_logger with posix_logger.

Improve CI configuration and added AppVeyor (Windows CI) badge to README.

Added native support for Windows.

Make WriteBatch::ApproximateSize() const.

Fix PosixWritableFile::Sync() on Apple systems.

Fix fdatasync() feature detection in opensource build.

C++11 cleanup for util/mutexlock.h.

Rework threading in env_posix.cc.

Remove InitOnce from the port API.

Expose WriteBatch::Append().

Fix documentation for log file growth.

Add move constructor to Status.

Replace port_posix with port_stdcxx.

Reimplement ConsumeDecimalNumber.

Document the building process.

Replace NULL with nullptr in C++ files.

Remove PLATFORM_IS_LITTLE_ENDIAN from port/posix.h.

Add more thread safety annotations.

Require C++11.

Replace SIZE_MAX with std::numeric_limits.

Add CMake build support.

Enable thread safety annotations.

leveldb::DestroyDB will now delete empty directories.

Replace SSE-optimized CRC32C in POSIX port with external library.

Fix file writing bug in CL 170738066.

Fix use of uninitialized value in LRUHandle.

Fix issue #474: a race between the f*_unlocked() STDIO calls in env_posix.cc and concurrent application calls to fflush(NULL).

Use __APPLE__ instead of OS_MACOS. The former is compiler-provided.

Report missing CURRENT manifest file as database corruption.

LevelDB: Add WriteBatch::ApproximateSize().

Other minor fixes, code cleanup, and documentation improvements.

Assets

2

All reactions

Release 1.20

02 Mar 00:18

cmumford

v1.20

a53934a

Compare

Choose a tag to compare

View all tags

Release 1.20

Convert documentation to markdown.

Implement support for Intel crc32 instruction (SSE 4.2).

Based on #309.

Limit the number of read-only files the POSIX Env will have open.

Add option for maximum file size.

Assets

2

2

songlong and 77553322AHST reacted with hooray emoji

All reactions

2 reactions

2 people reacted

Release 1.19

11 Aug 16:30

cmumford

v1.19

3080a45

Compare

Choose a tag to compare

View all tags

Release 1.19

A snappy change broke test assumptions about the size of compressed output. Fixed.

Fix problems in LevelDB's caching code.

Fix LevelDB build when asserts are enabled in release builds. (#367).

Change std::uint64_t to uint64_t (#354).

Fixes a bug encountered when reading records from leveldb files that have been split, as in a [] input task split.

Deleted redundant null ptr check prior to delete. (#338).

Fix signed/unsigned mismatch on VC++ builds.

Putting build artifacts in subdirectory.

Added continuous build integration via Travis CI.

log compaction output file's level along with number.

Misc. improvements to README file.

Fix Android/MIPS build (#115).

Only compiling TrimSpace on linux (#310).

Use xcrun to determine Xcode.app path instead of using a hardcoded path.

Add "approximate-memory-usage" property to leveldb::DB::GetProperty.

Add leveldb::Cache::Prune.

Fix size_t/int comparison/conversion issues.

Added leveldb::Status::IsInvalidArgument() method.

Suppress error reporting after seeking but before a valid First or Full record is encountered.

#include -> (#280).

Now attempts to reuse the preceding MANIFEST and log file when re-opened.

Add benchmark that measures cost of repeatedly opening the database.

Added a new fault injection test.

Add arm64 support to leveldb.

Assets

2

All reactions

Release 1.18

16 Sep 21:24

cmumford

v1.18

803d692

Compare

Choose a tag to compare

View all tags

Release 1.18

Update version number to 1.18

Replace the basic fprintf call with a call to fwrite in order to

work around the apparent compiler optimization/rewrite failure that we are

seeing with the new toolchain/iOS SDKs provided with Xcode6 and iOS8.

Fix ALL the header guards.

Createed a README.md with the LevelDB project description.

A new CONTRIBUTING file.

Don't implicitly convert uint64_t to size_t or int. Either preserve it as

uint64_t, or explicitly cast. This fixes MSVC warnings about possible value

truncation when compiling this code in Chromium.

Added a DumpFile() library function that encapsulates the guts of the

"leveldbutil dump" command. This will allow clients to dump

data to their log files instead of stdout. It will also allow clients to

supply their own environment.

leveldb: Remove unused function 'ConsumeChar'.

leveldbutil: Remove unused member variables from WriteBatchItemPrinter.

OpenBSD, NetBSD and DragonflyBSD have _LITTLE_ENDIAN, so define

PLATFORM_IS_LITTLE_ENDIAN like on FreeBSD. This fixes:

issue #143

issue #198

issue #249

Switch from to . The former never made it into the

standard and doesn't exist in modern gcc versions at all. The later contains

everything that leveldb was using from the former.

This problem was noticed when porting to Portable Native Client where no memory

barrier is defined. The fact that is missing normally goes

unnoticed since memory barriers are defined for most architectures.

Make Hash() treat its input as unsigned. Before this change LevelDB files

from platforms with different signedness of char were not compatible. This

change fixes: issue #243

Verify checksums of index/meta/filter blocks when paranoid_checks set.

Invoke all tools for iOS with xcrun. (This was causing problems with the new

XCode 5.1.1 image on pulse.)

include only once, and fix the following linter warning:

"Found C system header after C++ system header"

When encountering a corrupted table file, return Status::Corruption instead of

Status::InvalidArgument.

Support cygwin as build platform, patch is from https://code.google.com/p/leveldb/issues/detail?id=188

Fix typo, merge patch from https://code.google.com/p/leveldb/issues/detail?id=159

Fix typos and comments, and address the following two issues:

issue #166

issue #241

Add missing db synchronize after "fillseq" in the benchmark.

Removed unused variable in SeekRandom: value (issue #201)

Assets

2

All reactions

Release 1.17

15 Sep 16:57

cmumford

v1.17

e353fbc

Compare

Choose a tag to compare

View all tags

Release 1.17

Cleanup: delete unused IntSetToString

It was added in http://cr/19491949 (and was referenced at the time).

The last reference was removed in http://cr/19507363.

This fixes warning/error with pre-release crosstoolv18:

'std::string leveldb::{anonymous}::IntSetToString(const std::set&)' defined but not used [-Werror=unused-function]

Added arm64 and and armv7s to IOS build as suggested on leveldb mailing list.

Changed local variable type from int to size_t

This eliminates compiler warning/error and resolves issue #146

Assets

2

All reactions

Release 1.16

15 Sep 17:07

cmumford

v1.16

269fc6c

Compare

Choose a tag to compare

View all tags

Release 1.16

Make Log::Reader not report a corruption when the last record in a log file is truncated.

Fix issue #230: variable created but not utilized.

Remove comment that referenced a removed feature.

Assets

2

All reactions

Release 1.15

15 Sep 17:04

cmumford

v1.15

0cfb990

Compare

Choose a tag to compare

View all tags

Release 1.15

switched from mmap based writing to simpler stdio based writing. Has a minor impact (0.5 microseconds) on microbenchmarks for asynchronous writes. Synchronous writes speed up from 30ms to 10ms on linux/ext4.

Should be much more reliable on diverse platforms.

compaction errors now immediately put the database into a read-only mode (until it is re-opened). As a downside, a disk going out of space and then space being created will require a re-open to recover from, whereas previously that would happen automatically. On the plus side, many corruption possibilities go away.

force the DB to enter an error-state so that all future writes fail when a synchronous log write succeeds but the sync fails.

repair now regenerates sstables that exhibit problems

fix issue #224 - Use native memory barriers on OSX

fix issue #218 - QNX build is broken

fix build on iOS with xcode 5

make tests compile and pass on windows

Assets

2

All reactions

Release 1.14

15 Sep 17:13

cmumford

v1.14

0b9a89f

Compare

Choose a tag to compare

View all tags

Release 1.14

Fix issues #206, #207

Also,

Fix link to bigtable paper in docs.

New sstables will have the file extension .ldb. .sst files will continue to be recognized.

When building for iOS, use xcrun to execute the compiler. This may affect issue #183.

Assets

2

All reactions

Previous 1 2 3 Next

Previous Next

Footer

© 2024 GitHub, Inc.

Footer navigation

Terms

Privacy

Security

Status

Docs

Contact

Manage cookies

Do not share my personal information

You can’t perform that action at this time.

键值数据库LevelDB的优缺点及性能分析-腾讯云开发者社区-腾讯云

库LevelDB的优缺点及性能分析-腾讯云开发者社区-腾讯云IT阅读排行榜键值数据库LevelDB的优缺点及性能分析关注作者腾讯云开发者社区文档建议反馈控制台首页学习活动专区工具TVP最新优惠活动文章/答案/技术大牛搜索搜索关闭发布登录/注册首页学习活动专区工具TVP最新优惠活动返回腾讯云官网IT阅读排行榜首页学习活动专区工具TVP最新优惠活动返回腾讯云官网社区首页 >专栏 >键值数据库LevelDB的优缺点及性能分析键值数据库LevelDB的优缺点及性能分析IT阅读排行榜关注发布于 2022-01-20 14:23:212.7K0发布于 2022-01-20 14:23:21举报文章被收录于专栏:华章科技华章科技导读:LevelDB是一种为分布式而生的键-值数据库。作者:廖环宇 张仕华来源:大数据DT(ID:hzdashuju)01 LevelDB的特性LevelDB是一个C++语言编写的高效键-值嵌入式数据库,目前对亿级的数据也有着非常好的读写性能。虽然LevelDB有着许多键-值数据库所不具备的优秀特性,但是与Redis等一些主流键-值数据库相比也有缺陷。本节将对LevelDB的优缺点进行具体阐述。LevelDB的优点体现在:key与value采用字符串形式,且长度没有限制;数据能持久化存储,同时也能将数据缓存到内存,实现快速读取;基于key按序存放数据,并且key的排序比较函数可以根据用户需求进行定制;支持简易的操作接口API,如Put、Get、Delete,并支持批量写入;可以针对数据创建数据内存快照;支持前向、后向的迭代器;采用Google的Snappy压缩算法对数据进行压缩,以减少存储空间;基本不依赖其他第三方模块,可非常容易地移植到Windows、Linux、UNIX、Android、iOS。LevelDB的缺点体现在:不是传统的关系数据库,不支持SQL查询与索引;只支持单进程,不支持多进程;不支持多种数据类型;不支持客户端-服务器的访问模式。用户在应用时,需要自己进行网络服务的封装。读者可以综合LevelDB的优缺点,有针对性地评估其是否适用于实际开发的项目/产品,并对最终是否使用进行决定。02 LevelDB的性能分析在LevelDB的源码中,笔者写了一段用于测试LevelDB性能的代码(db_bench.cc)。经过编译后,生成用于性能测试的可执行程序db_bench。通过运行该性能测试程序,用户能直观地了解LevelDB在海量数据读写方面的性能。可为测试程序db_bench指定相关测试参数,也可以选择默认参数。db_bench在默认的测试参数下读写百万级别的数据时,每一个数据的key占用16字节,value占用100字节(启用压缩后,value占用50字节,即压缩率为50%)。db_bench主要针对读与写两个方面进行测试。写性能测试项具体如下。Fillseq:以顺序写的方式创建一个新的数据库。Fillrandom:以随机写的方式创建一个新的数据库。Overwrite:以随机写的方式更新数据库中某些存在的key的数据。Fillsync:每一次写操作,均将数据同步写到磁盘中才算操作完成;而对于上述3种其他的写操作,只是将需要写的数据送入操作系统的缓冲区就算成功。读性能测试项具体如下。Readrandom:以随机的方式进行查询读。Readseq:按正向顺序读。Readreverse:按逆向顺序读。在终端中输入命令执行db_bench,测试程序即可进行相应的读写操作,并记录相应的性能数据。$ ./db_bench复制针对上述的几个测试项,表1-1对比了LevelDB官方发布的与笔者实际测试的结果。两者硬件测试环境不同,因而相应测试项的数据也不相同。但总体而言,可以得知LevelDB读写性能的优异。▼表1-1 LevelDB测试数据此外,为了更好地测试比较LevelDB的实际性能,Google的工程师也将LevelDB与另外两种数据库(SQLite3和Kyoto TreeDB)进行了对比。经过测试证明,LevelDB相较于另外两种数据库,无论是在基本操作环境下,还是在某些特定配置环境下,均具有非常优秀的读写性能。具体测试结果,可以参见源码中的leveldb/doc/benchmark.html。关于作者:廖环宇,中南大学硕士、阿里云高级算法专家与工业大脑算法团队负责人,长期致力于人工智能算法与工业大数据技术的研究和实践,主导过许多大型数据智能平台的开发与应用。张仕华,贝壳找房资深软件开发工程师,毕业于北京科技大学,曾就职于360、滴滴。热衷源码研究与探究技术本质,合著有《Redis 5设计与源码分析》。本文摘编自《精通LevelDB》,经出版方授权发布。(ISBN:9787111693260)本文参与 腾讯云自媒体分享计划,分享自微信公众号。原始发表:2021-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除腾讯云测试服务性能测试数据库sql本文分享自 大数据DT 微信公众号,前往查看如有侵权,请联系 cloudcommunity@tencent.com 删除。本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!腾讯云测试服务性能测试数据库sql评论登录后参与评论0 条评论热度最新登录 后参与评论推荐阅读LV.关注文章0获赞0相关产品与服务数据库云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!产品介绍2024新春采购节领券社区专栏文章阅读清单互动问答技术沙龙技术视频团队主页腾讯云TI平台活动自媒体分享计划邀请作者入驻自荐上首页技术竞赛资源技术周刊社区标签开发者手册开发者实验室关于社区规范免责声明联系我们友情链接腾讯云开发者扫码关注腾讯云开发者领取腾讯云代金券热门产品域名注册云服务器区块链服务消息队列网络加速云数据库域名解析云存储视频直播热门推荐人脸识别腾讯会议企业云CDN加速视频通话图像分析MySQL 数据库SSL 证书语音识别更多推荐数据安全负载均衡短信文字识别云点播商标注册小程序开发网站监控数据迁移Copyright © 2013 - 2024 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有 深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569腾讯云计算(北京)有限责任公司 京ICP证150476号 |  京ICP备11018762号 | 京公网安备号11010802020287问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档Copyright © 2013 - 2024 Tencent Cloud.All Rights Reserved. 腾讯云 版权所有登录 后参与评论00