• leveldb-FilterBlock实现


    布隆过滤器

    leveldb的写入的策略采用了 LSM 树🌲,加上很多的优化,写入的性能很强悍。

    但是读取的性能是稍微差了一点,可能出现 读放大 的情况,为了进行优化,避免很多的不必要的磁盘IO,使用布隆过滤器 快速判断数据是不是处于某个SSTable

    常用的判断某个数据是否是处于集合中我们直接用 set Or hash就好了,但是这 必须要我们保存所有的数据,这在判断数据库的情况是肯定不行的,解决的办法就是使用 布隆过滤器

    bitmap进行判断,使用K个哈希函数将key进行映射到上面,那么我们需要判断 key 是否是处于这个集合中,再次用 k 个哈希函数进行映射。

    • 如果说映射到的 bitmap 中为0, 那么 数据一定不在集合中
    • 如果说都是1的话, 可能是在集合中

    如果说 n 的大小实在是太小了,那么 假阳性的情况可能是比较严重的

    Filter

    const FilterPolicy* policy_;    /* filter 类型,如 BloomFilterPolicy */
        std::string keys_;              /* User Keys,全部塞到一个 string 中 */
        std::vector<size_t> start_;     /* 每一个 User Key 在 keys_ 中的起始位置 */
        std::string result_;            /* keys_ 通过 policy_ 计算出来的 filtered data */
        std::vector<Slice> tmp_keys_;   /* policy_->CreateFilter() 的参数 */
        
        /* filter 在 result_ 中的位置,filter_offsets_.size() 就是 Bloom Filter 的数量 */
        std::vector<uint32_t> filter_offsets_;  
    };
    

    FilterBlock

    整个的写入流程

    1. DataBlock 中获得 Key =====> AddKey(const Slice& key), 将所有的 key 的数据都放在了 keys 中,因为是按照 string 来存储的,所以用 start 标识每个 key 的位置
    2. key 都保存下来之后我们就要开始调用 StartBlock(uint64_t offset), 传入参数 offset 也就是data block 的偏移量,我们就能知道处理了多少数据,用于计算 filter 的数目(每2KB创建一个filter)
    3. 具体的调用 GenerateFilter(), 按照规格 (2KB)调用创建 布隆过滤器的函数
    4. 最终调用Finish完成整个的构建,将最终的一些 metadata 写到最终的数据中
    #include "filterBlock.h"
    #include "Filter.h"
    
    #include "Coding.h"
    
    using namespace xindb;
    
    // DataBlock 中每 2KB 就生成一个布隆过滤器
    static const size_t kFilterBasebit = 11;
    static const size_t kFilterBase    = 1 << kFilterBasebit;  
    
    
    FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy)
        :policy_(policy) {}
    
    
    // 使用 ADD 将数据写入之后开始创建 filterblock, 需要传入 datablock 的偏移量来计算 filter 的数目
    void FilterBlockBuilder::StartBlock(uint64_t block_offset) {
        uint64_t filter_index = (block_offset / kFilterBase);       // 需要创建的 filter 的数目
        assert(filter_index >= filter_offsets_.size());
    
        // filter_offset 保存每个 bloom filter 的起始偏移量
        while (filter_index > filter_offsets_.size()) {
            GenerateFilter();
        }
    }
    
    
    // 将 InternalKey 写入到 keys_ 中, 用 start_ 标识 key 的位置
    // 将全部的数据写入完毕之后会调用StartBlock 
    void FilterBlockBuilder::AddKey(const Slice& key) {
        Slice k = key;
        start_.push_back(keys_.size());        // 新插入的key的起始位置
        keys_.append(k.data(), k.size());       // 直接append到keys中去
    }
    
    
    // 生成Filter, 每 2KB 生成一个 bloom 
    void FilterBlockBuilder::GenerateFilter() {
        // 获得key,放到tmp_keys中
        const size_t num_keys = start_.size();
    
        if (num_keys == 0) {
            filter_offsets_.push_back(result_.size());
            return ;
        }
    
        start_.push_back(keys_.size());
    
        tmp_keys_.resize(num_keys);                 // 作为参数传入到生成 Filter 的函数
    
    
        // 将keys中的 InternalKey 取出来
        for (size_t i = 0; i < num_keys; i++) {
            const char* base = keys_.data() + start_[i];
            size_t len = start_[i+1] - start_[i];
            tmp_keys_[i] = Slice(base, len);
        }
    
        // 记录 Bloom Filter 结果的偏移量
        filter_offsets_.push_back(result_.size());
    
        policy_->CreateFilter(&tmp_keys_[0], static_cast<int>(num_keys), &result_);
    
        tmp_keys_.clear();
        keys_.clear();
        start_.clear();
    }
    
    
    // 完成最终的 MetaData 的写入,供读取的时候能够快速的定位
    Slice FilterBlockBuilder::Finish() {
        if (!start_.empty()) {
            GenerateFilter();
        }
    
        // 将所有的偏移量写入到 result_ 的尾部
        size_t Size = result_.size();
        for (size_t i = 0; i < result_.size(); i++) {
            PutFixed32(&result_, filter_offsets_[i]);
        }
    
        // 将过滤器的个数扔到尾部
        PutFixed32(&result_, Size);
    
        // 将Base也就是我们每2KB生成一个过滤器写入
        result_.push_back(kFilterBase);
        return Slice(result_);
    }
    
    
    
    
  • 相关阅读:
    git切换源失败解决方案
    HIve数仓新零售项目ODS层的构建
    控制器功能端口说明
    【C++】初识STL
    linux内核——进程
    手搭手Ajax实现搜索地址自动补全功能
    Linux学习笔记:什么是文件描述符
    C++:无法查找或打开 PDB 文件?? 如何解决呢?以及产生原因是什么?
    一文速学-让神经网络不再神秘,一天速学神经网络基础(七)-基于误差的反向传播
    了解public,protected,default以及private看完这一篇就足够!!!
  • 原文地址:https://blog.csdn.net/qq_52245648/article/details/127104395