同学的科研中会处理卫星遥感图片,数据量很大,而一台机器的内存和硬盘不够用来处理,所以他的科研成果受到了一定的限制,我打算帮他做一个存储中间件,希望通过一个集群和简单好用的API来帮助提高科研效果。
我相信类似的文章肯定很多了,所以我不需要把我自己的过程再写一遍。大概就是先安装配置HDFS(HaDoop File System), 这个基础设施建设要以后,再安装配置HBase, 要知道HBase是一个运行在HDFS上的应用程序。
配置Hadoop用的Hadoop的官方文档,还挺好用的,没什么坑,配HBase也是用的官方文档 ,一样没毛病。如果你跟着文档走,基本上单机的HBase能够很好跑起来;当然你需要一个mac或者linux,windows就不能保证结果了。
如果你顺利的话,到最后会用
sbin/start-dfs.sh
来启动HDFS,用
sbin/stop-dfs.sh
来关闭HDFS;用
./bin/start-hbase.sh
来启动HBase,用
./bin/stop-hbase.sh
来关闭HBase。同样你也会用jps
来查看当前整个Hadoop相关的软件的运行情况。我很建议你顺着文档把小细节都过下来,虽然有些用不到,但会让你对整个Hadoop更有了解。
我们需要做对是key-value pair的大规模存储,语言是Java,我参考的文章是这个 。由于我经常用Berkeley DB做简易数据存储,对于NoSQL的存储方式已经很熟悉了,简单来说就是用分级别的索引来存储object。一个object 可以有多个级别的索引,当然,一般简单起见都只用第一级别的索引,比如一个类是这样的:
class Account {
String account;
String password;
}
Berkeley DB就是把这个类的实例存到磁盘上,不要你去思考转移成SQL里面的表。一个Account类,一般会用account这个变量作为索引,加好annotation 的类是这样的:
@Entity
class Account {
@PrimaryIndex
String account;
String password;
}
如果你存了一个account是”TOM”, password是”123” 的实例进入数据库,用index.get(“TOM”), 就能够把这个对象提取出来, 数据库会提供index的factory,总之看到这里我相信读者能理解NoSQL大概的样子。
HBase里的数据组织方法和Berkeley DB略有不同,它的第一级索引叫做row,数据类型是byte[]. 第二级索引叫family, 第三级索引叫qualifier. 由于我们的业务逻辑比较简单,只要存key-value, 所以我让所有存储的记录都用同样的family和qualifier, 让row成为唯一区分他们的东西。
Baeldung.com/hbase 里面的代码跟我自己的整合之后发现了一个问题。我要存储的是key-value pair, key和value都是String类型,而HBase里规定row(就是key)的数据类型是byte[], baeldung的样例代码里用了HBase的库里的一个Bytes.toBytes(string)
方法来把字符串转化成byte array. 但是两个有相同值的string literal,传入这个方法以后得到的结果不一样,导致我无法从HBase里面提取出数据,这就让我必须要重写toBytes()
方法和 Bytes.toString()
方法。
重写是简单的,只要遵从自己统一好的规定就行,由于我的key都是数字加空格,value都是数字,我直接把string里每一个char映射成它对应ASCII值,这就完成了toBytes()
方法;转回来就是从ASCII值回到字符的过程。
可能读者会对于我遇到的问题不是很懂,那我这里仔细讲一下,看懂的读者可以略过这一段。
table.put(Bytes.toBytes('key1'), Bytes.toBytes('value1'));
假设我们是用上面这一行代码向HBase里存入object的,再用
table.get(Bytes.toBytes('key1'));
去取这个object的时候会返回null。
为了解释这个问题,我特意写了另外一个测试程序:
System.out.println(Bytes.toBytes('key1'));
System.out.println(Bytes.toBytes('key2'));
发现输出是两个东西,他们的哈希值不一样,可能Bytes这个类就是按照哈希值来判断两个object是否相等,而新建的string literal再转化成byte[], 肯定是有不同的哈希值,所以会出错;我的代码跟哈希值无关,所以就容易成功。