@ -0,0 +1,62 @@ | |||
#pragma | |||
#include <iostream> | |||
#include "leveldb/db.h" | |||
#include <vector> | |||
#include <mutex> | |||
namespace leveldb { | |||
class LEVELDB_EXPORT MyLevelDB : leveldb::DB { | |||
public: | |||
MyLevelDB(Options& options, std::string db_name) | |||
: _op(options), _db_name(db_name) { | |||
//打开数据库 | |||
Status status = DB::Open(options, db_name + "_main", &_fields_db); | |||
if (!status.ok()) { | |||
std::cerr << "Open main database failure: " << status.ToString() << std::endl; | |||
abort(); | |||
} | |||
} | |||
//序列化为字符串 | |||
void SerializeValue(const FieldArray& fields, std::string& resString); | |||
// 反序列化为字段数组 | |||
void ParseValue(const std::string& value_str, FieldArray& resFieldArray); | |||
//字段存储 | |||
Status PutWithFields(const WriteOptions& options, const std::string& key,const FieldArray& fields); | |||
//字段查找 | |||
Status FindKeysByField(const ReadOptions& options, const Field field,std::vector<std::string>* keys); | |||
//创建索引 | |||
Status CreateIndexOnField(const std::string& field_name); | |||
//删除索引 | |||
Status DeleteIndex(std::string& field_name); | |||
void QueryByIndex(const ReadOptions& options, Field& field, | |||
std::vector<std::string>& keys); | |||
public: | |||
Status Put(const WriteOptions& options, const Slice& key, | |||
const Slice& value) override; | |||
Status Delete(const WriteOptions& options, const Slice& key) override; | |||
Status Write(const WriteOptions& options, WriteBatch* updates) override; | |||
Status Get(const ReadOptions& options, const Slice& key, | |||
std::string* value) override; | |||
Iterator* NewIterator(const ReadOptions& options) override; | |||
const Snapshot* GetSnapshot() override; | |||
void ReleaseSnapshot(const Snapshot* snapshot) override; | |||
bool GetProperty(const Slice& property, std::string* value) override; | |||
void GetApproximateSizes(const Range* range, int n, uint64_t* sizes) override; | |||
void CompactRange(const Slice* begin, const Slice* end) override; | |||
private: | |||
Options _op; | |||
std::string _db_name; | |||
DB* _fields_db; | |||
std::vector<std::string> index_list_; | |||
std::mutex mutex_; | |||
std::vector<DB*> index_db; | |||
}; | |||
} |
@ -0,0 +1,206 @@ | |||
#include "leveldb/write_batch.h" | |||
#include "db/dbformat.h" | |||
#include "leveldb/db.h" | |||
#include "util/coding.h" | |||
#include "leveldb/my_leveldb.h" | |||
#include "unordered_map" | |||
#include <sstream> | |||
namespace leveldb { | |||
//反序列化为字段数组 | |||
void MyLevelDB::ParseValue(const std::string& value_str, | |||
FieldArray& resFieldArray) { | |||
std::stringstream ss(value_str); | |||
std::string segment; | |||
// 按逗号分割字符串 | |||
while (std::getline(ss, segment, ',')) { | |||
std::string key; | |||
std::string value; | |||
std::stringstream kv(segment); | |||
if (std::getline(kv, key, ':') && std::getline(kv, value, ':')) { | |||
if (!key.empty() && !value.empty()) { | |||
resFieldArray.push_back(std::make_pair(key, value)); | |||
// std::cout << ((resFieldArray.back()).first).data() << std::endl; | |||
} else { | |||
std::cerr << "Invalid key-value pair: " << segment << std::endl; | |||
} | |||
} else { | |||
std::cerr << "Failed to parse segment: " << segment << std::endl; | |||
} | |||
} | |||
} | |||
//序列化为字符串 | |||
void MyLevelDB::SerializeValue(const FieldArray& fields, | |||
std::string& resString) { | |||
resString.clear(); | |||
for (int i = 0; i < fields.size(); i++) { | |||
const std::string& key = fields[i].first; | |||
const std::string& value = fields[i].second; | |||
resString += key + ":" +value; | |||
if (i != fields.size() - 1) { | |||
resString += ","; | |||
} | |||
} | |||
} | |||
Status MyLevelDB::PutWithFields(const WriteOptions& options,const std::string& key,const FieldArray& fields) { | |||
std::string value; | |||
SerializeValue(fields, value); | |||
auto slice_key = Slice(key.c_str()); | |||
auto slice_value = Slice(value.c_str()); | |||
Status s = _fields_db->Put(options, slice_key, slice_value); | |||
std::unordered_map<int, int> match; | |||
std::unique_lock<std::mutex> l(mutex_); | |||
for (int i = 0; i < fields.size(); i++) { | |||
for (size_t idx = 0; idx < index_list_.size(); idx++) { | |||
const auto& i_name = index_list_[idx]; | |||
if (fields[i].first == i_name) { | |||
match[i] = idx; | |||
break; | |||
} | |||
} | |||
} | |||
for (auto item : match) { | |||
std::string composed_key; | |||
composed_key += fields[item.first].second + ":" + key; | |||
s = index_db[item.second]->Put(options, composed_key, Slice()); | |||
} | |||
return s; | |||
} | |||
Status MyLevelDB::FindKeysByField(const ReadOptions& options, const Field field, | |||
std::vector<std::string>* keys) { | |||
auto it = _fields_db->NewIterator(options); | |||
it->SeekToFirst(); | |||
keys->clear(); | |||
while (it->Valid()) { | |||
auto val = it->value(); | |||
FieldArray arr; | |||
auto str_val = std::string(val.data(), val.size()); | |||
ParseValue(str_val, arr); | |||
for (auto pr : arr) { | |||
if (pr.first == field.first && pr.second == field.second) { | |||
Slice key = it->key(); | |||
keys->push_back(std::string(key.data(), key.size())); | |||
break; | |||
} | |||
} | |||
it->Next(); | |||
} | |||
delete it; | |||
return Status::OK(); | |||
} | |||
Status MyLevelDB::CreateIndexOnField(const std::string& field_name) { | |||
for (const auto& field : this->index_list_) { | |||
if (field == field_name) { | |||
return Status::InvalidArgument(field_name, | |||
"Index already exists for this field"); | |||
} | |||
} | |||
index_list_.push_back(field_name); | |||
Options op = _op; | |||
DB* field_db; | |||
op.index_mode = true; | |||
Status status = DB::Open(op, _db_name + "_index_" + field_name, &field_db); | |||
index_db.push_back(field_db); | |||
if (!status.ok()) { | |||
std::cerr << "Failed to open index DB: " << status.ToString() << std::endl; | |||
abort(); | |||
} | |||
return status; | |||
} | |||
Status MyLevelDB::DeleteIndex(std::string& field_name) { | |||
auto it = std::find(index_list_.begin(), index_list_.end(), field_name); | |||
if (it == index_list_.end()) { | |||
return Status::NotFound("Index not found for this field"); | |||
} | |||
// 从列表中移除该字段 | |||
index_list_.erase(it); | |||
return Status::OK(); | |||
} | |||
void MyLevelDB::QueryByIndex(const ReadOptions& options, Field& field, | |||
std::vector<std::string>& keys) { | |||
int i = 0; | |||
for (; i < index_list_.size(); i++) { | |||
if (index_list_[i] == field.first) { | |||
break; | |||
} | |||
} | |||
assert(i != index_list_.size()); | |||
auto it = index_db[i]->NewIterator(options); | |||
it->SeekToFirst(); | |||
while (it->Valid()) { | |||
auto val = it->key(); | |||
auto str_val = std::string(val.data(), val.size()); | |||
std::string key; | |||
std::string value; | |||
std::stringstream kv(str_val); | |||
std::getline(kv, key, ':'); | |||
std::getline(kv, value, ':'); | |||
if (key == field.second) { | |||
keys.push_back(value); | |||
} | |||
it->Next(); | |||
} | |||
delete it; | |||
} | |||
Status MyLevelDB::Put(const WriteOptions& options, const Slice& key, | |||
const Slice& value) { | |||
return _fields_db->Put(options, key, value); | |||
} | |||
Status MyLevelDB::Delete(const WriteOptions& options, const Slice& key) { | |||
return _fields_db->Delete(options, key); | |||
} | |||
Status MyLevelDB::Write(const WriteOptions& options, WriteBatch* updates) { | |||
assert(0); | |||
return Status(); | |||
} | |||
Status MyLevelDB::Get(const ReadOptions& options, const Slice& key, | |||
std::string* value) { | |||
return _fields_db->Get(options, key, value); | |||
} | |||
Iterator* MyLevelDB::NewIterator(const ReadOptions& options) { | |||
return _fields_db->NewIterator(options); | |||
} | |||
const Snapshot* MyLevelDB::GetSnapshot() { return _fields_db->GetSnapshot(); } | |||
void MyLevelDB::ReleaseSnapshot(const Snapshot* snapshot) { | |||
return _fields_db->ReleaseSnapshot(snapshot); | |||
} | |||
bool MyLevelDB::GetProperty(const Slice& property, std::string* value) { | |||
return false; | |||
} | |||
void MyLevelDB::GetApproximateSizes(const Range* range, int n, | |||
uint64_t* sizes) { | |||
/* uint64_t temp = 0; | |||
_main_db->GetApproximateSizes(range, n, sizes); | |||
for (auto& index_db : field_db_) { | |||
index_db->GetApproximateSizes(range, n, &temp); | |||
*sizes += temp; | |||
}*/ | |||
} | |||
void MyLevelDB::CompactRange(const Slice* begin, const Slice* end) { | |||
_fields_db->CompactRange(begin, end); | |||
} | |||
} |
@ -0,0 +1,83 @@ | |||
#include "leveldb/db.h" | |||
#include "leveldb/my_leveldb.h" | |||
#include <iostream> | |||
using namespace std; | |||
using namespace leveldb; | |||
int main() { | |||
Options op; | |||
op.create_if_missing = true; | |||
MyLevelDB db(op, "testMyDB"); | |||
//序列化测试 | |||
std::string res1; | |||
FieldArray fields1 = { | |||
{"name", "Customer#000000001"}, {"address", "abc"}, {"phone", "def"}}; | |||
db.SerializeValue(fields1,res1); | |||
std::cout << "序列化测试结果:" << std::endl << res1 << std::endl; | |||
//反序列化测试 | |||
FieldArray fields2; | |||
db.ParseValue(res1,fields2); | |||
std::cout << "反序列化测试结果:" << std::endl ; | |||
for (int i = 0; i < fields2.size(); i++) { | |||
std::cout << fields2[i].first << ":" << fields2[i].second << std::endl; | |||
} | |||
//字段存储 | |||
std::cout << "字段存储和查找结果:" << std::endl; | |||
std::string key2 = "k_1"; | |||
std::string key3 = "k_2"; | |||
std::string key4 = "k_3"; | |||
FieldArray field2 = {{"name", "Customer#000000001"}, | |||
{"address", "IVhzIApeRb"}, | |||
{"phone", "25-989-741-2988"}}; | |||
FieldArray field3 = { | |||
{"name", "Customer#000000001"}, {"address", "abc"}, {"phone", "def"}}; | |||
FieldArray field4 = { | |||
{"name", "Customer#000000001"}, {"address", "abc"}, {"phone", "def"}}; | |||
db.PutWithFields(WriteOptions(), key2, field2); | |||
db.PutWithFields(WriteOptions(), key3, field3); | |||
db.PutWithFields(WriteOptions(), key4, field4); | |||
//字段查找 | |||
FieldArray value_ret; | |||
std::vector<std::string> v; | |||
db.FindKeysByField(ReadOptions(), field2[1], &v); | |||
for (auto s : v) std::cout << s << "\n"; | |||
//创建索引 | |||
WriteOptions writeOptions; | |||
ReadOptions readOptions; | |||
Options options; | |||
options.create_if_missing = true; | |||
auto db1 = new MyLevelDB(options, "testdb2"); | |||
db1->CreateIndexOnField("address"); | |||
std::string key8 = "k_8"; | |||
std::string key9 = "k_9"; | |||
FieldArray fields8 = { | |||
{"name", "Customer#000000001"}, {"address", "abc"}, {"phone", "def"}}; | |||
FieldArray fields9 = {{"name", "Customer#000000001"}, | |||
{"address", "IVhzIApeRb"}, | |||
{"phone", "25-989-741-2988"}}; | |||
FieldArray fields10 = { | |||
{"name", "Customer#000000001"}, {"address", "abc"}, {"phone", "def"}}; | |||
FieldArray fields11 = { | |||
{"name", "Customer#000000001"}, {"address", "abc"}, {"phone", "def"}}; | |||
FieldArray fields12 = { | |||
{"name", "Customer#000000001"}, {"address", "abc"}, {"phone", "def"}}; | |||
Field query = {"address", "abc"}; | |||
db1->PutWithFields(WriteOptions(), key8, fields8); | |||
db1->PutWithFields(WriteOptions(), key9, fields9); | |||
std::cout << "索引存储与查找:" << std::endl; | |||
std::vector<std::string> keys; | |||
db1->QueryByIndex(readOptions, query,keys); | |||
for (int i = 0; i < keys.size();i++) { | |||
std::cout << keys[i] << std::endl; | |||
} | |||
return 0; | |||
} |
@ -0,0 +1,122 @@ | |||
#include "leveldb/db.h" | |||
#include "leveldb/my_leveldb.h" | |||
#include <iostream> | |||
using namespace std; | |||
using namespace leveldb; | |||
// ÍÌÍÂÁ¿ | |||
void TestThroughput(int num_operations) { | |||
WriteOptions writeOptions; | |||
ReadOptions readOptions; | |||
Options options; | |||
options.create_if_missing = true; | |||
auto db1 = new MyLevelDB(options, "testThroughput"); | |||
std::string key = "k_"; | |||
FieldArray fields = {{"name", "Customer#000000001"}, | |||
{"address", "IVhzIApeRb"}, | |||
{"phone", "25-989-741-2988"}}; | |||
//д | |||
auto start_time1 = std::chrono::steady_clock::now(); | |||
for (int i = 0; i < num_operations; ++i) { | |||
db1->PutWithFields(WriteOptions(), key + to_string(i), fields); | |||
} | |||
auto end_time1 = std::chrono::steady_clock::now(); | |||
auto duration1 = | |||
chrono::duration_cast<chrono::milliseconds>(end_time1 - start_time1) | |||
.count(); | |||
cout << "Put Op Throughput: " << num_operations * 1000 / duration1 << " OPS" << endl; | |||
//¶Á | |||
string str; | |||
auto start_time2 = std::chrono::steady_clock::now(); | |||
for (int i = 0; i < num_operations*100; ++i) { | |||
db1->Get(ReadOptions(), key, &str); | |||
} | |||
auto end_time2 = std::chrono::steady_clock::now(); | |||
auto duration2 = | |||
chrono::duration_cast<chrono::milliseconds>(end_time2 - start_time2) | |||
.count(); | |||
//cout << duration2 << endl; | |||
cout << "Get Op Throughput: " << num_operations*100 * 1000 / duration2 << " OPS" | |||
<< endl; | |||
//×ֶβéÕÒ | |||
std::vector<std::string> keys; | |||
auto start_time3 = std::chrono::steady_clock::now(); | |||
for (int i = 0; i < num_operations; ++i) { | |||
db1->FindKeysByField(ReadOptions(), fields[0],&keys); | |||
} | |||
auto end_time3 = std::chrono::steady_clock::now(); | |||
auto duration3 = | |||
chrono::duration_cast<chrono::milliseconds>(end_time3 - start_time3) | |||
.count(); | |||
cout << "FindKeysByField Op Throughput: " << num_operations * 1000 / duration3 << " OPS" | |||
<< endl; | |||
} | |||
// ÑÓ³Ù | |||
void TestLatency(int num_operations) { | |||
Options options; | |||
options.create_if_missing = true; | |||
auto db = new MyLevelDB(options, "testLatency"); | |||
std::string key = "k_"; | |||
FieldArray fields = {{"name", "Customer#000000001"}, | |||
{"address", "IVhzIApeRb"}, | |||
{"phone", "25-989-741-2988"}}; | |||
//Put | |||
int64_t latency1 = 0; | |||
int64_t tollatency = 0; | |||
auto end_time1 = std::chrono::steady_clock::now(); | |||
auto last_time1 = end_time1; | |||
for (int i = 0; i < num_operations*100; ++i) { | |||
// Operations | |||
db->PutWithFields(WriteOptions(), key + to_string(i), fields); | |||
end_time1 = std::chrono::steady_clock::now(); | |||
latency1 = std::chrono::duration_cast<std::chrono::milliseconds>(end_time1 - | |||
last_time1) | |||
.count(); | |||
last_time1 = end_time1; | |||
tollatency += latency1; | |||
} | |||
std::cout << num_operations*100<<" put op averange latency:" << (double)tollatency / num_operations<< std::endl; | |||
//Get | |||
int64_t latency2 = 0; | |||
tollatency = 0; | |||
auto end_time2 = std::chrono::steady_clock::now(); | |||
auto last_time2 = end_time2; | |||
std::string str; | |||
for (int i = 0; i < num_operations*100; ++i) { | |||
// Operations | |||
db->Get(ReadOptions(), key + to_string(i),&str ); | |||
end_time2 = std::chrono::steady_clock::now(); | |||
latency2 = std::chrono::duration_cast<std::chrono::milliseconds>(end_time2 - | |||
last_time2) | |||
.count(); | |||
last_time2 = end_time2; | |||
tollatency += latency2; | |||
} | |||
std::cout << num_operations*100 | |||
<< " Get operation averange latency:" << (double)tollatency / num_operations | |||
<< std::endl; | |||
//FindKeysByField | |||
int64_t latency3 = 0; | |||
tollatency = 0; | |||
auto end_time3 = std::chrono::steady_clock::now(); | |||
auto last_time3 = end_time3; | |||
std::vector<std::string> keys; | |||
for (int i = 0; i < 50; ++i) { | |||
// Operations | |||
db->FindKeysByField(ReadOptions(), fields[0], &keys); | |||
end_time3 = std::chrono::steady_clock::now(); | |||
latency3 = std::chrono::duration_cast<std::chrono::milliseconds>(end_time3 - | |||
last_time3) | |||
.count(); | |||
last_time3 = end_time3; | |||
tollatency += latency3; | |||
} | |||
std::cout << 50 << " FindKeysByField operation averange latency:" | |||
<< (double)tollatency / num_operations << "ms" | |||
<< std::endl; | |||
} | |||
int main() { | |||
TestThroughput(100); | |||
TestLatency(1000); | |||
} |