初识LevelDB
认识LevelDB & 源码下载编译
LevelDB是 Google 编写的key-value存储库,提供从Key到Value的有序映射。
LevelDB的代码量相比其他开源项目较少,除了测试之外大约有不到两万行代码。
Mac源码下载和编译运行
LevelDB下载地址:https://github.com/google/leveldb
- 下载与编译
git clone https://github.com/google/leveldb.git
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .
注意:上述命令编译的是Release版本,如果想要Debug,最后一条命令可以改为
cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build .
运行报错
CMake Error at CMakeLists.txt:299 (add_subdirectory):
The source directory
/home/xie/cpp/leveldb/third_party/googletest
does not contain a CMakeLists.txt file.
CMake Error at CMakeLists.txt:304 (add_subdirectory):
The source directory
/home/xie/cpp/leveldb/third_party/benchmark
does not contain a CMakeLists.txt file.
......
这是因为third_party下没有googletest和benchmark。
方法一:需要手动把这两个下载下来:
cd third_party
git clone https://github.com/google/benchmark.git
git clone https://github.com/google/googletest.git
方法二:自动下载
git submodule update --init
然后执行cmake命令就可以了。
- 执行测试
首先可以使用leveldb/benchmarks目录下的文件进行测试。
snappy/crc32c/zstd/tcmalloc没有lib
snappy: levelDB中会使用snappy压缩算法来对数据进行压缩,能够在不影响读写性能的情况下减小数据存储空间。压缩速度为 250 MB/秒及以上,无需汇编代码。解压缩时会检测压缩流中是否存在错误。
# snappy的安装和编译
git clone https://github.com/google/snappy.git
cd snappy
git submodule update --init
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ../
make
然后在CMakeLists.txt中修改SNAPPY的配置。找到**check_library_exists(snappy snappy_compress “” HAVE_SNAPPY)**的位置,然后添加以下参数:
link_directories("/Users/julia/CLionProjects/others/snappy/build")
include_directories("/Users/julia/CLionProjects/others/snappy" "/Users/julia/CLionProjects/others/snappy/build")
#注意此行为原来的配置
check_library_exists(snappy snappy_compress "" HAVE_SNAPPY)
#结束
set(HAVE_SNAPPY ON)
然后重新编译LevelDB,运行db_bench得到如下结果:
可以看到snappy是启用的。
其次,我们可以自己写Demo进行测试。在CMakeLists所在目录下创建一个demo进行测试。
//
// Created by Julia on 2024/8/18.
//
#include <cstdio>
#include <iostream>
#include "leveldb/my_comparator.h"
#include "include/leveldb/db.h"
#include "include/leveldb/write_batch.h"
int main() {
// Open a database.
leveldb::DB* db;
leveldb::Options opt;
opt.create_if_missing = true;
leveldb::Status status = leveldb::DB::Open(opt, "../db/testdb", &db);
assert(status.ok());
// Write data.
status = db->Put(leveldb::WriteOptions(), "test_name", "sjl");
assert(status.ok());
// Read data.
std::string value;
status = db->Get(leveldb::ReadOptions(), "test_name", &value);
assert(status.ok());
std::cout << value << std::endl;
// Delete data.
status = db->Delete(leveldb::WriteOptions(), "test_name");
assert(status.ok());
// Atomic Updates
status = db->Put(leveldb::WriteOptions(), "key1", "26");
assert(status.ok());
std::string value1;
status = db->Get(leveldb::ReadOptions(), "key1", &value1);
if(status.ok()) {
leveldb::WriteBatch batch;
batch.Delete("key1");
batch.Put("key2", value1);
status = db->Write(leveldb::WriteOptions(), &batch);
}
// Synchronous Writes
// leveldb默认是异步写入,开启sync是会使写操作一直被数据传输到底层存储器后再返回。
leveldb::WriteOptions write_potions;
write_potions.sync = true;
db->Put(write_potions, "key2", "girl");
write_potions.sync = false;
// Iteration 迭代打印键值对
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());
delete it;
// 打印[start, limit)
leveldb::Iterator* rangeIt = db->NewIterator(leveldb::ReadOptions());
for (rangeIt->Seek("key2"); rangeIt->Valid() && rangeIt->key().ToString() < "key3"; rangeIt->Next()) {
std::cout << rangeIt->key().ToString() << ":" << rangeIt->value().ToString() << std::endl;
}
// 反向迭代
for(rangeIt->SeekToLast(); rangeIt->Valid(); rangeIt->Prev()) {
std::cout << rangeIt->key().ToString() << ":" << rangeIt->value().ToString() << std::endl;
}
delete rangeIt;
// Snapshot
leveldb::ReadOptions options;
options.snapshot = db->GetSnapshot();
db->Put(write_potions, "key2", "new_girl");
leveldb::Iterator* itSnap = db->NewIterator(options);
itSnap->Seek("key2");
std::cout << itSnap->key().ToString() << ":" << itSnap->value().ToString() << std::endl;
delete itSnap;
db->ReleaseSnapshot(options.snapshot);
// Slice: it->key(), it->value() 返回的值就是Slice类型,Slice类型包含一个length和一个指向外部字节数组的指针。
leveldb::Slice s1 = "hello";
std::string str("world");
leveldb::Slice s2 = str;
std::string str2 = s1.ToString();
assert(str2 == std::string("hello"));
// Close
delete db;
// Comparators
MyComparator cmp;
leveldb::DB* db2;
leveldb::Options options2;
options2.create_if_missing = true;
options2.comparator = &cmp;
leveldb::Status status1 = leveldb::DB::Open(options2, "../db/myComparatorDB2", &db2);
assert(status1.ok());
leveldb::WriteBatch batch2;
batch2.Put("1", "1");
batch2.Put("2", "2");
batch2.Put("3", "3");
status1 = db2->Write(leveldb::WriteOptions(), &batch2);
assert(status1.ok());
leveldb::Iterator* it2 = db2->NewIterator(leveldb::ReadOptions());
for (it2->SeekToFirst(); it2->Valid(); it2->Next()) {
std::cout << it2->key().ToString() << ":" << it2->value().ToString() << std::endl;
}
delete it2;
delete db2;
// Compression :
// options.compression = leveldb::kNoCompression;
// Cache :
// options.cache = leveldb::NewLRUCache(100 * 1048576); // 100MB cache
// delete options.cache;
// 缓存里存放的时未压缩的数据
// 禁用缓存
// options.fill_cache = false;
// BloomFilter : 通过对key增加数据位来减少磁盘读取的操作次数
// options.filter_policy = NewBloomFilterPolicy(10);
// delete options.filter_policy;
}
//
// Created by Julia on 2024/8/20.
//
#include "comparator.h"
#include "slice.h"
#ifndef LEVELDB_MY_COMPARATOR_H
#define LEVELDB_MY_COMPARATOR_H
#endif // LEVELDB_MY_COMPARATOR_H
class MyComparator : public leveldb::Comparator {
public:
int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const override {
return leveldb::BytewiseComparator()->Compare(b, a);
}
// Ignore the following methods for now:
const char* Name() const override { return "MyComparator"; }
void FindShortestSeparator(std::string*, const leveldb::Slice&) const override{ }
void FindShortSuccessor(std::string*) const override{ }
};