LevelDB使用指南

2019-07-12 23:40发布

这篇文章是levelDB官方文档的译文,原文地址:LevelDB library documentation这篇文章主要讲leveldb接口使用和注意事项。 
leveldb是一个持久型的key-value数据库。key,value可以是任意的字节数组,key之间是有序的。key的比较函数可以由用户指定。

1. 打开数据库

leveldb使用文件系统目录名作为name,并把数据库所有内容都存储在这个目录中。这是个打开数据库,并且指定如果数据库不存在就新建的例子: #include #include "leveldb/db.h" leveldb::DB* db; leveldb::Options options; options.create_if_missing = true; leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); assert(status.ok()); ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
如果想在创建数据库时发现已经存在就报错,那么调用leveldb::DB::Open之前添加下面这一行: options.error_if_exists = true;
  • 1

2. 返回值状态Status

你也许已经注意到上面的leveldb::Status type . 这种类型是leveldb大多数函数的返回值,函数的返回值有可能是error。可以通过判断result是不是ok来判断: leveldb::Status s = ...; if (!s.ok()) cerr << s.ToString() << endl;
  • 1
  • 2

3. 关闭数据库

当数据库操作完成,关闭数据库只需要删除数据库对象: ... open the db as described above ... ... do something with db ... delete db;
  • 1
  • 2
  • 3

4. 读和写

数据库提供Put, Delete, and Get函数来修改查询数据库。下面的例子把key1的值赋给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);
  • 1
  • 2
  • 3
  • 4

5. 原子性更新

如果上面的代码中,再给key2赋值之后,删除key1之前,进程退出了,那么key1 and key2就会有相同的值。这种情况可以使用WriteBatch class来原子性的进行一系列的更新: #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); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
WriteBatch是一系列对数据库的更新操作,并且这些批量操作之间有一定的顺序性。注意到我们虽然在给key2赋值之前删除,使用writebtch最终并不会错误的造成vaue丢失。 
撇开writebatch带来的原子性优势,writebatch也能通过把多个更新放在一个批量操里面来加速操作。

6. 同步写

通常情况下,所有的leveldb写操作都是异步的:当leveldb把写操作交个操作系统之后就返回。从操作系统内存到硬盘等持久性存储是异步的。如果在写的时候打开同步写选项,那么只有当数据持久化到硬盘之后才会返回。(On Posix systems, this is implemented by calling either fsync(...) or fdatasync(...) or msync(..., MS_SYNC) before the write operation returns.) leveldb::WriteOptions write_options; write_options.sync = true; db->Put(write_options, ...);
  • 1
  • 2
  • 3
异步写通常比同步写快1000倍以上。异步写的不足就是当机器宕机时会丢失最后更新的数据。写进程的异常退出并不会造成数据的丢失。通常情况下异步写能够被妥善的处理。例如,当你在网数据库写大量的数据时,在机器宕机之后能通过重新写一次数据来修复。混合使用同步和异步也是可以的。例如每N次写做一次同步。当机器宕机的时候,只需要重新写最后一次同步写之后的数据。同步写一个新增一个标记来记录上一次同步写的位置。WriteBatch是一个异步写。一个WriteBatch内部的多个更新操作放在一起也可以使用同步写操作,(i.e., write_options.sync is set to true). 可以通过批量操作降低同步写的消耗。

7. 并发

一个数据库每次只能被一个进程打开。leveldb为了防止误操作需要一个lock。在一个进程内部,同一个leveldb::DB对象可以在这个进程的多个并发线程之间安全的共享。 例如,不同的线程可以写,获取指针,或者读取相同的数据库,而不需要额外的同步操作,因为leveldb自动做了请求的同步。然而,其他的对象,例如迭代器或者WriteBatch,需要外部的同步操作。如果两个线程共享同一个这样的对象,那么他们必须用自己的lock protocal对数据库操作进行保护。这在公共的header文件里有更详细的内容。

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
处理[start,limit)范围内的key: for (it->Seek(start); it->Valid() && it->key().ToString() < limit; it->Next()) { ... }
  • 1
  • 2
  • 3
  • 4
  • 5
逆序处理:(逆序会比顺序慢一些) for (it->SeekToLast(); it->Valid(); it->Prev()) { ... }
  • 1
  • 2
  • 3

9. Snapshots快照

快照在整个key-value存储状态上提供了一个持久性的只读视图。非空的ReadOptions::snapshot提供了一个针对db特定状态的只读视图。如果ReadOptions::snapshot是NULL,那么读操作是在对当前数据库状态的隐式视图上的进行的。使用DB::GetSnapshot()方法创建Snapshots: 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
如果快照不再需要了,应该使用DB::ReleaseSnapshot接口来释放,这会消除为了维持快照的状态多与操作。

10. Slice分片

上面代码中it->key() and it->value() 调用的返回值都是leveldb::Slice类型的实例,slice是一个包含长度和一个纸箱字节数组的简单结构体。因为我们不需要每次都复制很多的keys和values,所以返回Slice比返回std::string是一个更好的选择。另外,level-db不返回以null结尾的c类型的字符串,是因为leveldb允许key和value中包含'