Archive

Posts Tagged ‘hadoop’

Hadoop Architecture and Design

May 27th, 2008 No comments

Hadoop Architecture and Design
1 Introduction
Hadoop分布式文件系统是设计在廉价硬件上运行的分布式文件系统.它和现在已存在的其他分布式文件系统有很多的类似性.然而其中的差异是非常有意义的.HDFS是高容错和部署在廉价的硬件上的.HDFS提供高吞吐量对应应用数据并且适合大数据集的应用.HDFS使得一些POSIX(可移植操作系统接口Portable Operating System Interface)得需求变得轻松,如访问文件系统数据.HDFS原先使Apache Nutch网站搜索引擎项目得底层构建.HDFS使apache hadoop的核心项目.

2 Assumptions and Goals
2.1 Hardware Failure硬件故障比之系统异常更是一个标准. 一个hdfs系统可能由成百上千台机器组成,每一台机器存储了文件系统的一部分数据.事实上,他们是大量的组件,每个组件对于可能发生故障都很重要,那将意味着hdfs系统中的部分不能完成正常的动能.因此,发现问题并且快速的自动的修复问题是hdfs核心架构的目标.

2.2 Streaming Data Access
运行在hdfs上的应用需要数据流通道访问他们的数据.他们不是一般用途的应用,如典型运行在一般目的的文件系统. hdfs的设计目是大量处理多于用户的交互使用. 重点是高数据吞吐量比之低的数据访问的响应时间.POSIX利用多的硬件条件,但是这不是hdfs系统的目标.POSIX语义学在一些关键的领域提高数据吞吐已经过于商业

2.3 Large Date sets
运行在hdfs系统上应用可以存贮大量的数据.一个典型的hdfs文件系统是G到T的数据量. 因而hdfs专门支持大文件. 在一个集群里,它提供高集合的数据带宽和数百个结点等级.它可以支持数10m个文件数量.

2.4 simple coherency model
hdfs应用需要一次写入多次读的文件访问模型.一个文件一旦创建,写入,关闭讲不需要再次变更.这个目的是使数据有简单的一致的版本,并且可以使数据访问贷款提高.一个mapreduce或一个网页爬虫的应用满美的适合这个模型.这里有一个计划支持添加的写文件,在未来.

2.5 moving computation is cheaper than moving data
一个应用的计算请求是非常高效的,假如计算所依赖的数据就在附近.这尤其真实当在一个巨大数据量的时候.这可以将网络阻塞最小化和提高整个系统的吞吐量.假设在通常情况下移动计算到他的数据所在的地方比移动数据到应用运行的地方好.hdfs为应用提供了移动计算到数据所在地的接口.

2.6 portability across heterogeneous hardware and software platforms
hdfs设计的目的是容易轻便的从一个平台到另一个平台.这使得普遍采用hdfs系统作为一个大数据量的应用系统变得容易.

2.7 namenode and datanodes
hdfs是一个主仆结构得架构.一个hdfs的集群由单一的namenode和一定数量的datanode组成.namenode是主服务器,它管理文件系统的namespace和控制客户端访问文件.在集群中的一个datanode服务器通常负责处理运行时收到的数据的存贮. hdfs暴露文件系统的namespace,允许用户数据存贮在文件中.在内部,一个文件被分割到一个或多个block中,这些block存贮在一组 datanode中.namenode执行文件系统的命名操作,像打开,关闭,重命名文件目录.它也决定了block和datanode的关联关系.datanode为文件系统客户端的读写需求负责,也执行从namenode来的block的创建,删除,复制命令.
null
namenoe 和datanode是设计运行在廉价机器上运行的software的一部分.这些机器以运行gnu/linux操作系统为代表.hdfs是用java编码的;任何机器支持java就能运行namenode和datanode的软件.使用高度可移植的java语言意味着hdfs可以部署在广泛的机器上.一个典型的部署是一台机器上部署namenode,这个群里的其他机器都运行datanode.当然架构在设计上也不排除一个机器运行多个datanode, 但是在实际运用中很少见.
在集群里单一存在的namenode巨大的简化了这个系统的架构.namenode是仲裁人,是整个hdfs库metadata的仓库.系统设计成这样的一个方式,用户的数据从不通过namenode.

2.8 the file system namespace
hdfs支持传统父子结构的文件组织.一个用户或应用可创建目录,在目录里存贮文件.一个文件系统的namespace层次是和其他已经存在的文件系统类似的;可以创建,移动文件,从一个目录移到另一个目录,重命名一个文件.hdfs还实现了用户的权限控制.hdfs目前架构还不支持硬连接或软连接,但是不排除以后实现.
namenode维持了文件namespace.任何一个文件命名系统的更新都记录在namenode里.一个应用可以指定在hdfs系统中文件的备份数目.文件的备份数目称为文件复制因数.这些系统都保存在namenode中.

2.9 data replication
hdfs设计为可靠的存储大量的文件在一个大集群的机器中.它顺序的存储每一个文件在block 中;一个文件所关联的所有的block的大小,除了最后一个,其他的都是大小一致的.一个文件中的所有block为了容错都有备份.block的大小和备份因数都是可以配置的.hdfs中的文件都是一次写入,并且同时每个文件都只能有一个写入者.
namenode对要备份的block作出决定.它周期性的从datanode接收心跳和block报告.接收心跳意味着datanode在正常运作.block报告里包含了发送的datanode中含有的所有block列表.
null
2.10 replica placement:the first baby steps
备份的布置可以鉴定hdfs的可靠性和性能.最优化的备份布置区分hdfs与其他大多数分布式文件系统.这是一个特征并且需要大量的调优和经验.机架识别布置规则的目的式提高数据可靠性,可用性,和网络带宽利用.当前的备份布置规则的实现方式是这里理念下的第一努力方向.这个规则的实现方式的短期目的是使之在产品环境生效,掌握其更多的行为,建立一个基础测试,研究更多的老练的规则.
一个大的hdfs实力运行在由很多廉价并且在不同机架上的机器集群.两个不同机架上的结点间的通讯必须通过交换机完成.大多数情况下,同一机架上机器间的网络带宽大于不同机架上两台机器.
在启动的时候,每个datanode确定自己的机架并且通报自己的机架好给namenode完成注册.hdfs提供了apis使得添加模块可以使用apis 决定自己的机架号变得容易.一个简单但非最优的规则是布置备份到不同的机架上.这样防止当一个机架全部失败后数据的丢失,可以在读数据时利用多个机架的网络带宽.这个规则平均分配备份在一个集群里,使平衡失败组件的压力更加容易.无论如何,这样的规则增加了写操作的消耗,因为一个写操作需要传输block 到其他的机架上.
在备份因数设定为三的情况下,hdfs的布置规则会把一个备份放到一个本地机架的结点机器上,另一个放到也是本地机架但不同结点机器上,最后一个放到非本地机架的一个不同结点上.这个规则切割了机架内部数据的传输,提高了写操作的性能.发生机架故障的概率远远小于结点发生故障;这个规则和数据的可靠性,可用性的要求或者说保证并不冲突.
然而,此规则减少了网络带宽的使用综合,当读数据来自一个可布置在两个不同的机架比之3 个机架.在这个规则下,文件的备份不是平均的分配到不同的机架上.第一份备份在一个结点上,第二份备份在同一个结点的同一个机架上,第三个备份平均的分配到其他剩余的机架上.这个规则提高的写操作性能,没有减少数据的可靠性和读的性能.
当前,在这里描述的默认备份规则是在不断配合进步的.

2.11 replica selection
为了最小的全局带宽消费和读响应时间,hdfs尽量从最近的一个备份读取数据.假如存在一个备份和读的结点在同一个机架上,这样这个备份就是首选的读取备份.假如一个集群跨越了多月数据中心,那么将会首先选取本地数据中心的备份数据,而非其他远程数据中心备份.

2.12 safemode
在启动时,namenode输入一个特殊的状态”safemode”,那么block的备份将不会发生.namenode从datanode接收心跳和快报告消息.一个block报告包含了该datanode机器上所有的block信息.每个block 含有备份的最小数的说明.一个block被认为时安全的备份过当namenode检查block备份的数目的时候.在namenode的block检查打到了一个配置的block安全的百分比之后,namenode退出安全模式.这样确定了部分block的少于了特出的数量.namenode接着把这些 block备份到其他的结点上.

2.13 the persistence of file system metadata
hdfs的namespace由 namenode来存储.namenode用一个事务日志”editLog”来持续的记录发生在文件系统metadata的每一个变更.一个例子,在 hdfs里创建一个文件爱你的时候,namenode同时会在editlog里插入一条记录来指引这个文件.同样的,文件备份因数的变更也会在 editlog里插入数据.namenode使用本地操作系统的一个文件来存放editlog.全部的文件系统namespace,包括block与文件的对应关系,文件系统属性,这些存放在一个叫fsimage的文件里.fsimage这个文件也是作为namenode的本地系统文件保存.
namenode 在内存里保存了一个全部文件系统的namespace和文件blockmap的一份镜像.metadata的key被设计的很紧凑,这样4gb容量的 namenode就可以充足的支持大量的文件和目录数目.当namenode启动的时候,它会从硬盘上读取fsimage和editlog.它可以截取掉老的editlog,因为他的事务机制已经应用到维持fsimage.这个处理叫做checkpoint.在目前的实现钟,checkpoint只发生在 namenode启动的时候.在不久的将来,这个处理将会做成周期性的.
datanode存储hdfs的数据在本地文件系统里.datanode 不知道hdfs的文件.它存储每个hdfs数据的block到本地文件系统的一些文件中.datanode不在同一个目录下创建文件,它会用启发式的思维决定一个目录下最佳的文件数量和适当创建目录.在一个目录下创建所有的文件并不是最佳的,因为本地文件系统可能不能在一个目录下高效的支持大数量的文件个数.在datanode启动的时候,它检查它的整个文件系统,产生一个所有hdfs数据blocks的list,包括每个block与文件的对应关系,并发送这些信息给namenode,这就是blockreport.

2.14 the communication protocols所有hdfs通信协议都是基于tcp/ip协议之上的.一个客户端建立一个连接,在namenode机器上定义一个tcp端口.它通过clientprotocl和namenode通话.datanode和namenode 间通过datanodeprotocol通话.一个远程的程序调用(RPC)抽象的包装了clinetprotocol和 datanodeprotocol.在设计中,namenode从不建立任何RPCs.namenode只响应从datanode或client过来的 RPC请求.

2.15 robustness
hdfs的首要目标式存储数据的可靠性甚至在出现故障的情况下.可能存在的三种故障式:namenode故障,datanode故障,网络故障.

2.16 data disk failure, heartheat and re-replication
每个datanode周期性发送heartbeat消息给namenode. 网络的划分可能引起datanode的子集失去越namenode的联系.namenode会察觉heartbeat消息的丢失,并且标记这些 datanode为死机,并且不再发送IO请求给他们.这样在这些dead datanode上的数据对于hdfs来说就不再可用了.datanode的death将导致一些blocks的备份因数在原先配置的值之下.namenode持续追踪那些需要备份的blocks,并且无论何时都可以发起备份.必要的重新备份是因为一些原因:一个datanode变成不可用,一个备份被损坏,datanode上的硬盘故障,文件备份因数的增加.

2.17 cluster rebalancing
hdfs的架构和数据重新平衡计划是和谐的.计划可以自动的搬移数据从一个datanode到另一个,假如一台机器上空余空间小于一定的极限.在特殊文件的突然高需求的情况下,计划可以动态的创建附件备份,重新平衡集群里的数据.这些类型的数据重平衡计划目前还没有实现.

2.18 data integrity
有这样的可能,从datanode取块数据,在取到的时候损坏了.这样的损坏可那发生在:存储设备问题,网络问题或者程序的bug.hdfs的客户端程序实现了检查文件内容的机制.当一个clinet创建一个文件,它为每一个block计算了 checksum,并且保存到hdfs的namespace中.当一个client重新取到文件内容,他会检查这些数据的checksum是否和原先计算出的是否一致.假如不一致,client可以选择其他的datanode上的数据备份.

2.19 metadata disk failure
fsimage和editlog是hdfs数据结构的中心.这些文件如果损坏将引起 hdfs系统无法正常工作.由于这个原因,namenode可以配置从而支持维持多个fsimage和editlog的拷贝.任何引起fsimage或 editlog更新的操作都会同时更新这些备份.这些同时更新fsimage和editlog的操作会降低namenode同时处理namespace事务额能力.但是这部分的性能降低是可以接受的,因为虽然hdfs应用是非常强调data的,但是并非强调metadata. 在namenode重启时,他会选择使用最近的fsimage和editlog文件.
namenode是hdfs集群中单点机器.假如namenode失败了,手工处理是必须的.目前自动的选者另一台机器作为namenode启动还不支持.

2.20 snapshots
快照存储了一个特定时间的存储数据的.快照特性的一个使用就是恢复hdfs系统到一个已知的最好的版本.目前hdfs还没有实现快照功能,但是将会再以后的版本中实现.

3 data organization
3.1 data blocks
hdfs设计支持非常大的文件.一些应用和hdfs的是一致的.这些应用写一次数据,多次读取并且需要满意的数据流读取速度.hdfs支持一次写入多次读取的定义.一个典型的block的大小是64m,一个 hdfs的文件被分割成64m,每个block将被分配存储到不同的datanode上.

3.2 staging
client请求创建一个文件不会立即到达namenode.实际上,最初hdfs client缓存这个文件的数据到一个本地临时目录.应用的写操作明显的被指向到本地临时目录.当本地目录积累的数据大于hdfs的block大小时,client连接namenode.namenode插入这个文件到父子体系的文件系统,并且分配一个数据块存储.namenode回复clinet datanode的身份id和目标block.接着clinet将临时文件目录下的block数据写道特定的datanode下.当一个文件关闭后,在临时目录下还未传输的数据将一并写道datanode下.client通知namenode,这个文件关闭了.这个时候,namenode提交文件创建操作到持久的库里(应该时namespace吧).假如在一个文件关闭之前namenode死掉了,那么这个文件就丢失了.
在仔细的考虑运行在 hdfs上引用的目的后,上面的步骤才被采用的.这些应用需要流写入到文件.假如clinet直接写一个远程文件,没有clinet buffer,那么网络速度和网络堵塞会合吞吐量严重冲突.这样的步骤并非没有先例.早期的分布式文件系统,如AFS,使用clinet边的缓存提高性能.一个POSIX的需求达到高性能的数据上传已经变得不严格.

3.3 Replication pipelining
当client写数据到hdfs的文件时,他的数据会首先写入本地文件,像之前提到的那样.假设hdfs的文件备份因数是三.当本地文件积累到一个block后,client从namenode得到datanode的list.这组 list中的datanode将备份这个block数据.然后clinet提交数据block到第一台datanode.第一台开始接收数据,并把数据分成的小部分(4kb),把每个小部分写到自己的库里,并且传输这些分割的部分到第二个datanode.第二个datanode接收每个部分后,保存到自己的库里,再把数据传输到第三个datanode.最后第三个datanode也把数据写入库里.这样一个datanode可以接收数据从先前的管道中 datanode传过来的数据并且同时向管道中的下一个datanode传输数据.数据就是这样传输的从一个datanode到另一个.

3.4 accessibility
hdfs为应用提供了很多不同的访问途径.自然,hdfs为applications提供了java api.c语言包装的java api也有.另外,一个http browser也可以浏览hdfs中的文件.目前的工作正在实现通过WebDAV协议来访问hdfs.

3.5 dfsshell
hdfs允许用户数据来自一个文件或者目录.它提供了一个命令行接口,叫做”DFSShell”,使用户可以和hdfs中的数据交互.在语法结构上,这些命令和其他的shells使类似的,这样用户可以熟练使用,如:
bin/hadoop dfs -mkdir /foodir //Create a directory named /foodir
bin/hadoop dfs -rmr /foodir //delete a directory named /foodir
bin/hadoop dfs -cat /foodir/myfile.txt //View the contents of a file named /foodir/myfile.txt

3.6 dfsadmin
DFSAdmin这个命令是被hdfs集群的管理者使用的.这些命令只能被hdfs管理者使用.如:
bin/hadoop dfsadmin -safemode enter //Put a cluster in SafeMode
bin/hadoop dfsadmin -report //Generate a list of Datanodes
bin/hadoop dfsadmin -decommission datanodename //Decommission Datanode datanodename

3.7 browser interface
hdfs可以配置一个tcp端口,这样可以在web页面查看hdfs的namespace.允许用户通过浏览器查看hdfs的namespace,查看文件的内容.

4 space reclamation
4.1 file deletes and undeletes
当一个文件被user或者应用删除的时候,它不会立即从hdfs里删除.hdfs首先将他重命名到/trash目录.这个文件可以从/trash中被快速的恢复.这个文件将会在 /trash目录下保存一定的时间(这个时间是配置的).当/trash下的文件超出这个时间后,namenode才将他从hdfs的namespace 中删除.这个文件删除引起相关联的blocks被释放.注意:这个删除延迟和相应的空间释放的时间是可以评估的.
用户可以恢复存放在/trash 目录下的数据.假如一个用户想恢复它删除的数据,用户可以进入/trash目录,找到文件./trash目录下值保存了这个文件删除时的最后拷贝. 这个/trash目录就想其他的目录带有特殊的特性:hdfs使用特殊的规则从而自动的删除这个目录下的文件.当前默认的规则是从/trash删除6个小时前的数据.在以后,这个规则将是通过定义好的接口可以配置的.

4.2 Decrease Replication Factor
当一个文件的备份因数减少时,namenode选择多余的可被删除备份.下一个hearbeat把这个信息带给datanode.datanode接收消息,删除相应的blocks,这样空余空间就出来了.这里也有时间延迟,从设置减少备份数据到真正的空余空间出现.

Categories: technic Tags: , , , ,

关于hadoop和hbase的适用

May 23rd, 2008 No comments

学习hadoop也有一段时间了,在测试了多天,分析了多天,其实对其内部实现还是不是很了解.源代码不是很好看哦.

今天yahoo的一个hadoop的专家过来介绍了一下,yahoo的使用经验,普遍讲的是mapreduce这块内容.而hdfs这块涉及不多.在 hadoop的archetcture里也提到hadoop不适合存储小文件.它本身就是从nutch项目里分出来的,目的是存放大文件.不过这个专家提到,已经考虑小文件的存放,正在实现中.其实从我的测试来看,读取小文件的效率还是可以的,目前数据量不多,不知道在T级别数据量,10m文件个数的情况下会如何?专家提到了文件数量和block数量是有限制的(好像是namenode的内存中保留的mapfile的限制,jvm开的内存大小).所以目前建议存放大量小文件时打包放入.但是这样的话单个访问小文件就麻烦一点了.总总看来hdfs目前似乎并不合适存放大量的图片了.不过可以根据自己的情况再测试一下,性能可以接受的话,还是可以考虑的,毕竟比起专业的存储设备,还是便宜很多的.

hbase原先作为爬虫的数据库,存放网页的部分数据.一个网页一般不会大于100k的,所以在此level下,数据的访问速度还是可以的,但是当大于这个极限时,速度变的非常慢,其内部实现可能就是针对小数据量的优化,对于存放小文件这样的,并不适用.

目前来看该系统的设计目的应该时作为mapreduce.而不是作为文件的存储.

Categories: technic Tags: , ,

测试hbase和hadoop操作文件的性能

May 23rd, 2008 2 comments

测试hbase和hadoop操作文件的性能
1:单线程hbase的文件存入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
        String parentPath = "F:/pic/2003-zhujiajian";
        File[] files = getAllFilePath(parentPath);
 
        HBaseConfiguration config = new HBaseConfiguration();
        HTable table = new HTable(config, new Text("offer"));
        long start = System.currentTimeMillis();
        for (File file :files) {
            if(file.isFile()) {
                byte[] data = getData(file);
                createRecore(table,file.getName(),"image_big",data);
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("time cost=" + (end-start));

输出:
108037206 bytes, 303个files write from local windows to remote hbase,cost 23328 or 21001 milliseconds
2:单线程hadoop的文件存入

1
2
3
4
5
6
7
8
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);
        Path src = new Path("F:/pic/2003-zhujiajian");
        Path dst = new Path("/user/zxf/image");
        long start = System.currentTimeMillis();
        fs.copyFromLocalFile(src, dst);
        long end = System.currentTimeMillis();
        System.out.println("time cost=" + (end-start));

输出:
108037206 bytes, 303 files write from local windows to remote hdfs,cost 26531 or 32407 milliseconds

3:单线程hbase的文件读取

花费的时间慢的难以置信
108037206 bytes, 303 files read from hdfs to local cost 479350 milliseconds

4:单线程hadoop的文件读取
108037206 bytes, 303 files read from hdfs to local cost 14188 milliseconds

5:深入测试
取几个文件对比

1
2
3
4
5
 fileSize(byte)  hdfs time(ms) hbase time(ms)
 12341140        1313          14688
 708474          63            4359
 82535           15            3907
 55296           16            125

6 思考
测试期间发生了一个region offline的错误,重启服务也还是报错,后然重新format namenode, delete datanode上数据,重启发现还有datanode没有起来,ssh上去发现java进程死了
浪费了1个多小时,仔细想了一下 HTable分散到各个HRegionServer上的各子表,一台datanode挂了,当有数据请求时,连不上,所以报region offline错误

为什么hbase读取的performance那么差?我单个读取11m的文件需要14000 milliseconds,而hdfs真个文件目录的读取才14188 milliseconds
http://blog.rapleaf.com/dev/?p=26,这篇文章中说到
Finally, another thing you shouldn’t do with HBase (or an RDBMS, for that matter), is store large amounts of binary data. When I say large amounts, I mean tens to hundreds of megabytes. Certainly both RDBMSs and HBase have the capabilities to store large amounts of binary data. However, again, we have an impedance mismatch. RDBMSs are built to be fast metadata stores; HBase is designed to have lots of rows and cells, but functions best when the rows are (relatively) small. HBase splits the virtual table space into regions that can be spread out across many servers. The default size of individual files in a region is 256MB. The closer to the region limit you make each row, the more overhead you are paying to host those rows. If you have to store a lot of big files, then you’re best off storing in the local filesystem, or if you have LOTS of data, HDFS. You can still keep the metadata in an RDBMS or HBase – but do us all a favor and just keep the path in the metadata.
看来,hbase不合适存放二进制文件,存放图片这样的application还是hdfs更合适了

alter table offer change image_big IN_MEMORY;
a:重新测试了几遍,包括重启hbase,hdfs,hbase的读取速度还是和原先没大差别

b:删除原有数据,重新写入后,再测试读发现,小文件的读取效率搞了很多

1
2
3
4
5
  fileSize(byte)  1(ms)   2(ms)  3(ms)
  12341140        11750   11109  11718
  708474          625     610    672
  82535           78      78     78
  55296           47      62     47

这样就是说读cache有较大的性能提升,在data数量不是非常大的时候,瓶颈是在读取速度上,100k一下的数据读取效率还是可以的,花费时间基本上和要读取的data的长度成正比
但是之前的效率为什么没有变?难道不能cache从磁盘读取的数据?
然后试着读取了最先放入的一批文件中的几个,现在还是很慢,重复b的操作后效率提升了
原因可能是系统在创建row’s clunm data的时候打上了cache标志,cache适合clunm系统绑定在一起的,hbase启动的时候会把打了cache标志的colunm数据读到memory中.
所以在我执行alter table offer change image_big IN_MEMORY之前所创建的数据都没有cache标志, 此cache不是像其他的cache,启动的时候不做load,访问后再cache,这样一来,cache的数据愈多必然造成启动速度的加慢,我这里也有所感觉了,当然对用户体验是好的,不会在第一次访问的时候特别慢

c:那为hbase读取数据的速度为什么比hdfs慢,特别是大文件的时候慢那么多呢?过多的网络交互?
从debug日志来看,情况的确是这样,文件越大,regionServer response clinet的次数非常多.具体还需分析源代码仔细看看了.

Categories: technic Tags: , ,

hbase的搭建

May 6th, 2008 No comments

hbase的搭建
URL:http://hadoop.apache.org/hbase/docs/r0.1.1/api/overview-summary.html

在已经创建的hdfs基础上搭建
1:修改hadoop/contrib/hbase/conf/hbase-env.sh
加入java_home的路径

2:修改hadoop/contrib/hbase/conf/hbase-site.xml,加入如下

1
2
3
4
5
6
7
8
9
10
  <property>
    <name>hbase.master</name>
    <value>10.0.4.121:11100</value>
    <description>The host and port that the HBase master runs at.</description>
  </property>
  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://10.0.4.121:10100/hbase</value>
    <description>The directory shared by region servers.</description>
  </property>

3:启动hbase

1
hadoop/contrib/hbase/bin/start-hbase.sh

4: 查看http://wiki.apache.org/hadoop/Hbase/HbaseShell,进行shell操作

4.1 首先进入shell

1
 hadoop/contrib/hbase/bin/hbase shell

4.2 创建表

1
 CREATE TABLE offer(image_big,image_small);

4.2 插入数据,查询,删除数据
如:

1
2
3
4
5
6
  INSERT INTO offer(image_big:,image_small:) VALUES ('abcdefg','abc') WHERE row = 'testinsert';
  INSERT INTO offer(image_big:,image_small:) VALUES ('hijklmn','hij') WHERE row = 'testinsert';
  INSERT INTO offer(image_big:content,image_big:path,image_small:content,image_small:path) VALUES ('abcdefg','path_big','abc','path_small') WHERE row = 'testinsert';
  INSERT INTO offer(image_big:content,image_big:path,image_small:content,image_small:path) VALUES ('hijklmn','path_big','hij','path_small') WHERE row = 'testinsert';
 
  SELECT * FROM offer WHERE row = 'testinsert';

返回结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 +------------------------+-------------------------+
 | Column                 | Cell                    |
 +------------------------+-------------------------+
 | image_big:             | hijklmn                 |
 +------------------------+-------------------------+
 | image_big:content      | hijklmn                 |
 +------------------------+-------------------------+
 | image_big:path         | path_big                |
 +------------------------+-------------------------+
 | image_small:           | hij                     |
 +------------------------+-------------------------+
 | image_small:content    | hij                     |
 +------------------------+-------------------------+
 | image_small:path       | path_small              |
 +------------------------+-------------------------+
1
 SELECT count(*) FROM offer WHERE row = 'testinsert';

返回:

1
 1 row(s) in set. (0.02 sec)

从上可以看到,虽然我们插入了4条数据,但是结果是1,hbase覆盖了相同的数据,insert2覆盖insert1,insert4覆盖insert2,相当于update,从shell的介绍中我们也看到hql没有提供update
此时的数据结果应该如下:

1
2
3
4
5
6
7
 +----------+--------------------------+---------------------------+
 |          |  Column   image_big      |      Column image_small   |
 |   key    +--------------------------+---------------------------+
 |          |   :   |:content | :path  |  :  |:content|  :path     |
 +-------------------------------------+---------------------------+
 |testinsert|hijklmn|hijklmn  |path_big| hij |  hij   |  path_small|
 +----------+--------------------------+---------------------------+

加入insert加入TIMESTAMP会怎么样呢?

1
2
3
4
5
6
  DELETE * FROM offer WHERE row = 'testinsert';
 
  INSERT INTO offer(image_big:,image_small:) VALUES ('abcdefg','abc') WHERE row = 'testinsert' timestamp '1209982310285';
  INSERT INTO offer(image_big:,image_small:) VALUES ('hijklmn','hij') WHERE row = 'testinsert' timestamp '1209982311285';
  INSERT INTO offer(image_big:content,image_big:path,image_small:content,image_small:path) VALUES ('abcdefg','path_big','abc','path_small') WHERE row = 'testinsert' timestamp '1209982312285';
  INSERT INTO offer(image_big:content,image_big:path,image_small:content,image_small:path) VALUES ('hijklmn','path_big','hij','path_small') WHERE row = 'testinsert' timestamp '1209982313285';

结果无论是

1
  SELECT * FROM offer WHERE row = 'testinsert'

or

1
  SELECT * FROM offer WHERE row = 'testinsert' timestamp '1209982310285';

都只返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  +-------------------------+----------------------+
  | Column                  | Cell                 |
  +-------------------------+----------------------+
  | image_big:              | hijklmn              |
  +-------------------------+----------------------+
  | image_big:content       | hijklmn              |
  +-------------------------+----------------------+
  | image_big:path          | path_big             |
  +-------------------------+----------------------+
  | image_small:            | hij                  |
  +-------------------------+----------------------+
  | image_small:content     | hij                  |
  +-------------------------+----------------------+
  | image_small:path        | path_small           |
  +-------------------------+----------------------+

我迷惑了,如hbase Architecture介绍中是有timestamp的,数据按照时间备份的.但这里怎么理解哦…
http://www.mail-archive.com/core-user@hadoop.apache.org/msg00222.html,上面的页面中说到似乎目前还不支持,但是我这里插入是成功的;另外个人理解row和timestamp从数据结果上来说都是index级的,应该是数据本身之外的,那么不显示倒是没啥问题,但是数据好像被覆盖呢?难道目前不支持……
先delete

1
  DELETE * FROM offer WHERE row = 'testinsert';

再select

1
  SELECT * FROM offer WHERE row = 'testinsert';
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  +-------------------------+----------------------+
  | Column                  | Cell                 |
  +-------------------------+----------------------+
  | image_big:              | abcdefg              |
  +-------------------------+----------------------+
  | image_big:content       | abcdefg              |
  +-------------------------+----------------------+
  | image_big:path          | path_big             |
  +-------------------------+----------------------+
  | image_small:            | abc                  |
  +-------------------------+----------------------+
  | image_small:content     | abc                  |
  +-------------------------+----------------------+
  | image_small:path        | path_small           |
  +-------------------------+----------------------+

这个意外的发现,说明数据是有备份的,是不过没有搜索到历史数据,select中的timestamp条件好像没有起作用,每次返回都是最新的数据.架构中说道insert如果没有时间条件,系统默认会加上当前时间.

5 client访问hbase
如上次访问HDFS,引入hbase-site.xml,lib包,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  package com.chua.hadoop.client;
 
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.DataInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.util.Iterator;
  import java.util.SortedMap;
 
  import org.apache.commons.httpclient.HttpClient;
  import org.apache.commons.httpclient.methods.GetMethod;
  import org.apache.hadoop.hbase.HBaseConfiguration;
  import org.apache.hadoop.hbase.HTable;
  import org.apache.hadoop.io.Text;
 
  /**
   * 类HBase.java的实现描述:TODO 类实现描述
   * @author chua 2008-5-4 下午05:03:33
   */
  public class HBase {
 
      /**
       * @param args
       */
      public static void main(String[] args) throws Exception {
          String domain = "www.dlog.cn";
          String path_s = "/uploads/m/me/meichua/meichua_100.jpg";
          String path_b = "/uploads/m/me/meichua/200804/22094433_tLuyw.jpg";
          byte[] data_s = getData(domain, path_s);
          byte[] data_b = getData(domain,path_b);
 
          HBaseConfiguration config = new HBaseConfiguration();
          HTable table = new HTable(config, new Text("offer"));
          createRecore(table,"chua","image_big",data_b,path_b);
          createRecore(table,"chua","image_small",data_s,path_s);
 
          //取得一个row的所有data,遍历keySet
          SortedMap map = table.getRow(new Text("chua"));
          if(!map.isEmpty()) {
              Iterator it = map.keySet().iterator();
              while(it.hasNext()){
                  System.out.println(it.next());
              }
          }
          //取得某个row的colunmName的data
          byte[] data = table.get(new Text("chua"), new Text("image_big:content"));
          saveAsFile(data,"c:/chua_big.jpg");
      }
 
      public static void createRecore(HTable table,String row, String colunm,byte[] data, String path) throws IOException {
          long lockId = table.startUpdate(new Text(row));
          table.put(lockId, new Text(colunm+":content"), data);
          table.put(lockId, new Text(colunm+":path"), path.getBytes());
          table.commit(lockId);
      }
 
      /**
       * 从网上读取图片
       * @param domain
       * @param path
       * @return
       */
      public static byte[] getData(String domain,String path){
          byte[] dataResource = null;
          try {
              HttpClient client = new HttpClient();
              client.getHostConfiguration().setHost(domain,80,"http");
              GetMethod getMethod = new GetMethod(path);
              int status = client.executeMethod(getMethod);
              if(status == 200) {
                  dataResource = getMethod.getResponseBody();
              }
              getMethod.releaseConnection();
          } catch(Exception e) {  
              System.out.println("Download error"+e);
          }
          return dataResource;
      }
 
      /**
       * 从本地文件读取
       * @param path
       * @return
       */
      public static byte[] getData(String path) {
          File file = new File(path);
          DataInputStream dis = null;
          try {
              dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
              int length = dis.available();
              byte[] data = new byte[length];
              dis.read(data);
              return data;
          } catch (Exception e) {
              e.printStackTrace();
              return null;
          }
      }
 
      /**
       * 存到一个文件
       * @param data
       * @param path
       */
      public static void saveAsFile(byte[] data,String path) {
          if(data != null) {
              try {
                  BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(path));
                  for(byte tmp : data) {
                      out.write(tmp);
                  }
                  out.close();
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
  }

输出:
image_big:content
image_big:path
image_small:content
image_small:path
以上是一个client访问hbase的例子,比较简单

6 hbase架构介绍

http://wiki.apache.org/hadoop/Hbase/HbaseArchitecture

Categories: technic Tags: , ,

hadoop学习2

April 17th, 2008 2 comments

在windows上访问hadoop系统
按照hadoop的例子HadoopDFSFileReadWrite.java,在eclipse里建立项目,拷贝lib导入projext,创建conf拷贝到本地作为src一部分
修改hadoop-site.xml中的fs.default.name属性为namenode的ip形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
    static void usage() {
        System.out.println("Usage : Client <inputfile> <output file>");
        System.exit(1);
    }
 
    static void printAndExit(String str) {
        System.err.println(str);
        System.exit(1);
    }
 
    public static void main(String[] argv) throws IOException {
        for(String arg : argv) {
            System.out.println("arg="+arg);
        }
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);
 
        if (argv.length != 2) usage();
 
        // Hadoop DFS deals with Path
        Path inFile = new Path(argv[0]);
        Path outFile = new Path(argv[1]);
 
        // Check if input/output are valid
        if (!fs.exists(inFile)) {
            printAndExit("Input file not found");
        }
        if (!fs.isFile(inFile)) {
            printAndExit("Input should be a file");
        }
        if (fs.exists(outFile)) {
            printAndExit("Output already exists");
        }
 
        // Read from and write to new file
        FSDataInputStream in = fs.open(inFile);
        FSDataOutputStream out = fs.create(outFile);
        byte buffer[] = new byte[256];
        try {
            int bytesRead = 0;
            while ((bytesRead = in.read(buffer)) > 0) {
                out.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            System.out.println("Error while copying file");
        } finally {
            in.close();
            out.close();
        }
    }

运行报错:
Exception in thread “main” java.io.IOException: Login failed: Cannot run program “whoami”: CreateProcess error=2, ?????????
at org.apache.hadoop.dfs.DFSClient.createNamenode(DFSClient.java:124)
at org.apache.hadoop.dfs.DFSClient.(DFSClient.java:143)
at org.apache.hadoop.dfs.DistributedFileSystem.initialize(DistributedFileSystem.java:65)
at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:166)
at org.apache.hadoop.fs.FileSystem.getNamed(FileSystem.java:122)
at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:94)
at com.chua.hadoop.client.Client.main(Client.java:25)

网上查资料,发现windows上没有whoami命令(即使有估计也是访问不了,因为hdfs的文件权限形式;后来试了一下随便填了一个用户去做,报错:org.apache.hadoop.fs.permission.AccessControlException: Permission denied),
在hadoop-site.xml里加入

1
2
3
4
 <property>
  <name>hadoop.job.ugi</name>
  <value>zxf,zxf</value>
 </property>

还是报错
java.net.ConnectException: Connection refused

此时我又把代码放到linux下运行,还是同样的错误,期间让费了很多时间,看了很多blog,都没有相关资料,我看了hadoop中关于Permissions and Security的资料,也没有看出问题.
于是把代码放到linux下,并把把fs.default.name改回到namenode的运行参数,即:localhost:prot的形式,这次运行成功,我纳闷
分别telnet localhost prot与telnet ip port,发现localhost的可以连上,而ip的连不上,问题集中在这里了,我没有socket编程经验,所以问了旁边的人,说socket listener的建立分形式的,像我这样的情况,listener只能创建本地socket.
这次修改hdfs环境的hadoop-site.xml将fs.default.name改成ip形式,重启hdfs
然后把clinet的hadoop-site.xml中的fs.default.name改为ip,运行成功,这个问题困扰了一天时间.
windows环境的clinet运行也同样成功了