LevelDB學習筆記 (1):初識LevelDB
- 2021 年 7 月 3 日
- 筆記
- C++, LevelDB, LevelDB學習筆記, 資料庫
LevelDB學習筆記 (1):初識LevelDB
1. 寫在前面
1.1 什麼是levelDB
LevelDB就是一個由Google開源的高效的單機Key/Value存儲系統,該存儲系統提供了Key到Value的有序映射。
1.2 為什麼要學levelDB
學習源碼算是一種很好的學習方式,準備精讀幾個經典的開源程式碼,那學習levelDB的原因主要有下
- Google開源,程式碼品質非常高,而且只有1w行(去掉測試程式碼)不是過於複雜
- 現在主流的kv存儲系統有很多基於levelDB或者借鑒了levelDB的思想
2. Linux/Mac下的編譯運行
1. Quick start
下面來自於官方文檔
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . #release版本
cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . #debug版本
在類linux系統下編譯非常簡單,上面兩行就可以了
不過這裡編譯可能會遇到一些問題

比如我遇到了上面的問題,這是因為在third_party這個文件夾內缺少googletest和benchmark。
這裡手動下載一下這兩個文件把這個目錄下的空文件替換了就好了。然後在執行cmake即可
上面是對應的github地址,直接點進去git clone就行,當然最近的github好像git clone總是出問題,我是直接下載zip然後替換過去的。
2. Quick test
新建一個文件夾叫my_test。添加一個新的cpp文件run_test.cpp
隨後修改CMakeList.txt里添加關於新測試文件的規則。就可以了
我們準備寫一個運行的測試文件,來創建一個db,隨後進行添加和刪除數據
#include <iostream>
#include <cassert>
#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, "../tmp/testdb", &db);
assert(status.ok());
// Write data.
status = db->Put(leveldb::WriteOptions(), "name", "zxl");
assert(status.ok());
// Read data.
std::string val;
status = db->Get(leveldb::ReadOptions(), "name", &val);
assert(status.ok());
std::cout << val << std::endl;
}
上面的程式碼就是我們在tmp文件下創建一個testdb。然後寫入一條數據 key = “name” value = “zxl”。這個時候我們執行get操作就會得到我們剛才寫入的值

3. LevelDB的基本操作
關於資料庫的創建以及讀寫操作我們上面以及提過了
3.1 原子更新-Atomic Updates
下面的例子來自官方文檔
考慮下面這一種情況,假如進程在Put key2 和 Delete key1兩個操作之間結束,那麼key1和key2這兩個不同的鍵值將存儲相同的值,這和我們的意願相違背。為了避免這種情況的出現,leveldb利用WriteBatch來進行具有原子性的更新。
td::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);
#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操作。
WriteBatch類除了原子性的優勢外,也可以用於通過將大量個體變動放置在同一批次中而加速批量更新。
3.2 迭代 – Iteration
下面的例子來自官方文檔
下面的例子演示了如何從資料庫中成對的列印鍵值:
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()) {
...
}
好下面結合迭代和剛才學到的原子更新自己寫一個測試文件
- 順序全體迭代
// test Atomic Updates
leveldb::WriteBatch batch;
batch.Delete("name");
batch.Put("name0", "zxl0");
batch.Put("name1", "zxl1");
batch.Put("name2", "zxl2");
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());
這段程式碼就會輸出我們寫入的三個k-v對

2. range 迭代
for (it->Seek("name1"); it->Valid() && it->key().ToString() < "name2"; it->Next()) {
std::cout << it->key().ToString() << ": " <<
it->value().ToString() << std::endl;
}
delete it;

只會輸出key值位於[name1,”name2″)之間的value也就只輸出了key=name1的value
當然還有其他的基本操作,但是這裡我們展示了增刪改查已經足夠了.後面會在梳理具體實現的時候在增加新的測試文件


