LevelDB學習筆記 (1):初識LevelDB
1. 寫在前面
1.1 什麼是levelDB
LevelDB就是一個由Google開源的高效的單機Key/Value儲存系統,該儲存系統提供了Key到Value的有序對映。
1.2 為什麼要學levelDB
學習原始碼算是一種很好的學習方式,準備精讀幾個經典的開原始碼,那學習levelDB的原因主要有下
- 谷歌開源,程式碼質量非常高,而且只有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
當然還有其他的基本操作,但是這裡我們展示了增刪改查已經足夠了.後面會在梳理具體實現的時候在增加新的測試檔案