@ -0,0 +1,26 @@ | |||||
#include "blob_file.h" | |||||
#include <fstream> | |||||
namespace leveldb { | |||||
BlobFile::BlobFile(const std::string& filename) : filename_(filename) { | |||||
// 初始化 BlobFile,例如打开文件 | |||||
} | |||||
BlobFile::~BlobFile() { | |||||
// 关闭文件 | |||||
} | |||||
Status BlobFile::Put(const Slice& key, const Slice& value) { | |||||
std::ofstream file(filename_, std::ios::app | std::ios::binary); | |||||
if (!file.is_open()) { | |||||
return Status::IOError("Failed to open blob file"); | |||||
} | |||||
// 简单实现,将 key 和 value 写入文件 | |||||
file.write(key.data(), key.size()); | |||||
file.write(value.data(), value.size()); | |||||
file.close(); | |||||
return Status::OK(); | |||||
} | |||||
} // namespace leveldb |
@ -0,0 +1,25 @@ | |||||
#ifndef LEVELDB_BLOB_FILE_H_ | |||||
#define LEVELDB_BLOB_FILE_H_ | |||||
#include <string> | |||||
#include "leveldb/status.h" | |||||
#include "leveldb/slice.h" | |||||
namespace leveldb { | |||||
class BlobFile { | |||||
public: | |||||
BlobFile(const std::string& filename); | |||||
~BlobFile(); | |||||
// 写入键值对 | |||||
Status Put(const Slice& key, const Slice& value); | |||||
private: | |||||
std::string filename_; | |||||
// 内部实现,例如文件指针或缓冲区 | |||||
}; | |||||
} // namespace leveldb | |||||
#endif // LEVELDB_BLOB_FILE_H_ |
@ -0,0 +1,122 @@ | |||||
#include "gtest/gtest.h" | |||||
#include "leveldb/env.h" | |||||
#include "leveldb/db.h" | |||||
#include "util/coding.h" | |||||
#include <iostream> | |||||
using namespace leveldb; | |||||
constexpr int value_size = 2048; | |||||
constexpr int data_size = 128 << 20; | |||||
// 根据字段值查找所有包含该字段的 key | |||||
std::vector<std::string> FindKeysByField(leveldb::DB* db, Field &field) { | |||||
Iterator* iter = db->NewIterator(ReadOptions()); | |||||
std::vector<std::string> ret_keys; | |||||
int64_t bytes = 0; | |||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { | |||||
auto fields_ret = ParseValue(iter->value().data()); | |||||
for (Field each_field : fields_ret) | |||||
{ | |||||
std::cout << each_field.first << " " << each_field.second << std::endl; | |||||
if (field.first.compare(each_field.first) == 0) { | |||||
if (field.second.compare(each_field.second)==0) | |||||
{ | |||||
ret_keys.push_back(iter->key().data()); | |||||
} | |||||
else | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
delete iter; | |||||
return ret_keys; | |||||
} | |||||
Status OpenDB(std::string dbName, DB **db) { | |||||
Options options; | |||||
options.create_if_missing = true; | |||||
return DB::Open(options, dbName, db); | |||||
} | |||||
TEST(TestField, Read) { | |||||
DB *db; | |||||
if(OpenDB("testdb", &db).ok() == false) { | |||||
std::cerr << "open db failed" << std::endl; | |||||
abort(); | |||||
} | |||||
std::string key = "k_1"; | |||||
FieldArray fields = { | |||||
{"name", "Customer#000000001"}, | |||||
{"address", "IVhzIApeRb"}, | |||||
{"phone", "25-989-741-2988"} | |||||
}; | |||||
// 序列化并插入 | |||||
std::string value = SerializeValue(fields); | |||||
db->Put(WriteOptions(), key, value); | |||||
// 读取并反序列化 | |||||
std::string value_ret; | |||||
db->Get(ReadOptions(), key, &value_ret); | |||||
auto fields_ret = ParseValue(value_ret); | |||||
std::cout << "第一个字段名:"<< fields_ret[0].first << "第一个字段值" << fields_ret[0].second<< std::endl; | |||||
delete db; | |||||
} | |||||
TEST(TestField, Find) { | |||||
DB *db; | |||||
if(OpenDB("testdb", &db).ok() == false) { | |||||
std::cerr << "open db failed" << std::endl; | |||||
abort(); | |||||
} | |||||
std::vector<std::string> keys = {"s_1", "s_2", "s_3", "s_4"}; | |||||
// 构造一组字段数组 | |||||
std::vector<FieldArray> FieldArrays = { | |||||
{ | |||||
{"name", "Sarah"},{"sex", "f"},{"age", "20"} | |||||
}, | |||||
{ | |||||
{"name", "Mike"},{"sex", "m"},{"age", "19"},{"hobby", "badminton"} | |||||
}, | |||||
{ | |||||
{"name", "Amy"},{"sex", "f"},{"age", "21"},{"talent", "sing"} | |||||
}, | |||||
{ | |||||
{"name", "John"}, {"sex", "m"},{"age", "20"} | |||||
} | |||||
}; | |||||
// 序列化并插入 | |||||
for (int i=0; i<FieldArrays.size(); i++) | |||||
{ | |||||
std::string key = keys[i]; | |||||
FieldArray fields = FieldArrays[i]; | |||||
std::string value = SerializeValue(fields); | |||||
db->Put(WriteOptions(), key, value); | |||||
} | |||||
// 构建目标字段 | |||||
Field field = {"sex", "f"}; | |||||
std::vector<std::string> key_ret; | |||||
// 查询得到对应的key | |||||
key_ret = FindKeysByField(db, field); | |||||
for (int i = 0; i < key_ret.size(); i++) { | |||||
std::cout << "找到的键:" << key_ret[i] << std::endl; | |||||
} | |||||
delete db; | |||||
} | |||||
int main(int argc, char** argv) { | |||||
// All tests currently run with the same read-only file limits. | |||||
testing::InitGoogleTest(&argc, argv); | |||||
return RUN_ALL_TESTS(); | |||||
} |
@ -0,0 +1,119 @@ | |||||
#include "gtest/gtest.h" | |||||
#include "leveldb/env.h" | |||||
#include "leveldb/db.h" | |||||
#include "table/blob_file.h" // 假设 BlobFile 的头文件 | |||||
using namespace leveldb; | |||||
constexpr int value_size = 2048; // 单个值的大小 | |||||
constexpr int data_size = 128 << 20; // 总数据大小 | |||||
constexpr int min_blob_size = 1024; // KV 分离的阈值 | |||||
Status OpenDB(std::string dbName, DB** db) { | |||||
Options options; | |||||
options.create_if_missing = true; | |||||
options.key_value_separated = true; // 启用 KV 分离 | |||||
return DB::Open(options, dbName, db); | |||||
} | |||||
// 插入数据,模拟 KV 分离 | |||||
void InsertData(DB* db) { | |||||
WriteOptions writeOptions; | |||||
int key_num = data_size / value_size; | |||||
srand(static_cast<unsigned int>(time(0))); | |||||
for (int i = 0; i < key_num; i++) { | |||||
int key_ = rand() % key_num + 1; | |||||
std::string key = std::to_string(key_); | |||||
std::string value(value_size, 'a'); // 大 value | |||||
db->Put(writeOptions, key, value); // 使用标准 Put 接口插入 | |||||
} | |||||
} | |||||
// 检查数据是否被正确存入 BlobFile | |||||
void VerifyBlobFile(const std::string& blob_file_path, int expected_entries) { | |||||
BlobFile blobfile(blob_file_path, BlobFile::kReadMode); | |||||
Status status = blobfile.Open(); | |||||
ASSERT_TRUE(status.ok()); | |||||
int entry_count = 0; | |||||
BlobFile::Iterator it = blobfile.NewIterator(); | |||||
for (it.SeekToFirst(); it.Valid(); it.Next()) { | |||||
++entry_count; | |||||
const Slice& key = it.key(); | |||||
const Slice& value = it.value(); | |||||
ASSERT_GT(value.size(), min_blob_size); // 确认 value 大于阈值 | |||||
} | |||||
ASSERT_EQ(entry_count, expected_entries); // 确认条目数是否正确 | |||||
blobfile.Close(); | |||||
} | |||||
// KV 分离读写测试 | |||||
TEST(TestKVSeparation, WriteAndRead) { | |||||
DB* db; | |||||
if (OpenDB("testdb", &db).ok() == false) { | |||||
std::cerr << "open db failed" << std::endl; | |||||
abort(); | |||||
} | |||||
// 插入数据 | |||||
InsertData(db); | |||||
// 验证 BlobFile 内容 | |||||
VerifyBlobFile("blob_data", data_size / value_size); | |||||
// 随机点查数据 | |||||
ReadOptions readOptions; | |||||
srand(static_cast<unsigned int>(time(0))); | |||||
int key_num = data_size / value_size; | |||||
for (int i = 0; i < 100; i++) { | |||||
int key_ = rand() % key_num + 1; | |||||
std::string key = std::to_string(key_); | |||||
std::string value; | |||||
Status status = db->Get(readOptions, key, &value); | |||||
ASSERT_TRUE(status.ok()); // 验证是否成功读取 | |||||
if (value.size() > min_blob_size) { | |||||
ASSERT_TRUE(value == std::string(value_size, 'a')); // 验证大 value 的内容 | |||||
} | |||||
} | |||||
delete db; | |||||
} | |||||
// KV 分离压缩测试 | |||||
TEST(TestKVSeparation, Compaction) { | |||||
DB* db; | |||||
if (OpenDB("testdb", &db).ok() == false) { | |||||
std::cerr << "open db failed" << std::endl; | |||||
abort(); | |||||
} | |||||
// 插入数据 | |||||
InsertData(db); | |||||
leveldb::Range ranges[1]; | |||||
ranges[0] = leveldb::Range("-", "A"); | |||||
uint64_t sizes[1]; | |||||
db->GetApproximateSizes(ranges, 1, sizes); | |||||
ASSERT_GT(sizes[0], 0); | |||||
// 执行压缩 | |||||
db->CompactRange(nullptr, nullptr); | |||||
// 验证压缩后主数据区的大小 | |||||
ranges[0] = leveldb::Range("-", "A"); | |||||
db->GetApproximateSizes(ranges, 1, sizes); | |||||
ASSERT_EQ(sizes[0], 0); | |||||
// 验证 BlobFile 内容仍然有效 | |||||
VerifyBlobFile("blob_data", data_size / value_size); | |||||
delete db; | |||||
} | |||||
int main(int argc, char** argv) { | |||||
testing::InitGoogleTest(&argc, argv); | |||||
return RUN_ALL_TESTS(); | |||||
} |