PalDB 详解

转自:http://www.jianshu.com/p/23db015e81a5

github:https://github.com/linkedin/PalDB


简介

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

img_0e09d0d6ded959146d5cb36650b11aed.png

读的吞吐量是 leveldb 和 rocksdb 的5倍

img_7f26e8f75e84faf4f620c7a380dd5dbe.png

内存使用是 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块:序列化,预写入,最终写入。

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

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

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

数据文件:存储 value 长度以及 value

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

img_2278d16a708b6a3bcc53e9ef46b0b61f.png

预写入工程

预写入过程中还会记录一些重要的值,如: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 信息拼接,形成最终的数据库文件。文件结构如下:

img_edd7dd903bb78da635cd29a8432edde3.png

最终数据库文件结构

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

读取 meta 信息,如 key 数据,key 长度数量、每个 key 长度对应的索引文件 slot 数等,并存储在内存中。

以一个只读内存映射文件方式(MappedByteBuffer)打开 key 索引集合。由于 PalDB 将 key 索引集合当做一个文件打开,由于内存映射文件的大小限制,key 索引集合的大小不能超过 2G

以一个或多个只读内存映射文件方式打开数据文件集合。如果数据文件过大(大于2G),PalDB 会将其切分成多块。

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

img_27cb642bc631efc91ecee7e442f462c4.png

读取数据流程

总结

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

作者:两棵橘树

链接:http://www.jianshu.com/p/23db015e81a5

來源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值