第5章大数据分布式数据库系统HBase HBase是一个具有高性能、高可靠性、面向列、可伸缩的分布式存储系统,利用HBase技术可在普通计算机上搭建大规模结构化的存储集群。HBase的目标是存储并处理大型数据,具体来说是仅需使用普通的硬件配置,就能够处理由成千上万的行和列所组成的大型数据。 与MapReduce的离线批处理计算框架不同,HBase是一个可以随机访问的存储和检索数据平台,弥补了HDFS不能随机访问数据的缺陷,适合实时性要求不高的业务场景。HBase中的数据以Byte[]数组的方式存储,它不区分数据类型,支持结构化、半结构化、非结构化数据,允许动态、灵活的数据模型。 本章简要介绍了HBase的特性及其与传统关系数据库的对比,分别介绍了HBase应用场景、数据模型、HBase工作原理、Shell操作命令,并介绍了常用的HBase编程接口以及编程实例。 5.1HBase概述 5.1.1HBase简介 HBase是一个分布式的、面向列的开源数据库,该技术来源于Google的论文《BigTable: 一个结构化数据的分布式存储系统》。HBase的目标是处理非常庞大的表,可以通过水平扩展的方式,利用计算机集群处理由超过十亿行数据和数百万列元素组成的数据表。 HBase利用Hadoop MapReduce来处理HBase中的海量数据,实现高性能计算; 利用ZooKeeper作为协同服务,实现稳定服务和失败恢复; 使用HDFS作为高可靠的底层存储,利用廉价集群提供海量数据存储能力。此外,为了方便在HBase上进行数据处理,Sqoop为HBase提供了高效、便捷的RDBMS数据导入功能,Pig和Hive为HBase提供了高层语言支持。 5.1.2HBase特性 (1) 规模大。一个表可以有上亿行、上百万列。 (2) 面向列。面向列表(族)的存储和权限控制,列(族)独立检索。 (3) 稀疏。对于为空(NULL)的列,并不占用存储空间,因此表可以设计得非常稀疏。 (4) 无模式。每一行都有一个可以排序的主键和任意多的列,列可以根据需要动态增加,同一张表中不同的行可以有截然不同的列。 (5) 数据多版本。在创建表时可以自定义多个VERSION(版本)。可以将版本数理解为开辟的存储空间数目,当写入数据的次数大于版本信息时,存储的信息将会被清理,即HBase只保存数据最新的N个版本,当版本数过多时老版本会被删除。默认情况下初始版本为1,即只提供一个版本空间存储数据。 (6) 数据类型单一。HBase中的数据都是以Byte[]数组的方式存储。 5.1.3HBase与传统关系数据库对比 关系数据库管理系统(Relational DateBase Management System,RDBMS)从20世纪70年代发展到今天,是一种非常成熟稳定的数据库管理系统,具备的功能包括面向磁盘的存储和索引结构、多线程访问、基于锁的同步访问机制、基于日志记录的回复机制和事务机制等。 但是随着Web 2.0应用的不断发展,传统的关系数据库已经无法满足Web 2.0的需求,无论在数据高并发方面,还是在高可扩展性和高可用性方面,传统的关系数据库都显得有些力不从心。关系数据库的关键特性——完善的事务机制和高效的查询机制,在Web 2.0时代也成为“鸡肋”。随着HBase在内的非关系型数据库的出现,有效弥补了传统关系数据库的缺陷,使Web 2.0应用中得到了广泛使用。HBase与RDMBS的对比如表5.1所示。 表5.1HBase与RDBMS的对比 对比项 HBase RDBMS 数据类型 用户把不同格式的结构化、非结构化数据存储为Byte[]数组,需要自己编写程序把字符串解析成不同数据类型 关系数据库采用关系模型,具有丰富的数据类型和存储方式 数据操作 HBase操作只有简单的插入、查询、删除、清空等,无法实现像关联数据库中的表与表之间连接的操作 关系数据库中包含了丰富的操作,如插入、删除、更新、查询等,其中会涉及复杂的多表连接,需要借助多个表之间的主外键关联来实现 数据存储 HBase是基于列存储的,每个列族都由几个文件保存,不同列族的文件是分离的 关系数据库是基于行模式存储的,元组或行会被连续地存储在磁盘页中 续表 对比项 HBase RDBMS 索引 HBase只有一个索引——行键 关系数据库通常可以针对不同列构建复杂的多个索引,以提高数据访问性能 数据更新 HBase在更新时,当更新次数不超过版本号时,并不会删除数据旧的版本,而是生成一个新的本,旧的版本仍然保留 在关系数据库中,更新操作会用最新的当前值去替换记录中原来的旧值,旧值被覆盖后就不存在了 扩展性 可以实现灵活的水平扩展 关系数据库很难实现横向扩展,纵向扩展的空间也比较有限 5.1.4HBase应用场景 HBase使用场景。 (1) 确信有足够大的数据。HBase适合分布在有上亿或上千亿行的数据。如果只有上百或上千万行,则用传统的RDBMS可能是更好的选择。因为所有数据可以在一两个节点保存,集群其他节点可能闲置。 (2) 确信可以不依赖所有RDBMS的额外特性(例如,列数据类型、第二索引、事务、高级查询语言等)。 (3) 确信有足够的硬件环境。因为HDFS在小于5个数据节点时,基本上体现不出它的优势。 HBase的一个典型应用是WebTable,一个以网页URL为主键的表,其中包含爬取的页面和页面的属性(如语言和MIME类型)。WebTable非常大,行数可以达十亿级别。在WebTable上连续运行用于批处理分析和解析的MapReduce作业,能够获取相关的统计信息,增加验证的MIME类型列以及人工搜索引擎进行索引的解析后的文本内容。同时,表格还会被以不同运行速度的“爬取器”(crawler)随机访问并随机更新其中的行; 在用户单击访问网站的缓存页面时,需要实时地将这些被随机访问的页面提供给他们。 5.2HBase数据模型 HBase是一个类似于BigTable的分布式数据库,它是一个稀疏的、长期存储的(存在HDFS上)、多维度的、排序的映射表。这张表的索引是行键、列(列族: 列限定符)和时间戳。HBase的数据都是字符串,没有类型。 可以将一个表想象成一个大的映射关系,通过行键+列(列族: 列限定符)+时间戳,就可以定位特定数据。由于HBase是稀疏存储数据的,所以某些列可以是空白的。 5.2.1HBase数据模型术语 1. 表 HBase采用表(table)来组织数据,表由行和列组成,列划分为若干个列族。 2. 行 每个HBase表都由若干行(row)组成,每个行由行键(row key)来标识。访问表中的行有3种方式: 全表扫描、通过单个行键访问、通过一个行键的区间来访问。行键可以是任意字符串,其最大长度为64KB,行键的长度一般在10~100字节之间,在HBase内部行键被保存的方式多为字节数组。 3. 列族 一个HBase表被分组成许多“列族”(column family)的集合,它是基本的访问控制单元。列族需要在表创建时进行定义。当列族被创建之后,数据可以被存放到列族中的某个列下面。列名都以列族作为前缀,例如,score: math和score: English这两个列都属于score这个列族。 4. 列限定符 列限定符(column qualifier)是列族中数据的索引,列族里的数据通过列限定符(或列)来定位。例如,person: student,student就可以看做是对于列族person的一个列限定符,里面放的就是关于学生的数据。列限定符没有数据类型,一般被视为字节数组Byte[]。在HBase中,为了方便操作,使用冒号(: )来分隔列族和列族修饰符,写在HBase源码中不能够修改。 5. 单元格 在HBase表中,通过行、列族和列限定符和时间戳或版本来确定一个“单元格”(cell)。一个单元格中可以保存同一份数据的多个版本。单元格的内容是不可分割的字节数组。单元格中存储的数据没有数据类型,被视为字节数组Byte[]。每个版本对应一个不同的时间戳。 6. 时间戳 每个单元格都保存着同一份数据的多个版本,这些版本采用时间戳(timestamp)进行索引。每次对一单元格执行操作时(新建、删除、修改)时,HBase都会隐式地自动生成并存储一个时间戳。在每个存储单元中,不同版本的数据按照时间戳倒序排序,即最新的版本数排在最前面。时间戳一般是64位整型,可以由用户赋值,也可以由HBase在数据库写入时自动赋值。 5.2.2HBase数据逻辑模型 下面以一个实例来阐释HBase的逻辑视图模型。表5.2是一张用来存储学生信息的HBase表,学号作为行键唯一标识每个学生。表中存有如下两组数据: 学生李明,学号1001,英语成绩100分,数学成绩80分; 学生张三,学号1002,英语成绩100分,数学成绩90分。表中的数据通过一个行键、一个列族和列限定符进行索引和查询定位。即通过{row key,column family,timestamp}可以唯一地确定一个存储值,即一个键值对: {row key, column family, timestamp}→ value 表5.2HBase数据逻辑视图——学生表 行键 列族“Sname” 列族“course” math English 时间戳 value 1001 course: math t3 80 course: English t2 100 Sname t1 LiMing 1002 course: math t6 90 course: English t5 100 Sname t4 ZhangSan 5.2.3HBase数据物理模型 在逻辑视图中,HBase中的每个表是由许多行组成的,但是在物理存储层面,它采用了基于列的存储方式,而不是像传统关系数据库那样采用基于行的存储方式,这也是HBase和传统关系数据库的重要区别。表5.2中的逻辑视图在物理存储的时候,会存储成如表5.3所示的形式。按照Sname和course这两个列族分别存放,属于同一个列族的数据会保存在一起,空的列不会被存储成null,而是不会被存储,但是当被请求时会返回null值。 表5.3HBase数据物理视图——学生表 列族 行键 列限定符 时间戳 value Sname Coures 1001 1002 1002 1001 Sname t1 LiMing Sname t4 ZhangSan math t6 90 English t5 100 math t3 80 English t2 100 5.3HBase工作原理 5.3.1HBase体系结构 面对海量数据,HBase可采用Master/Slave的方式进行分布式部署,一般采用HDFS作为底层数据存储。HBase的表包含的行的数量通常很大,无法存储在一台机器上,根据需要按行键的值对表中的行进行分区,每个行区间构成一个分区,并成为Region,它包含了位于这个区间的所有数据。HBase体系结构如图5.1所示。 图5.1HBase体系结构 5.3.2HBase工作组件 1. Client(客户端) 客户端包含访问HBase的接口,可做一些本地缓存,如借助ZooKeeper服务器从主服务器HBase Master获取Region位置信息,并从Region(HRegion)服务器上读取数据。 2. Master(HMaster) 管理运行不同的Region服务器,也为客户端操作HBase的所有元数据提供接口,同时负责RegionServer的故障处理和Region的切分。在HBase集群中可以有多个Master,实现集群的高可用,同时Master也负责对表的操作。 Master功能。 (1) 管理用户对表的增加、删除、修改、查询等操作。 (2) 实现不同Region服务器之间的负载均衡。 (3) 在Region切分或合并后,负责重新调整Region的分布。 (4) 对发生故障失效的Region服务器上的Region进行迁移。 3. Region和Region服务器 Region是HBase中分布式存储和负载均衡的最小单元,即不同的Region可以分别在不同的Region服务器上,但同一个Region是不会拆分到多个Region服务器上的。Region服务器的功能是管理Region,负责Region的切分和合并。同时,Region服务器也是HBase中最核心的模块,负责维护分配给自己的Region,并响应用户的读写请求。HBase一般采用HDFS作为底层存储文件系统,因此Region服务器需要向HDFS文件系统中读写数据。采用HDFS作为底层存储,可以为HBase提供可靠、稳定的数据存储,HBase自身并不具备数据复制和维护数据副本的功能,而HDFS可以为HBase提供这些支持。Region按大小分割,每个表一般只有一个Region,随着数据不断插入表,Region不断增大,当Region的某个列族达到一个阈值(默认256MB)时就会分成两个新的Region。 4. Store(HStore) Region虽然是分布式存储的最小单元,但并不是存储的最小单元。Region由一个或者多个Store组成,每个Store保存一个列族; 每个Strore又由一个MemStore和0至多个StoreFile组成,StoreFile包含HFile,HFile是HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile; MemStore存储在内存中,StoreFile存储在HDFS上(数据写入先写MemStore,当MemStore超过阈值(默认64MB),则会刷入以StoreFile磁盘)。HBase系统为每个Region服务器配置了一个HLog文件,它是一种预写式日志(Write Ahead Log)。 简单概括Region服务器是HBase的核心模块,而Store则是Region服务器的核心。每个Store对应了表中的一个列族的存储。每个Store包含了一个MemStore缓存和若干个StoreFile文件。 5. HLog 每当我们写一个请求,数据会先写入MemStore中,当MemStore超过了当前阈值才会被放入StoreFile中,但是在这期间一旦主机停电,那么之前MemStore缓存中的数据就会全部丢失。因此,HBase使用HLog来应对这种突发情况。 HBase要求用户更新数据必须先被记入日志后才能写入MemStore缓存,并且直到MemStore缓存内容对应的日志已经被写入磁盘后,该缓存内容才会被刷新写入磁盘。 6. ZooKeeper服务器 ZooKeeper服务器通常由多台机器构成集群来提供稳定、可靠的协同服务。在HBase服务器集群中,包含了一个Master和多个Region服务器,Master是这个HBase集群的“总管”,它必须知道Region服务器的状态。使用ZooKeeper就可以轻松做到这一点,每个Region服务器都需要到ZooKeeper中进行注册,ZooKeeper会实时监控每个Region服务器的状态并通知给Master,当某个Region服务器发生故障时,ZooKeeper会将故障通知Master。 在HBase中还可以同时启动多个Master,这时ZooKeeper不仅能够帮助维护当前的集群中的机器的服务状态,而且能够帮助选出一个Master作为“总管”,让这个“总管”来管理集群,并保证在任何时刻总有唯一一个Master在运行,避免Master的“单点失效”问题。 在ZooKeeper文件中还存储了ROOT表(不能被分割,用于实现Region定位)的地址和Master的地址。HBase通过“三级寻址”方式找到需要的数据,客户端利用ZooKeeper服务器上的ZooKeeper文件来访问HBase数据。ROOT表记录了.META.表(切分成多个Region)的Region位置信息,.META.表记录了用户数据表的Region位置信息,HBase数据访问的三层结构如图5.2所示。 图5.2HBase数据访问的三层结构 5.4HBase操作命令 HBase提供了常用的Shell命令,方便用户对表进行操作。首先,我们需要启动HDFS和HBase进程; 然后,在终端输入“hbase shell”命令即可进入Shell环境。 5.4.1HBase表操作 1. create(创建表) (1) 创建表student_1,列族为Sname,列族版本号为5(相当于可以存储最近的5个版本的记录),命令如下。 hbase>create 'student_1', {NAME => 'Sname', VERSIONS => 5} (2) 创建表student_2, 3个列族分别为Sname、gender、score,命令如下。 hbase>create 'student_2',{NAME => 'Sname'}, {NAME => 'gender}, {NAME => 'score'} 或者使用如下等价的命令(推荐)。 hbase>create 'student_2', 'Sname', 'gender, 'score' (3) 创建表student_3,将表依据分割算法HexStringSplit分布在5个Region里,命令如下。 Hbase>create 'student_3', 'Sname',{NUMREGIONS => 5, SPLITALGO => 'HexStringSplit'} (4) 创建表student_4,指定切分点,命令如下。 hbase>create 'student_4', 'Sname', {SPLITALGO => ['10','20', '30', '40']} 2. alter(修改列族模式) (1) 向表student_1中添加列族gender,命令如下。 hbase>alter 'student_1', NAME => 'gender' (2) 删除表student_1中的列族gender,命令如下。 Hbase>alter 'student_1', NAME => 'gender', METHOD => 'delete' (3) 设定表student_1中列族Sname最大值为128MB,命令如下。 hbase>alter 'student_1', METHOD => 'table_att', MAX_FILESIZE => '134217728' 以上命令中,“134217728”表示字节数,128MB等于134217728字节。 3. list(列出HBase中所有的表信息) hbase>list 4. count(统计表中的行数) 可以使用如下命令统计表student_1的行数。 hbase>count 'student_1' 5. describe(显示表的相关信息) 可以使用如下命令显示表student_1的信息。 hbase>describe 'student_1' 6. enable/disable(使表有效或无效) 可以使用如下命令使表student 1无效。 hbase>disable 'student_1' 可以使用如下命令使表student 1有效。 hbase>enable 'student_1' 7. drop(删除表) 删除某个表之前,必须先使该表无效。例如,删除表student_1,命令如下。 hbase>disable 'student_1' hbase>drop 'student_1' 8. exists(判断表是否存储) 判断表student_1是否存在,命令如下。 hbase>exists 'student_1' 9. exit(退出表) 退出HBase Shell。 hbase>exit 5.4.2HBase数据操作 1. put(向表、行、列指定的单元格添加数据) 向表student_2中行键为1001号的学生和列score: math所对应的单元格中增加数据80,时间戳设为1234(若不指定会默认生成系统当前时间与1970年1月1日零点的毫秒值之差作为时间戳)命令如下。 hbase>put 'student_2', 1001, 'score:math', 80,1234 2. get(通过指定表名、行键、列、时间戳、时间范围和版本号来获得相应单元格的值) (1) 获得表student_2、行键1001、列score: math、时间范围为[ts1,ts2]版本号为4的数据,命令如下。 hbase>get 'student_2','1001',{COLUMN =>'score:math', TIMERANGE=>[ts1, ts2], VERSIONS=> 4} (2) 获得表student_2、行键1001、列socre: math和score: English上的数据,命令如下。 hbase>get 'student_2', '1001', 'score:math', 'score:English' 3. scan(查询整个表的相关信息) 可以通过TIMERANGE、FILTER、LIMIT、STARTROW、STOPROW、TIMESTAMP、MAXLENGTH、COLUMNS、CACHE来限定所需要浏览的数据。 (1) 浏览表“.META.”、列info: regioninfo上的数据,命令如下。 hbase>scan '.META.', {COLUMNS => 'info:regioninfo'} (2) 浏览表student_1、列score: English、时间范围为[1303668804,1303668904]的数据,命令如下。 hbase>scan 'student_1', {COLUMNS => 'score:English', timerange => [1303668804, 1303668904]} 4. delete(删除指定单元格的数据) 删除表student_1、行1001、列族Sname、时间戳为ts1上的数据,命令如下。 hbase>delete 'student_1', '1001', 'Sname', ts1 5.5HBase编程接口 5.5.1HBase常用Java API 与HBase数据存储管理相关的Java API主要包括Admin、HBaseConfiguration、HTableDescriptor、HColumnDescriptor、Put、Get、ResultScanner、Result、Scan。以下讲解这些类的功能与常用方法。 1. org.apache.hadoop.hbase.client.Admin public interface Admin是一个接口,必须通过调用Connection.getAdmin()方法,返回一个实例化对象。该接口用于管理HBase数据库的表信息,包括创建表、删除表、列出表项、使表有效或无效、增加或删除表的列族成员、检查HBase的运行状态等。如表5.4所示。 表5.4Admin接口的主要方法 方法功能 void addColumn(TableName tableName,ColumnFamilyDescriptor columnFamily) 向一个已存在的表中添加列 void closeRegion(String regionname,String serverName) 关闭Resign void createTable(TableDescriptor desc) 创建表 void disableTable(TableName tableName) 使表无效 void deleteTable(TableName tableName) 删除表 Void enableTable(TableName tableName) 使表有效 Boolean tableExists(TableName tableName) 检查表是否存在 HTableDescriptor[] listTables() 列出所有表 Void void abort(String why, Throwable e) 终止服务器或客户端 Boolean balancer() 负载均衡 2. org.apache.hadoop.hbase.HBaseConfiguration 该类用于管理HBase的配置信息。如表5.5所示。 表5.5HBaseConfiguration类的主要方法 方法功能 Configuration create() 使用默认的HBase配置文件创建Configuration Configuration addHbaseResources(Configuration conf) 向当前Configuration增加conf中的配置信息 Void merge(Configuration destConf,Configuration srcConf) 合并两个Configuration 3. org.apache.hadoop.hbase.client.Table public interface Table接口,必须调用Connection.getTable()返回一个实例化对象。该接口用于与HBase进行通信。多线程情况下,使用HTablePool较好。如表5.6所示。 表5.6Table接口的主要方法 方法功能 Void close() 释放所有资源 Void delete(Delete delete) 删除指定的单元格或行 Boolean exists(Get get) 检查Get对象指定的列是否存在 Result get(Get get) 从指定行的单元格中取得相应的值 Void put(Put put) 向表中增加值 ResultScanner getScanner(byte[] family) ResultScanner getScanner(byte[] family, byte[] qualifier) ResultScanner getScanner(Scan scan)获得ResultScanner实例 HTableDescriptor getTableDescriptor() 获得当前表格的HTableDescriptor对象 TableName getName()获取当前表名 4. org.apache.hadoop.hbase.HTableDescriptor HTableDescriptor包含了HBase中表格的详细信息,如表中的列族、该表的类型(ROOT,.META.)、该表是否只读、MemStore的最大空间、Region什么时候应该切分等。如表5.7所示。 表5.7HTableDescriptor类的主要方法 方法功能 HTableDescriptor addFamily(HColumnDescriptor family) 增加列族 Collection getFamilies() 返回所有列族的名称 TableName getTableName() 返回表名实例 Byte[] getValue(Bytes key) 获得属性值 HTableDescriptor removeFamily(byte[] column) 删除列族 HTableDescriptor setValue(byte[] key, byte[] value) 设置属性的值 5. org.apache.hadoop.hbase.HColumnDescriptor HColumnDescriptor包含了列族的详细信息,如列族的版本号、压缩设置等。如表5.8所示。 表5.8HColumnDescriptor类的主要方法 方法功能 Byte[] getName() 获得列族名称 Byte[] getValue(byte[] key) 获得某列单元格的值 HColumnDescriptor setValue(byte[] key, byte[] value) 设置某列单元格的值 6. org.apache.hadoop.hbase.client.Put 用于对单元格执行增加数据操作。如表5.9所示。 表5.9Put类的主要方法 方法功能 Put addColumn(byte[] family, byte[] qualifier, byte[] value) 将指定的列族、列、值增加到Put对象中 List get(byte[] family, byte[] qualifier) 获取列族和列中的所有单元格 Boolean has(byte[] family, byte[] qualifier) 列族和列是否存在 Boolean has(byte[] family, byte[] qualifier, byte[] value) 检查列族和列中是否存在value 7. org.apache.hadoop.hbase.client.Get 用来获取单行的信息。如表5.10所示。 表5.10Get类的主要方法 方法功能 Get addColumn(byte[] family, byte[] qualifier) 根据列族和列获取对应的列 Get setFilter(Filter filter) 通过设置过滤器获取具体的列 8. org.apache.hadoop.hbase.client.Result 用于存放Get或Scan操作后的查询结果,并以的格式存储在map结构中。如表5.11所示。 表5.11Result类的主要方法 方法功能 Boolean containsColumn(byte[] family, byte[] qualifier) 检查是否包含列族和列限定符指定的列 List getColumnCells(byte[] family, byte[] qualifier) 获得列族和列限定符指定的列中的所有单元格 续表 方法功能 NavigableMap getFamilyMap(byte[] family) 根据列族获得包含列和值的所有行的键值对 Byte[] getValue(byte[] family, byte[] qualifier) 获得列族和列指定的单元格的最新值 9. org.apache.hadoop.hbase.client.ResultScanner 客户端获取值的接口,如表5.12所示。 表5.12ResultScanner类的主要方法 方法功能 Void close() 关闭scanner并释放资源 Result next() 获得下一个Result实例 10. org.apache.hadoop.hbase.client.Scan 可以利用Scan设定需要查找的数据,如设定版本号、起始行号、终止行号、列族、列名、返回值数量上限等。如表5.13所示。 表5.13Scan类的主要方法 方法功能 Scan addFamily(byte[] family) 设定列族 public Scan addColumn (byte[] family, byte[] qualifier) 设定列族和列 public Scan setMaxVersions() public Scan setMaxVersions(int maxVersions) 设定版本的最大个数 public Scan setTimeRange (long minStamp,long maxStamp) 设定最大最小时间戳范围 public Scan setFilter(Filter filter) 设定Fileter过滤 public Scan setStartRow(byte[] startRow) 设定开始的行 public Scan setStopRow(byte[] stopRow) 设定结束的行(不包含) public Scan setBatch(int batch) 设定最多返回的单元格数目 5.5.2HBase API编程实例 5.5.1节学习了HBase数据库的Java API操作,这一小节采用在Linux操作系统下,使用IDEA进行程序开发。在进行HBase编程之前需要启动Hadoop和HBase。这里使用的Hadoop版本为2.7.3,HBase版本为2.2.0。 1. 在IDEA中创建项目 IDEA启动后,呈现如图5.3所示的启动界面,选择Create New Project(创建一个新项目),弹出如图5.4所示界面,选中左边栏Java,单击Next按钮,弹出如图5.5所示的界面,输入创建项目名为hbaseDemo,项目路径可以自由定义,本书将项目路径设置为~/IdeaProjects/hbaseDemo,单击Finish按钮,IDEA启动后界面如图5.6所示。 图5.3IDEA启动界面 图5.4项目选择界面 图5.5设置项目名称和路径界面 图5.6IDEA启动后的界面 2. 为项目添加所需要的JAR包 在这里已经配置好了JDK1.8的环境,需要配置HBase需要用到的JAR包。单击左上角的File下拉菜单,选择Project Structure命令进入如图5.7所示的界面。 图5.7Project Structure中的Libraties 单击左上角的“+”号,选择Java,出现如图5.8所示的界面。为了能够编写一个能够与HBase交互的Java应用程序,需要在这个界面中加载该Java程序所需要用到的JAR包,这些JAR包中包含了可以访问HBase的Java API。这些JAR包都位于Linux系统的HBase安装目录下,在本书中位于/usr/locak/hbase/lib目录下。选中所有的.jar文件(包括文件夹下面的),单击OK按钮,如图5.9所示。 3. 编写Java应用程序 编写一个Java应用程序,对HBase数据库进行操作。具体实现功能如下。 (1) 完成与HBase数据库的连接和关闭。 (2) 创建表student_test。 (3) 向student_test表中插入一条数据: 行键1001,Sname为XiaoQiang。 (4) 在student_test表中删除一条数据。 (5) 查询数据。 在IDEA工作界面左侧的面板中(如图5.10所示),右击src文件夹,在弹出的快捷菜单中选择New→Java Class命令,弹出如图5.11所示的对话框。 图5.8添加相关jar包界面 图5.9相关jar包添加完成 图5.10新建Java Class文件 图5.11新建Java Class文件对话框 在对话框输入类名HBaseOperation,单击OK按钮,弹出如图5.12所示的界面。 图5.12新建一个类文件之后的IDEA界面 IDEA自动创建了一个名为HBaseOperation.java的源代码文件,在该文件中可以输入的代码如下。 import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.*; import org.apache.hadoop.hbase.client.*; import java.io.IOException; public class HBaseOperation { public static Configuration configuration; public static Connection connection; public static Admin admin; //主函数 public static void main(String[] args) throws IOException { createTable("student_test", new String[]{"Sname"}); insertRow("student_test", "1001", "Sname", "", "XiaoQiang"); getData("student_test", "1001", "Sname", "", "XiaoQiang"); } //建立连接 public static void init(){ //configuration.addResource("core-site.xml"); //configuration.addResource("hbase-site.xml"); configuration = HBaseConfiguration.create(); configuration.set("hbase.rootdir", "hdfs://localhost:9000/hbase"); try { connection = ConnectionFactory.createConnection(configuration); admin = connection.getAdmin(); } catch (IOException e) { e.printStackTrace(); } } //关闭连接 public static void close(){ if(admin != null){ try { admin.close(); } catch (IOException e) { e.printStackTrace(); } } if (null != connection){ try { connection.close(); } catch (IOException e) { e.printStackTrace(); } } } //建立表 @Deprecate public static void createTable(String myTableName, String[] colFamily) throws IOException{ init(); TableName tableName = TableName.valueOf(myTableName); if (admin.tableExists(tableName)){ System.out.println("该表已存在"); }else{ HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName); for (String str : colFamily) { HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(str); hTableDescriptor.addFamily(hColumnDescriptor); } admin.createTable(hTableDescriptor); } close(); } //删除表 public static void deleteTable(String tableName) throws IOException{ init(); TableName tn = TableName.valueOf(tableName); if (admin.tableExists(tn)){ admin.disableTable(tn); admin.deleteTable(tn); } close(); } //查看已有表 @Deprecate public static void listTables() throws IOException{ init(); HTableDescriptor[] hTableDescriptors = admin.listTables(); for (HTableDescriptor hTableDescriptor : hTableDescriptors) { System.out.println(hTableDescriptor.getNameAsString()); } close(); } //插入数据 public static void insertRow(String tableName, String rowKey, String colFamily, String col, String val) throws IOException{ init(); Table table = connection.getTable(TableName.valueOf(tableName)); Put put = new Put(rowKey.getBytes()); put.addColumn(colFamily.getBytes(), col.getBytes(), val.getBytes()); table.put(put); table.close(); close(); } //删除数据 public static void deleteRow(String tableName, String rowKey, String colFamily, String col, String val) throws IOException{ init(); Table table = connection.getTable(TableName.valueOf(tableName)); Delete delete = new Delete(rowKey.getBytes()); //删除指定列族 //delete.addFamily(Bytes.toBytes(colFamily)); //删除指定列 //delete.addColumn(Bytes.toBytes(colFamily), Bytes.toBytes(col)); table.delete(delete); table.close(); close(); } //根据rowkey查找数据 public static void getData(String tableName, String rowKey, String colFamily, String col, String val) throws IOException{ init(); Table table = connection.getTable(TableName.valueOf(tableName)); Get get = new Get(rowKey.getBytes()); get.addColumn(colFamily.getBytes(), col.getBytes()); Result result = table.get(get); showCell(result); table.close(); close(); } //格式化输出 public static void showCell(Result result){ Cell[] cells = result.rawCells(); for (Cell cell : cells) { System.out.println("RowName:" + new String(CellUtil.cloneRow(cell))+ ""); System.out.println("TimeStamp:" + cell.getTimestamp() + ""); System.out.println("column Family:" + new String(CellUtil.cloneFamily(cell)) + ""); System.out.println("row Name:" + new String(CellUtil.cloneQualifier(cell)) + ""); System.out.println("value" + new String(CellUtil.cloneValue(cell)) + ""); } } } 4. 编译运行程序 在确保Hadoop和HBase已经启动的情况下编译运行以上编写的代码。单击IDEA工作界面右上方的绿色箭头,即可运行程序,如图5.13所示。 图5.13运行程序 程序运行完成之后,在控制台面板中会打印写入HBase数据库的学生信息,如图5.14所示。 可以在Linux的终端中启动HBase Shell,查看生成的student_test表,启动HBase Shell的命令如图5.15所示。 图5.14控制台打印信息 图5.15启动HBase Shell的命令 进入HBase Shell,可以使用list命令查看HBase数据库中是否含有名称为student_test的表。如图5.16所示,我们发现student_test表已经创建成功。 图5.16查看HBase中的所有表 可以使用scan命令查询刚才插入到student_test表中的一行数据,如图5.17所示。 图5.17通过scan命令查询插入的数据 如果需要反复调试HBaseOperation.java代码,需要删除HBase已经创建的student test,可以使用Shell命令删除student_test表,如图5.18所示。 图5.18删除student_test表 代码中的删除一条数据和关闭Hadoop与HBase连接的操作请自行测试。 5. 应用程序的部署 可以将以上编写的Java应用程序打包后部署到远程的服务器上运行。具体的部署方法请参见本书的第14章,在此不再赘述。 习题 1. 简述HBase及HBase的特性。 2. 对比分析HBase与传统关系数据库的异同。 3. 解释下列HBase中的模型术语: 行键、列族、列限定符、单元格、时间戳。 4. 描述HBase的逻辑视图和物理视图的不同。 5. 已知职工表emp: 语文老师,姓名张三,年龄30,薪金8000; 英语老师,姓名王五,年龄35,薪金10000。请分别做出HBase逻辑视图和物理视图。 6. 简述HBase各功能组件及其作用。 7. 简述HBase的三层架构中各层次的名称和作用。 8. 列举并说明HBase的常用命令。