架构简介:
简述: Hbase基于hadoop的HDFS系统,其文件存储都是存储在HDFS上的。由上图看到HBase主要处理两种文件:一种是预写日志(Write-Ahead Log,WAL)即图中的HLog。另一种文件是实际的数据文件HFile(HFile本事就是HDFS的文件,文件格式遵循HBase可导入的格式)。预写日志WAL是可开关的,器主要保障数据的稳定性。当机器结点挂掉,可以通过回放日志达到数据恢复的目的。
HBase中最核心的HRegion模块如下图:
简述:上图就是HRegionServer的具体的系统的架构。具体的关系一目了然。各个部件的主要功能如下: HMaster:
HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master在运行,主要负责Table和Region的管理工作:
(1)管理用户对表的增删改查操作
(2)管理HRegionServer的负载均衡,调整Region分布
(3)Region Split后,负责新Region的分布
(4)在HRegionServer停机后,负责失效HRegionServer上Region迁移
Client:
(1)使用HBase RPC机制与HMaster和HRegionServer进行通信
(2)Client与HMaster进行通信进行管理类操作
(3)Client与HRegionServer进行数据读写类操作
Zookeeper:
(1)Zookeeper Quorum存储-ROOT-表地址、HMaster地址
(2)HRegionServer把自己以Ephedral方式注册到Zookeeper中,HMaster随时感知各个HRegionServer的健康状况
(4)Zookeeper避免HMaster单点问题 ps:HRegionServer管理一些列HRegion对象;每个HRegion对应的Table中一个Region,HRegion有多个HStore组成;每个HStore对应Table中一个Column Family的存储;
HBase的存储系统简单描述一下,HBase和BigTable(BigTable的论文如下:http://box.cloud.taobao.com/file/downloadFile.htm?shareLink=1G0bDlGn)一样,都是基于LSM树的系统。
下面简述一下LSM树:
在LSM树系统中:输入数据首先被存储在日志文件,这些文件内的数据完全有序。当有日志文件被修改时,对应的更新会被保存在内存中来加速查询。从宏观上来说,LSM树原理是把一个大树拆分成N棵小树,它首先写入内存中,对着小树越来越大,内存中的小树会flush到磁盘中,磁盘定期可以做merge操作,合并成一棵大树,以优化读性能。
附稿:
BigTable论文中关键信息总结:
2.1 Bigtable是一个分布式的结构化数据存储系统,他被设计用来处理海量数据:通常是分布在数千台普通服务器上的PB级数据。<br>
2.2 列族:列关键字组成的集合叫做“列族”,列族是访问控制的基本单位。存放在同一列族下的所有数据通常都属于同一个类型(我们可以把同一个列族下的数据压缩在一起)。<br>
2.3 列族创建后,其中的任何一个列关键字都可以存放数据。根据我们的设计意图,一张表中的列族不能太多(最多几百个),并且列族在运行期间很少改变。与之对应的,一张表可以由无限多个列。<br>
2.4 时间戳:在BigTable中,表的每个数据项都可以包含同一份数据的不同版本;不同版本的数据通过时间戳来索引。BigTable时间戳的类型是64位整型。BigTable可以给时间戳赋值,用来表示精确到毫秒的“实时”时间;用户程序也可以给时间赋值。如果应用程序需要避免数据版本冲突,那么必须自己生成具有唯一性的时间戳。数据项中,不同版本的数据按照时间戳倒叙排序,及最新的数据排在最前面。<br>
2.5 BigTable支持单行上的事物处理,利用这个功能,用户可以对存在一个行关键字下的数据进行原子性的读-写-更新 操作。虽然BigTable提供了一个允许用户跨行批量写入的数据的接口,但是,BigTable目前还不支持通用的跨行事物处理。<br>
2.6 BigTable是建立在其他的几个Google基础构件上的。BigTable使用Google的分布式文件系统(GFS)存储日志文件和数据文件。<br>
2.7 BigTable内部存储数据的文件是Google SSTable格式的。SSTable是一个持久化的、排序的、不可更改的Map结构,而Map是一个key-value映射数据结构,key和value的值都是任意的Byte串。
2.8 BigTable还依赖一个高可用的,序列化的分布式锁组件,叫做Chubby。(对应apache的zookeeper)。<br>
2.9 Bigtable包括了三个主要的组件:链接到客户程序中的库、一个Master服务器和多个Tablet服务器。针对系统工作负载的变化情况,BigTable可以动态的向集群中添加(或者删除)Tablet服务器。<br>
2.10 Bloom过滤器(又叫:布隆过滤器):它实际上是一个很长的二进制矢量和随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的有点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。<br>
3.WAL:WAL是标准的Hadoop SequenceFile,并且存储了HLogKey实例。这些键包括序列号和实际数据,所以在服务器崩溃时可以回滚还没有持久化的数据。
ps:一旦数据被写入WAL中,数据就会被放到MemStore中。同时还会检查Memstore是否已经满了,如果满了,就会被请求刷写到磁盘中去。
4.Table&Region:(1)Table随着记录增多不断变大,会自动分裂成多份Splits,成为Regions。(2)一个region由[startkey,endkey)表示。(3)不同region会被Master分配给相应的RegionServer进行管理。
5.数据访问结构:
首先会有zookeeperfile获取-ROOT-结点的地址,-ROOT-是一张特殊的表,并且只会存在一个Region上,记录着.META.表的位置,.META.用户表的region信息,.META.本身可以横跨多个Region。客户请求获取地址信息的过程,就是这样一个树状结构,逐级请求。
6.HBase的一个特殊的功能是,能为一个单元格(一个特定列的值)存储多个版本的数据。这是通过每个版本使用一个时间戳。并且按照降序存储来实现的。每个时间戳是一个长整型值,以毫秒为单位。
Hbase客户端简单的CRUD操作:
(1) put方法,一类作用于单行,另一类作用于多行。 ps:当每一次不指定时间戳调用Put类的add方法时,Put实例会使用来自构造函数的可选的时间戳参数(也称作ts),如果用户在构造Put实例时也没有时间指定时间戳,则时间戳将会有Region服务器设定。
(2) 原子性的put操作(compare-and-set):
接口实例:
boolean checkAndPut(byte[] row,byte[] family,byte[] qualifier,byte[] value,Put put) throws IOException
(3) get方法:
I.get方法分为两类,一类是一次获取一行数据;另一类是一次获取多行数据。获取数据时可以获取一个或多个列族的数据,也可以指定获取的列族内数据的特定的列。或者指定时间戳范围或者版本数目进行查询。
II.获取多行数据get方法的方法签名如下:
Result[] get(List<Get> gets) throws IOException
ps:用户可以一次请求获取多行数据。它允许用户快速高效地从远程服务器获取相关的或完全随机的多行数据。
(4) exists方法通过RPC验证请求的数据是否存在,但不会从远程服务器返回请求的数据,只返回一个布尔值表示这个结果。
方法签名如下:
boolean exists(Get get) throws IOException
ps:用户通过这种方法只能避免网络数据传输的开销,不过需要检查或频繁检查一个比较大的列时,这种方法还是十分实用的。 (5) Delete 方法:
Delete方法可以删除一列中特定的版本,一列中全部版本,给定版本或更旧的版本,删除整个列族,或者删除列族中所有列的给定版本或更旧的版本。
另外和Put方法类似,也可以删除一个Delete列表对象:
接口如下:
void delete(List<Delete> deletes) throws IOException
(5) 原子性操作 compare-and-delete:
方法签名如下:
boolean checkAndDelete(byte[] row,byte[] family,byte[] qualifier,byte[] value,Delete delete) throws IOException
(6) 批量操作:
上面总结了对Hbase单行的操作,Hbase还提供了批量操作的接口,可以对多行进行操作,并且可以实现多行的Put,Get或Delete操作。
方法签名如下:
void batch(List<Row> actions,Object[] results) throws IOException,InterruptedException
Object[] batch(List<Row> actions) throws IOException,InterruptedException
(7) 数据扫描 Scan:
顾名思义Scan操作能扫描遍历Hbase数据库中的数据,扫描器的获取通过 HTable.getScanner()获取,此方法返回真正的扫描器实例的同时,用户也可以使用它迭代获取数据,方法签名如下:
ResultScanner getScanner(Scan scan) throws IOException
ResultScanner getScanner(byte[] family) throws IOException
ResultScanner getScanner(byte[] family,byte[] qualifier) throws IOException
同时Scan类拥有以下构造器:
Scan()
Scan(byte[] startRow,Filter filter)
Scan(byte[] startRow)
Scan(byte[] startRow,byte[] stopRow)
(8) ResultScanner:
扫描操作一次可能请求大量的行,如果全部返回可能占用大量的内存和带宽,ResultScanner把扫描操作转换为类似get操作,将每一行数据封装成一个Result实例。
ResultScanner的一些方法如下:
Result next() throws IOException
Result[] next(int nbRows) throws IOException
void close()
2.过滤器:
Hbase过滤器(filter)提供了非常强大的特性来帮助用户提高其处理表中数据的效率。用户不仅可以使用Hbase中预定义好的过滤器,而且可以实现自定义的过滤器。
Get和Scan两个类都支持过滤器,理由如下:这类对象不能对行键、列名或列值进行过滤,但是通过过滤器可以达到这个目的。除了Hbase内置的filter外,用户可以实现Filter接口来自定义需求。所有过滤器都在服务器端生效,叫做谓词下推。
(1)比较过滤器 CompareFilter:
方法签名:
CompareFilter(CompareOp valueCompareOp,WritableByteArrayComparable valueComparator)
用户使用时需要传入操作符,和待比较的值,比如大于或小于多少才从服务器端返回数据。
(2)行过滤器 RowFilter:
方法签名:
RowFilter(CompareOp valueCompareOp,BinaryComparator rowkey)
可以过滤大于、小于或等于指定行键的数据。
(3)列族过滤器(FamilyFilter):
方法签名和行过滤器类似:
FamilyFilter(CompareOp valueCompareOp,BinaryComparator rowkey)
用来过滤满足条件的列族。
(4)值过滤器(ValueFilter):
方法签名:
ValueFilter(CompareOp valueCompareOp,BinaryComparator value)
用来过滤满足条件的列值。
(5)参考过滤器(DependentColumnFilter):
简述:这个是一种特殊的过滤器,这个过滤器的比较传入的参考值不再是指定的常量,而是相对于Hbase自身的某一行做比较。
方法签名如下:
DependentColumnFilter(byte[] family,byte[] qualifiter)
方法参数分别是参考列的列族和列名,如果某列和该列含有不同的时间戳,则被丢弃掉。
+++ DependentColumnFilter(byte[] family,byte[] qualifiter,boolean dropDependentColumn) 这个方法和上面的方法类似,只不过增加了一个布尔值定义是否包含参考咧。 +++
DependentColumnFilter(byte[] family,byte[] qualifiter,boolean dropDependentColumn,CompareOp valueCompareOp,WritableByteArrayComparable valueComparator)
这个方法定义了所有列和指定列的值的比较。 这个过滤器从参数名称上不太好理解,可以参考官网的wiki(http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/filter/DependentColumnFilter.html)
(6)单列值过滤器(SingleColumnValueFilter):
简述:用户针对如下情况是可以使用该过滤器:用一列的值决定是否一行数据被过滤。首先设定待检查的列,然后设置待检查的列的对应值。方法签名如下:
SingleColumnValueFilter(byte[] family,byte[] qualifiter,CompareOp compareOp,byte[] byte[] value)
(7)单列值排除过滤器(SingleColumnValueExcludeFilter):
简述:单列值排除过滤器集成自SingleColumnValueFilter,经过拓展后提供一种略微不同的语意:参考列不被包括在结果中。
(8)前缀过滤器(PrefixFilter):
简述:当构造当前过滤器时传入一个前缀,所有与前缀匹配的行都会被返回客户端。构造函数如下:
public PrefixFilter(byte[] prefix)
(9)分页过滤器(PageFilter):
简述:用户可以使用这个过滤器对结果按行分页。当用户创建当前过滤器实例时需要指定pagesize参数,这个参数可以控制每页返回的行数。
实例代码如下:
Filter filter = new PageFilter(15)
int totalRows = 0;
byte[] lastRow = null;
while(true) {
Scan scan = new Scan();
scan.setFilter(filter);
if(lastRow != null) {
byte[] startRow = Bytes.add(lastRow,POSTFIX);
scan.setStartRow(startRow);
}
ResultScanner scanner = table.getScanner(scan);
int localRows = 0;
Result result;
while((result = scanner.next()) != null) {
totalRows++;
lastRow = result.getRow();
}
scanner.close();
if(localRows == 0) {
break;
}
}
ps:Hbase中的行键是按字典序排列的,因此返回的结果也是如此排序的,并且起始行是被包括在结果中的。
(10)行键过滤器(KeyOnlyFilter):
简述:在一些应用中只需要将结果中的KeyValue中的键返回,而不需要返回实际的数据。利用这个过滤器可以实现这个目的。
(11)首次行键过滤器(FirstKeyOnlyFilter):
简述:这个类使用了过滤器框架提供的另一个优化特性:他在检查完第一列之后会通知region服务器结束对当前列的扫描,并跳到下一行,与全表扫描相比,其性能得到了提升。
这种过滤器通常在行数统计(row counter)的应用场景中使用。具体使用参考blog:http://blog.csdn.net/liuxiaochen123/article/details/7878580
(12)包含结束过滤器(InclusiveStopFilter):
简述:扫描操作中的开始行被包含在结果中,但终止行被排除在外。使用这个过滤器是,用户也可以将结束行包含到结果中。
(13)时间戳过滤器(TimeStampsFilter):
简述:当用户需要在扫描结果中对版本进行细粒度的控制时,这个过滤器可以满足需求。用户需要传入一个装载了时间戳list实例。
(14)列计数过滤器(ColumnCountGetFilter):
简述:用户可以使用这个过滤器来限制每行最多取回多少列。当一行列的列数达到设定的最大值时,这个过滤器会停止这个扫描操作,所以它不太适合扫描操作,反而比较适合在get()方法中使用。
(15)列前缀过滤器(ColumnPrefixFilter):
简述:类似于PrefixFilter,这个过滤器通过列名称进行前缀匹配过滤。用户需要指定一个前缀来创建过滤器。所有与设定前缀匹配的列都会包含在结果中。
(16)随机行过滤器(RandomRowFilter):
简述:这个过滤器可以让结果中包含随机的行。构造函数需要传入参数chance,chance取值区间在0.0和1.0之间。其内部实现原理用到了java 的 Random.nextFloat,当chance取负数时,将包含排除所有的行,但取值大于1.0时,将包含所有的行。
(17)跳转过滤器(SkipFilter):
简述:这个过滤器包装了一个用户提供的过滤器,当被包装的过滤器遇到一个需要过滤的KeyValue实例时,用户可以拓展并过滤掉整行数据。换句话说,当过滤器发现某一行中的一列需要过滤时,那么整行数据将被过滤掉。
(18)全匹配过滤器(WhileMatchFilter):
简述:当被过滤的数据中,遇到一行不匹配时,则放弃整个过滤操作。即列匹配才会继续执行,遇到不匹配的列则中断执行(估计也是性能优化点)。
(19)FilterList:
简述:实际应用中,用户可能需要多个过滤器共同限制返回到客户端的结果,FilterList(过滤器列表)提供了这项功能。
(20)自定义过滤器:
简述:最后,用户可能需要按各自的需求实现自定义过滤器。用户可以实现Filter接口或者直接继承FilterBase类,后者已经为接口中所有成员方法提供了默认实现。
ps:过滤器设在在Scan和Get对象当中,具体过滤器的执行全部多事在服务端执行,用以减少服务端和客户端的网络IO。
3.计数器:
Hbase内部提供了计数器,可以用来统计Hbase表的一些信息,例如:用于记录某个列值的个数。
4.客户端管理API:
除了进行数据处理的客户端API,HBase还提供了数据描述的API,类似于传统RDBMS中的DDL和DML。因为生产环境,作为基于Hbase的开发人员基本无法获取HBaseAdmin的管理权限,针对DDL和DML的API就不详细介绍了,这部分内容了解一下就可以了。
8.Bulkload数据导入,HBase提供了Bulkload数据导入的方式,可以通过Hbase自带的工具和利用其API自己写MR的方式将HDFS文件转换成HFILE的文件格式,其实其还是HDFS文件,不过文件格式变成了HBase的存储文件,然后通过其load API就能导入到数据库中。
Hfile文件格式如下图:
ps:可见其存储都是keyvalue的形式,还没完全包明白暂时不详细总结了。
HBase内置的用于生成HFILE的API是:org.apache.hadoop.hbase.mapreduce.ImportTsv
具体的调用方式如下:
$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.mapreduce.ImportTsv \
-Dimporttsv.bulk.output=$HFILE_PATH \
-Dimporttsv.separator=$SEPARATER \
-Dimporttsv.columns=$COLUMNS $TABLE_NAME $HDFS_PATH
ps: HFILEPATH:就是生成的Hfile的存储路径。SEPATATER就是数据行的分隔符。COLUMNS就是需要导入的列名,这个比较讲究,这个是根据HDFS的列的数据来定义的,比如写成:HBASEROW_KEY,cf:value1,cf:value2 就表示HDFS的第一列作为ROWKEY,这个HBASEROWKEY是约定好的关键字,这个关键字放在什么位置就表示HDFS的第几列作为rowkey,同时也就要求被转换的HDFS文件必须某一列可以作为rowkey(可能需要预处理)。然后cf就是列族,value就是对应的列名。