本文笔者想要来分享和介绍一个高性能的内存键值缓存系统和技术-MegaHash。笔者已经将其应用在一个对性能非常敏感的信息查询系统当中,并取得了比较好的效果。
关于MegaHash
MegaHash(MH)没有正式的官网,使用github进行相关技术和产品的发布和服务。
根据其产品描述,MegaHash是一个超快速的键-值哈希表,使用C++编写,并被封装为Nodejs模块和API,方便nodejs应用程序的集成和使用。它的主要特性包括:
- 高性能读、写、删除和键迭代操作
- 性能稳定并且可预测
- 低内存消耗
- 所有的数据存储都在V8堆之外
- 支持Buffer、字符串、数字、布尔和对象等多种类型
- 测试过高达100万的键值
- 兼容基本的ES6 Map API
可以看到,MH的一个核心特点是其性能,因为它针对的是普通nodejs应用的一个比较忽视的方面,就是对于大型数据集的内存管理。普通的nodejs程序工作时的内存基本上都局限在V8内存堆中,也使用V8和相关的内存机制,但当数据规模超过限制时,会出现灵活性不足的问题。MH自行实现了相关的内存和数据管理机制,不会占用应用程序的堆内存,从而提高了额外的性能、灵活性和健壮性。
其他一些技术细节和限制包括:
- 安装过程需要GCC编译器
- MH使用"Separate Chaining(独立链接)"技术管理键和存储
- MH使用32位DJB2算法对键进行摘要,并使用特别得方式建立索引,从而能够在速度和内存开销之间取得平衡
- MH的值,实际上都会被序列化,并在写入和读取时自动处理
- 键的长度限制为65K字节,不能为空(实际上使用buffer),数量没有明确限制
- MH值得限制为2GB
性能
关于性能,这里有一个简单的测试案例,可以帮助读者初步理解Megahash系统能够提供的应用性能。这个测试(图)的设置和结果要点包括:
- 平台: AWS EC2 c5a.16xlarge VM
- 配置: 64核/128GB内存/20Gbps网络
- 数据: 10亿个key,简单数据
- 性能(平均): 写535261/秒,读961851/秒
- 内存: 总占用约34GB,其中Megahash的开销约为27GB
这样的配置,放到现在,就是一台普通的工业服务器的配置水平,但能够支持数百K/S级别的数据读写操作,完全可以满足一般规模的企业级业务系统的使用了。另外,从测试的图中可以看到,在一定的范围内,其读写性能基本上不受数据规模的影响,这对于应用的稳定性是非常重要的。
应用
MH的开发和集成也是非常简单,和一般的npm应用差异不大。一般的步骤和操作如下。
安装npm
当然作为一个外部软件,需要先进行安装,可以使用npm:
npm install megahash
引用和实例化
然后,在使用之前,需要先引用和实例化:
const MegaHash = require('megahash');
let hashTable = new MegaHash();
实例化之后,实际上就在系统中建立起了一个内存键值存储表,可以进行相关的数据操作了。
一般操作
作为一个键值数据库,一般的操作包括增加(设置)、读取、存在检查、删除和清除,其参考操作代码如下:
js
// 增加和设置
hash.set( "hello", "there" );
// 读取和查询,如果没有,结果为 undefined
let hvalue = hash.get("hello");
// 存在检查
let isexist = hash.has("hello");
// 删除键
hash.delete("hello");
// 清除数据
hash.clear();
数据类型
MH的key使用的就是普通的字符串,它的值支持更大的数据类型,包括普通的字符串、js对象、buffer、number、boolean和null等等。
遍历
除了可以直接使用键进行访问之外,还可以使用遍历的方式来访问MH实例:
js
let key = hash.nextKey();
while (key) {
// do something with key
key = hash.nextKey(key);
}
状态
MH提供了存储状态检查的功能,可以检查如键的数量、数据大小、索引大小等统计和状态信息,使用方法如下:
js
// hash表的大小
let length = hash.length();
// 统计信息
let stats = hash.stats();
console.log(stats);
Example stats:
{
"numKeys": 10000,
"dataSize": 217780,
"indexSize": 87992,
"metaSize": 300000,
"numIndexes": 647
}
错误处理
MH并没有提供太复杂的错误处理机制,因为最常见能够遇到的问题就是资源限制无法操作了,如下列代码:
js
let result = hash.set( "hello", "there" );
if (!result) {
throw new Error("Failed to write to MegaHash: Out of memory");
}
注意事项
首先,MH是一些特殊的目标和场景设计的,它是一个内存数据系统,它没有持久化的功能,如果考虑到意外宕机的恢复,可能需要自己实现相关的持久化的机制。
另外,和Redis不同,它也没有相关的网络访问方式和额外的数据类型,不能提供更为复杂的业务支撑功能,或者需要开发者进行额外的开发。MH提供的接口和功能比较简单,只能够和应该作为应用系统内部的一个程序模块来使用,而不是一个独立的子系统,当然如果业务的抽象层级足够的化,我们也可以将其封装成通用的模块来使用。
还有,就是其安装过程现在还不是特别方便,因为涉及C++原生程序,需要进行现场的编译,对Windows的支持也可能存在问题。
所以,MH这个技术是一种比较特别的技术,适用于一些特别的应用场景。比如对性能和并发要求比较高的,相对静态的数据和信息查询系统。
小结
本文探讨和分享了一个内存哈希表存储技术-Megahash,了解了其技术实现和特点,以及应用的方式和场合。