From 9c254f8709d71292b7ac0b6bdbcf1afb4aba7398 Mon Sep 17 00:00:00 2001 From: wangxuefei <10225501435@stu.ecnu.edu.cn> Date: Wed, 6 Nov 2024 19:24:57 +0800 Subject: [PATCH] write README.md --- README.md | 36 +++++++++++++++++++++++++++++++++++- db/db_impl.cc | 8 ++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b2e23ec..f7367ec 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,41 @@ TTL(Time To Live),即生存时间,是指数据在存储系统中的有 3. Compaction清理:在数据压缩过程中,删除过期的数据。 ## 3.3. 实现步骤 #### 3.3.1. 修改数据结构 -在Put操作中,将过期时间戳与值一起存储。 +在 Put 操作中,将 TTL 与当前时间相加获得 DDL,DDL 为数据失效的时间,将 DDL 与值一起存储,存储格式为``。 +``` +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(now.time_since_epoch()).count(); + auto microsecondsTimestamp = static_cast(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); +} +``` +### 3.3.2 修改读取流程 +在 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(std::stoll(substring)); +auto now = std::chrono::system_clock::now(); +auto timestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); +auto microsecondsTimestamp = static_cast(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); + } +} +``` ## 实验中遇到的问题 ### 1. TTL存储的位置以及存储方式 我们最初的想法是把TTL跟value存储在一起,形式为``,这样Put操作会很简单,仅仅把两个字符串拼接起来即可,但这样的话,在Get操作中时,无法判断从何处分割TTL和value,所以我们决定在TTL和value之间添加一个标志符,存放形式改为``,这样,在Get操作时,只需先找到第一个下划线,下划线前面的为TTL,后面的为value,这样就能把TTL和value区分开来。但还有一个问题,判断条件为:`写入数据的时间+ TTL < 读取数据的时间 `,如果仅存放TTL,虽然在调用get时我们可以获得读取数据的时间,并通过解码value获得TTL,但我们没有办法获得写入数据的时间,所以只能通过在Put操作时,把写入数据的时间也写入value中,这样在Get时,就能获得写入数据的时间,从而判断是否过期。所以,我们又把value的形式改为``,这样,通过两个下划线把TTL、写入时间和value区分开来,就能实现在get操作时判断是否过期。但我们又想到,既然在get操作解码得到TTL和写入时间之后要加在一块,并且TTL和写入时间都是在get操作时与value进行编码,那么我们为什么不在get操作时就把TTL和写入时间加在一起,再与value编码呢,把写入时间+TTL记为DDL,这样就可以把value编码为``,在get操作时,只需解码得到DDL,然后拿当前时间跟DDL作比较,即可知道数据是否过期。 diff --git a/db/db_impl.cc b/db/db_impl.cc index 6d2928b..21bf63a 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -1161,15 +1161,15 @@ Status DBImpl::Get(const ReadOptions& options, const Slice& key, mem->Unref(); if (imm != nullptr) imm->Unref(); current->Unref(); - /** parse ttl from value **/ + // for TTL size_t pos = value->find_last_of('_'); if (pos != std::string::npos) { std::string substring = value->substr(pos + 1); - auto ttl = static_cast(std::stoll(substring)); + auto ddl = static_cast(std::stoll(substring)); auto now = std::chrono::system_clock::now(); auto timestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); auto microsecondsTimestamp = static_cast(timestamp); - if (ttl <= microsecondsTimestamp) { + if (ddl <= microsecondsTimestamp) { value->clear(); Slice msg1("value not found!"); Slice msg2("value has expired!"); @@ -1178,7 +1178,7 @@ Status DBImpl::Get(const ReadOptions& options, const Slice& key, value->resize(pos); } } - /** parse ttl from value **/ + // for TTL return s; }