PalDB 详解

作者: 两棵橘树 | 来源:发表于2016-08-14 15:49 被阅读1329次

    简介

    PalDB 是 Linkedin 公司开源的一款只读型的 KV 存储数据库,目的是在某些场景下替代 HashMap/HashSet 或 LevelDB,在性能和内存之间做了一个良好的平衡。下面是官方给出的测试图表:

    读的吞吐量是 leveldb 和 rocksdb 的5倍 内存使用是 hashset 的1/6

    使用方式

    作为一个存储工具包,其使用方式也很简单,一看就会明白:

    //写数据
    StoreWriter writer = PalDB.createWriter(new File("store.paldb"));
    writer.put("foo", "bar");
    writer.put(1213, new int[] {1, 2, 3});
    writer.close();
    //读数据
    StoreReader reader = PalDB.createReader(new File("store.paldb"));
    String val1 = reader.get("foo");
    int[] val2 = reader.get(1213);
    reader.close();
    

    应用场景

    PalDB 适合一次写入,多次读取,且数据量较大的场景,如:

    • Hadoop/Spark 计算时产生的一些中间结果
    • 机器学习训练出的模型
    • 词典

    实现原理

    PalDB 本质上是一个哈希表,用开放寻址法处理哈希冲突。下面从读写两方面来分析其实现细节。

    写数据的过程主要分为3块:序列化,预写入,最终写入。

    1. 序列化:序列化过程主要负责将准备写入的 key-value 值进行序列化。PalDB 自己实现了对 java 基本对象的序列化,对数据进行了一定的压缩(如果觉得压缩的仍不够,PalDB 默认支持 Snappy 压缩算法,可手动开启)。

    2. 预写入:程序每调用一次 writer.put(Object key, Object value) ,PalDB 就进行一次预写入。预写入负责写两类文件:

    • 索引文件:存储 key 以及 value 在数据文件中的位置
    • 数据文件:存储 value 长度以及 value

    这两类文件都有一个或者多个,成对出现,文件数量决于 key(序列化后)的长度,一个 key 长度对应一对<索引文件,数据文件>。也就是说,key 长度是一个一级索引,这个在读的时候会用到。下面用一张图总结下预写入的过程。


    预写入工程

    预写入过程中还会记录一些重要的值,如:value 位置的最大长度,key 总数以及每个 key 长度下的 key 数量。

    3.最终写入
    当写完数据最终调用 writer.close() 时就进入最终写入阶段。预写入生成的索引文件只是顺序的存储了 key 以及 value 在数据文件中的位置,最终写入阶段负责将索引文件转化成哈希表,跟索引文件一样,每一个 key 长度对应一个哈希表。对每一个哈希表:

    • 哈希表 slot 数量 = 该 key 长度下的 key 数量 / loadFactor(默认0.75,可手动指定)
    • 每个 slot 的大小是固定的,等于 key 长度 + value 位置的最大长度(因此,slot 里的数据其实是有部分空闲的)。

    写这个哈希表的过程是顺序读预写入阶段生成的索引文件,按 key hash 到指定 slot(用开放寻址法处理哈希冲突)并写入 key 以及 value 位置的过程。
    遍历处理完所有 key 长度对应的索引文件后,将所有哈希表、数据文件、meta 信息拼接,形成最终的数据库文件。文件结构如下:


    最终数据库文件结构

    首先 PalDB 会将数据库文件初始化,初始化过程分为三步:

    1. 读取 meta 信息,如 key 数据,key 长度数量、每个 key 长度对应的索引文件 slot 数等,并存储在内存中。
    2. 以一个只读内存映射文件方式(MappedByteBuffer)打开 key 索引集合。由于 PalDB 将 key 索引集合当做一个文件打开,由于内存映射文件的大小限制,key 索引集合的大小不能超过 2G
    3. 以一个或多个只读内存映射文件方式打开数据文件集合。如果数据文件过大(大于2G),PalDB 会将其切分成多块。

    初始化完成后,就可以调用 reader.get(Object o) 方法进行数据读取,数据读取的流程如下:


    读取数据流程

    总结

    PalDB 的实现原理还是比较简单的,但是在某些场景下效果会比常规方法更好。就笔者的实践来说,用 PalDB 存储推荐模型来代替之前的文件 load 到内存的方式,在性能影响很小的情况下大大减少了内存的使用,值得一试。

    相关文章

      网友评论

        本文标题:PalDB 详解

        本文链接:https://www.haomeiwen.com/subject/zdjesttx.html