王雪飞 9c254f8709 | 3 viikkoa sitten | ||
---|---|---|---|
.github/workflows | 删除 | 3 viikkoa sitten | |
benchmarks | 删除 | 3 viikkoa sitten | |
cmake | 删除 | 3 viikkoa sitten | |
db | 删除 | 3 viikkoa sitten | |
doc | 删除 | 3 viikkoa sitten | |
helpers/memenv | 删除 | 3 viikkoa sitten | |
include/leveldb | 删除 | 3 viikkoa sitten | |
issues | 删除 | 3 viikkoa sitten | |
port | 删除 | 3 viikkoa sitten | |
table | 删除 | 3 viikkoa sitten | |
test | 删除 | 3 viikkoa sitten | |
third_party | 删除 | 3 viikkoa sitten | |
util | 删除 | 3 viikkoa sitten | |
.clang-format | 3 viikkoa sitten | ||
.gitignore | 3 viikkoa sitten | ||
.gitmodules | 3 viikkoa sitten | ||
AUTHORS | 3 viikkoa sitten | ||
CMakeLists.txt | 3 viikkoa sitten | ||
CONTRIBUTING.md | 3 viikkoa sitten | ||
LICENSE | 3 viikkoa sitten | ||
NEWS | 3 viikkoa sitten | ||
README.md | 3 viikkoa sitten | ||
TODO | 3 viikkoa sitten |
小组成员:
王雪飞 10225501435
马也驰 10215501408
TTL(Time To Live),即生存时间,是指数据在存储系统中的有效期。设置TTL可以使得过期的数据自动失效,减少存储空间占用,提高系统性能。
为什么需要TTL功能:
在LevelDB中添加TTL功能的方案:
在 Put 操作中,将 TTL 与当前时间相加获得 DDL,DDL 为数据失效的时间,将 DDL 与值一起存储,存储格式为<TTL_value>
。
Status DB::Put(const WriteOptions& opt, const Slice& key,
const Slice& value, uint64_t ttl) {
WriteBatch batch;
auto now = std::chrono::system_clock::now();
auto timestamp = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
auto microsecondsTimestamp = static_cast<uint64_t>(timestamp) + ttl*1000000;
std::string value_ttl = value.ToString();
value_ttl += "_" + std::to_string(microsecondsTimestamp);
Slice new_value(value_ttl.c_str(), value_ttl.size());
batch.Put(key, new_value);
return Write(opt, &batch);
}
在 Get 操作中,获得 value 之后,找到第一个下划线,下划线前面的数据为 DDL,然后用当前时间与 DDL 作比较,判断数据是否过期,过期则返回NotFound。
size_t pos = value->find_last_of('_');
if (pos != std::string::npos) {
std::string substring = value->substr(pos + 1);
auto ddl = static_cast<uint64_t>(std::stoll(substring));
auto now = std::chrono::system_clock::now();
auto timestamp = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
auto microsecondsTimestamp = static_cast<uint64_t>(timestamp);
if (ddl <= microsecondsTimestamp) {
value->clear();
Slice msg1("value not found!");
Slice msg2("value has expired!");
s = leveldb::Status::NotFound(msg1, msg2);
} else {
value->resize(pos);
}
}
我们最初的想法是把TTL跟value存储在一起,形式为<TTL value>
,这样Put操作会很简单,仅仅把两个字符串拼接起来即可,但这样的话,在Get操作中时,无法判断从何处分割TTL和value,所以我们决定在TTL和value之间添加一个标志符,存放形式改为<TTL_value>
,这样,在Get操作时,只需先找到第一个下划线,下划线前面的为TTL,后面的为value,这样就能把TTL和value区分开来。但还有一个问题,判断条件为:写入数据的时间+ TTL < 读取数据的时间
,如果仅存放TTL,虽然在调用get时我们可以获得读取数据的时间,并通过解码value获得TTL,但我们没有办法获得写入数据的时间,所以只能通过在Put操作时,把写入数据的时间也写入value中,这样在Get时,就能获得写入数据的时间,从而判断是否过期。所以,我们又把value的形式改为<TTL_写入时间_value>
,这样,通过两个下划线把TTL、写入时间和value区分开来,就能实现在get操作时判断是否过期。但我们又想到,既然在get操作解码得到TTL和写入时间之后要加在一块,并且TTL和写入时间都是在get操作时与value进行编码,那么我们为什么不在get操作时就把TTL和写入时间加在一起,再与value编码呢,把写入时间+TTL记为DDL,这样就可以把value编码为<DDL_value>
,在get操作时,只需解码得到DDL,然后拿当前时间跟DDL作比较,即可知道数据是否过期。
综上所述,我们的编码格式经过多次迭代:<TTL value>
-><TTL_value>
-><TTL_写入时间_value>
-><DDL_value>
,最终得到一个比较满意的编码方式。