写了个突破 V8 堆内存限制的 HashMap, 打算往里面放N个G的文件索引.
发布于 5 年前 作者 Chunlin-Li 5012 次浏览 最后一次编辑是 4 年前 来自 分享

本人 CNode 新人, GitHub 新人. 第一次在CNode发帖, 第一次在NPM上发开源项目. 求喷, 但请温柔点~

最近工作中遇到问题, 需要一个巨大无比的HashMap, 用来存放日志索引, 直接用 Object 很快 OOM, 后来尝试使用 Buffer 作为 Value 值, 发现无论Buffer大小, 最多也就能撑住 1000多万 key/value 对. (其实知道这种工作不适合 Node, 但还是想找出办法解决这个问题)

通过各种方法找解决方案未果… 最后决定自己写个HashMap, 专为这种巨量数据用.
地址 : https://github.com/Chunlin-Li/Bigmap

目前第一版, 功能比较简单, 只能 set/get 键值对, 但是觉得用来解决项目中的问题是够用了.
存储空间支持自动增长, 可以选择性的进行 rehash. rehash 可以异步也可以同步. set/get 操作都是同步操作. 速度肯定要比 Object 要慢, 实测慢 5-10 倍左右.

数据库的方案之前确实想过, 但是真的不合适, 每次使用的时候数据都是一次性的, 而且数据库基本都是网络接口, 我不知道有没有可以直接进程通信的方式使用数据库, 加入网络IO的开销觉得实在是有些高了.

14 回复

这种需求不应该用Redis等kv数据库解决吗……

@zsxsoft 嗯, 是啊. 想到过用 Redis 解决. 但是毕竟引入了网络IO, 即使在本地起一个Redis速度应该也会变得很慢.
早先时候尝试用了 Mongodb, 本地有个现成的(单机). 结果发现插入速度太慢, 每秒 1W 左右. WireTiger引擎, 考虑到CPU资源不多, 没开压缩.

用内存数据库SQLite

@JexCheng 如果使用的是buffer, 是不受GC托管的, 受GC托管的内存是有限制的, 64位2G类似这样

@JexCheng 就是为了解决插入1000W条数据后, GC压力过大的问题, 才会去实现这样一个模块. 昨天测试了, 如果直接用 Object, 插入 13W Key Value, V8的 Heap 就增长了 23MB. 而同样的数据量, 用 Buffer 实现的 Hash Map 几乎不消耗 Heap, 只有 100KB 左右.

Unix Socket + Redis 效率似乎更好,另支持可变字符串长度和丰富数据结构和持久化等 :-)

redis (127.0.0.1) x 8,497 ops/sec ±1.99% (67 runs sampled) redis (unix socket) x 23,826 ops/sec ±4.68% (73 runs sampled) redis (unix socket) multiple (10) x 14,239 ops/sec ±3.74% (71 runs sampled)(批量10个,即按单个计算是 142,390 ops/sec) redis (unix socket) multiple (20) x 8,557 ops/sec ±3.75% (62 runs sampled)(批量20个,即按单个计算是 171,140 ops/sec) Nodejs Object x 52,831 ops/sec ±7.01% (18 runs sampled) BigMap x 39,098 ops/sec ±5.31% (29 runs sampled)

@breeswish 非常感谢! 在这之前确实不知道 unix socket, 回去研究研究. 另外在考虑用 C++ addon 实现 hash map 会不会性能上会好一些, 可以在没有本地 redis 的情况下使用.

@Chunlin-Li 做好后像breeswish一样做个benchmark看看,这个效率跟你对数据的读写频率之类的都有关.现成的东西好处是无数人已经踩过无数坑,然后填平了,自己写往往还要再踩一遍

@Chunlin-Li 不错。C++ 的之前看到过一个:https://www.npmjs.com/package/hashtable ,但我没测试过

@JexCheng 我是写完后才发现有这个库的… 不过测试了一下, 但似乎它的每个 Key 是在 Local Handle上的, 当 table 的 key 特别多的时候, 比如 1000W 以上时, 堆内存还是会几乎被耗尽. 而我的这个虽然执行效率比他的低, 但是几乎完全不使用堆内存. 在我遇到的问题中, 它的 hashtable 也解决不了问题.

https://github.com/Level/levelup 这东西合适你用吗?leveldb

@alsotang Levelup 的 API 和功能都挺合适, 然而数据是存在文件系统中的… 我希望尽量避免磁盘 IO 和网络 IO. 里面介绍到的 MemDown 是在内存中的, 回头抽空研究研究这个, 估计可以用. 非常感谢!!

@Chunlin-Li 从你的数据量大小来说,应该存文件系统是个比较合适的选择吧?不过我不懂你的具体场景。你可以测测速试试。而且 v8 在存储这么大的 Object 时,效率能不能比上 leveldb,这点很值得测一测。

@alsotang 主要是因为内存很大(三位数), 数据又不需要长期保存或持久化. 目前来看, 对 V8 没什么负担, 只不过 Hash 是用的 js 实现的 murmurhash, 当碰撞增多的时候, 反复 hash 运算成为性能上的一个很大开销… 觉得早晚得用 C++ 实现才行, 这样可以想法避免使用 open addressing 的方式.

回到顶部