Bläddra i källkod

Update Readme

main
kevinyao0901 3 veckor sedan
förälder
incheckning
9bfeb4d6ad
1 ändrade filer med 97 tillägg och 60 borttagningar
  1. +97
    -60
      README.md

+ 97
- 60
README.md Visa fil

@ -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. **错误处理与回滚**
- 如果二级索引数据库的写入或删除操作失败,主数据库的写入或删除操作将被回滚。
- 在回滚过程中,主数据库会恢复为操作前的状态(插入操作时删除新数据,删除操作时恢复原始数据)。
#### 实现意义
这种设计确保了主数据库和二级索引数据库的一致性,即便在部分写入或删除操作失败的情况下,仍能通过回滚机制保证数据的完整性和原子性。
---
#### **删除数据的实现**

Laddar…
Avbryt
Spara