From 9bfeb4d6ad7d943b7ee649d5d054441ddbec888f Mon Sep 17 00:00:00 2001 From: kevinyao0901 Date: Sun, 22 Dec 2024 15:18:00 +0800 Subject: [PATCH] Update Readme --- README.md | 157 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index bfd999a..039e19f 100644 --- a/README.md +++ b/README.md @@ -192,71 +192,108 @@ Status DBImpl::DeleteIndex(const std::string& fieldName) { --- -### 5. **对 `Put` 和 `Delete` 方法的内容更新描述** - -为了在 `Put` 和 `Delete` 操作中同步更新二级索引,我们对代码进行了以下扩展: - -#### **Put 方法** - -在 `Put` 方法中,新增逻辑检查并更新字段索引: - -1. **字段值提取与检查** - - 遍历所有已创建索引的字段列表 (`fieldWithIndex_`)。 - - 检查待插入数据值 (`val`) 中是否包含当前字段。 - - 如果字段存在,提取该字段的值 (`fieldValue`)。 - -2. **构建索引键与插入索引数据库** - - 使用字段名和字段值组合构建索引键 (`field:fieldValue`)。 - - 将该索引键与原始键 (`key`) 写入二级索引数据库 `indexDb_`。 - - 如果写入操作失败,立即返回错误状态。 - -此逻辑保证在 `Put` 方法中,对 `fieldWithIndex_` 中的每个字段都可以维护最新的索引关系。 - -#### **Delete 方法** - -在 `Delete` 方法中,新增逻辑检查并移除相关字段索引: - -1. **字段值提取与检查** - - 遍历所有已创建索引的字段列表 (`fieldWithIndex_`)。 - - 检查待删除数据键 (`key`) 中是否包含当前字段。 - - 如果字段存在,提取该字段的值 (`fieldValue`)。 - -2. **构建索引键与删除索引条目** - - 使用字段名和字段值组合构建索引键 (`field:fieldValue`)。 - - 从二级索引数据库 `indexDb_` 中删除该索引键。 - - 如果删除操作失败,立即返回错误状态。 - -此逻辑确保在 `Delete` 操作中能够正确移除已删除记录对应的二级索引条目。 - ---- - -### 6. **数据插入与删除原子性的实现** - -#### **插入数据的实现** - -在数据插入操作中,我们使用 `WriteBatch` 来确保对主数据库 (`DBImpl`) 和二级索引数据库 (`indexDb_`) 的操作能够作为一个原子事务提交。首先,将数据插入主数据库,然后根据预定义的字段集合 (`fieldWithIndex_`) 生成索引信息,并将这些索引数据插入到二级索引数据库中。如果插入操作中的任何一个步骤失败(例如,主数据库或索引数据库插入失败),整个事务将回滚,确保数据库的一致性。 - +### 5. **`Put` 和 `Delete` 方法的内容** +以下是实验报告中对 `Put` 和 `Delete` 方法的描述,以及如何通过事务和回滚机制实现数据插入与删除的原子性。 + +#### `Put` 方法描述 +`Put` 方法用于向主数据库和二级索引数据库中插入或更新数据。其关键步骤如下: +1. **主数据库写入**:首先尝试向主数据库插入或更新数据。 +2. **二级索引更新**:遍历需要创建索引的字段 (`fieldWithIndex_`),从新值中提取字段对应的索引键和值,并将索引插入到二级索引数据库。 +3. **提交事务**: + - 提交主数据库的写入操作。 + - 提交二级索引数据库的写入操作。 +4. **回滚机制**:如果二级索引数据库的写入失败,会回滚主数据库的插入操作以确保数据一致性。 + +关键代码: ```cpp -WriteBatch batch; // 创建事务 - -// 在主数据库插入数据 -batch.Put(key, val); - -// 遍历并生成二级索引数据 -for (const auto& field : fieldWithIndex_) { - std::string fieldValue = 提取字段值(val, field); - if (!fieldValue.empty()) { - std::string indexKey = field + ":" + fieldValue; - std::string indexValue = key.ToString(); - batch.Put(Slice(indexKey), Slice(indexValue)); // 将索引数据加入事务 - } +Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) { + ... + // 在主数据库写入新数据 + batch.Put(key, val); + + // 遍历字段并更新索引 + for (const auto& field : fieldWithIndex_) { + ... + indexBatch.Put(Slice(indexKey), Slice(indexValue)); + } + + // 提交主数据库事务 + s = this->Write(o, &batch); + if (!s.ok()) { + return s; + } + + // 提交二级索引数据库事务 + s = indexDb_->Write(o, &indexBatch); + if (!s.ok()) { + // 如果二级索引写入失败,回滚主数据库写入 + for (const auto& insertedKey : keysInserted) { + batch.Delete(insertedKey); + } + this->Write(o, &batch); + return s; + } + ... } +``` -// 提交事务 -Status s = this->Write(o, &batch); +#### `Delete` 方法描述 +`Delete` 方法用于从主数据库和二级索引数据库中删除数据。其关键步骤如下: +1. **获取原始数据**:在删除前从主数据库读取原始值,确保在删除失败时可以回滚。 +2. **主数据库删除**:从主数据库中删除目标键。 +3. **二级索引删除**:遍历字段,计算对应的索引键,并将其从二级索引数据库中删除。 +4. **提交事务**: + - 提交主数据库的删除操作。 + - 提交二级索引数据库的删除操作。 +5. **回滚机制**:如果二级索引数据库的删除失败,会尝试将主数据库的删除操作回滚为原始状态。 + +关键代码: +```cpp +Status DBImpl::Delete(const WriteOptions& options, const Slice& key) { + ... + // 从主数据库删除目标键 + batch.Delete(key); + + // 遍历字段并删除索引 + for (const auto& field : fieldWithIndex_) { + ... + indexBatch.Delete(Slice(indexKey)); + } + + // 提交主数据库事务 + s = this->Write(options, &batch); + if (!s.ok()) { + return s; + } + + // 提交二级索引数据库事务 + s = indexDb_->Write(options, &indexBatch); + if (!s.ok()) { + // 如果二级索引删除失败,回滚主数据库删除 + if (!originalValue.empty()) { + batch.Put(key, originalValue); + } else { + batch.Put(key, ""); + } + this->Write(options, &batch); + return s; + } + ... +} ``` -通过上述方式,插入操作确保了主数据库与二级索引数据库的一致性。如果插入过程中任何一个数据库发生错误,整个事务会回滚,不会进行部分写入,从而避免了数据不一致的情况。 +#### 6.数据插入与删除的原子性实现 +通过以下策略确保数据插入与删除操作的原子性: +1. **事务机制**: + - 主数据库和二级索引数据库的写入操作分别使用 `WriteBatch` 封装,并在提交前记录必要的数据以支持回滚。 +2. **错误处理与回滚**: + - 如果二级索引数据库的写入或删除操作失败,主数据库的写入或删除操作将被回滚。 + - 在回滚过程中,主数据库会恢复为操作前的状态(插入操作时删除新数据,删除操作时恢复原始数据)。 + +#### 实现意义 +这种设计确保了主数据库和二级索引数据库的一致性,即便在部分写入或删除操作失败的情况下,仍能通过回滚机制保证数据的完整性和原子性。 + +--- #### **删除数据的实现**