RocksDB敏感数据过滤器项目实战之自定义压缩过滤器
1.背景
今天要分享的是rocksdb的compaction filter功能,该功能允许用户自定义filter与factory在compaction过程中完成自己的业务逻辑,比如本文的敏感数据过滤器项目完美展示了 CompactionFilter 的三大核心能力:
下面我们来深入CompactionFilter的源码,并以一个完整的项目来实战。
注:懒人版,完整代码已更新至星球
2.什么是CompactionFilter?
CompactionFilter 是 RocksDB 的核心扩展点,允许在 SST 文件创建过程中对键值对进行过滤和转换。
多版本 API 设计 (Filter → FilterV2 → FilterV3)
Filter可以理解为你的过滤条件,FilterV2是普通类型实现,FilterV3是宽列的实现,当然v3也是支持普通类型传递调用v2。
整个设计层面有一个factory来构建各种filter,比如:我们在写入数据的时候会写很多empty value,那么compaction的时候可以进行remove,你可以自己实现自己的过滤factory来实现自己的filter。
像v2与v3返回的是Decision,比如:你要在compaction时删除掉emtpy数据,那么当检查到empty value时,你返回kRemove,否则返回kKeep。
enum class Decision {
kKeep, // 保留键值对
kRemove, // 移除(转换为墓碑)
kChangeValue, // 修改值
kRemoveAndSkipUntil, // 跳过键范围(性能优化)
kChangeBlobIndex, // 修改 Blob 索引
kIOError, // 内部错误
kPurge, // 转换为 SingleDelete
kChangeWideColumnEntity, // 修改宽列实体
kUndetermined // 无法确定决策
};
比如,你自己实现的RemoveEmptyValueCompactionFilter
bool RemoveEmptyValueCompactionFilter::Filter(int /*level*/,
const Slice& /*key*/,
const Slice& existing_value,
std::string* /*new_value*/,
bool* /*value_changed*/) const {
// remove kv pairs that have empty values
return existing_value.empty();
}
在filterV2默认实现中当filter返回true的话就会返回remove 决策。
virtual Decision FilterV2(int level, const Slice& key, ValueType value_type,
const Slice& existing_value, std::string* new_value,
std::string* /*skip_until*/) const {
switch (value_type) {
case ValueType::kValue: {
bool value_changed = false;
bool rv = Filter(level, key, existing_value, new_value, &value_changed);
if (rv) {
return Decision::kRemove;
}
return value_changed ? Decision::kChangeValue : Decision::kKeep;
}
// other......
}
}
3.敏感数据过滤项目实战
我们创建一个敏感词的factory以及filter,核心逻辑是对于敏感的词进行过滤或者压缩,比如:用户写入了k=userid,v=password,那么这个password写入的时候是明文的,但是压缩时会自动变为md5加密,这样增加安全性,这种不仅可以提升实时写入效率,还可以自定义压缩的逻辑,非常的强大。
在该项目中我们将会使用正则做一些匹配工作,同时支持普通类型与宽列实体来完整的演示compaction filter功能。
比如:main函数逻辑如下,写入宽列数据之后,手动compact,最终得到:
原始数据存储完成
压缩之前数据
用户数据:
credit_card: 1234-5678-9012-3456
email: john@example.com
name: John Doe
ssn: 123-45-6789
开始压缩
压缩完成,读取数据...
用户数据:
credit_card: 5d444387a5d39f5deb5b24a82a9b990d4ada981515d84469cda488f605799c10
email: john@example.com
name: John Doe
ssn: 01a54629efb952287e554eb23ef69c52097a75aecc0e3a93ca0855ab6d7a31a0
密码 (处理后): d2360faa4c7109412588f56d8411a72dacf8df1de36fb405faa53d4412639829
添加新的敏感模式 'api_key'...
API密钥 (处理后): da4ec3358a10c9b0872eb877953cc7b07af5f4d75e4c1cb0597cbbf41e5dbe35
核心实现:
rocksdb::WideColumns sensitive_columns = {
{"name", "John Doe"},
{"email", "john@example.com"},
{"credit_card", "1234-5678-9012-3456"},
{"ssn", "123-45-6789"}
};
status = db->PutEntity(rocksdb::WriteOptions(), default_cf, "user:1001", sensitive_columns);
if (!status.ok()) {
std::cerr << "Failed to write wide columns: " << status.ToString() << std::endl;
}
status = db->CompactRange(rocksdb::CompactRangeOptions(), nullptr, nullptr);
if (!status.ok()) {
std::cerr << "Compaction failed: " << status.ToString() << std::endl;
}
// do something
学习更多干货,欢迎关注转发!