HBase是Apache Hadoop中的一个子项目,Hbase依托于Hadoop的HDFS作为最基本存储基础单元,通过使用hadoop的DFS工具就可以看到这些这些数据存储文件夹的结构,还可以通过Map/Reduce的框架(算法)对HBase进行操作
一、 hbase架构
1.概述。
HBase是Apache Hadoop的数据库,能够对大型数据提供随机、实时的读写访问。HBase的目标是存储并处理大型的数据。HBase是一个开源的,分布式的,多版本的,面向列的存储模型。它存储的是松散型数据。
![](https://img-blog.csdn.net/20160715152418494?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
上图是hadoop的生态系统描述,hadoop所有应用都是构建于hdfs(它提供高可靠的底层存储支持,几乎已经成为分布式文件存储系统事实上的工业标准)之上的分布式列存储系统,主要用于海量结构化数据存储。
HBase是一种NoSQL数据库. NoSQL是一个通用词表示数据库不是RDBMS ,后者支持 SQL 作为主要访问手段。有许多种 NoSQL 数据库: BerkeleyDB 是本地 NoSQL 数据库例子, 而 HBase 是大型分布式数据库。 技术上来说, HBase 更像是"数据存储(Data Store)" 多于 "数据库(Data Base)"。因为缺少很多RDBMS特性, 如列类型,第二索引,触发器,高级查询语言等
然而, HBase 有许多特征同时支持线性化和模块化扩充。 HBase 集群通过增加RegionServers进行扩充。 它可以放在普通的服务器中。例如,如果集群从10个扩充到20个RegionServer,存储空间和处理容量都同时翻倍。 RDBMS 也能很好扩充, 但仅对一个点 - 特别是对一个单独数据库服务器的大小 - 同时,为了更好的性能,需要特殊的硬件和存储设备。Hbase特性:
强一致性读写: HBase 不是 "最终一致性(eventually consistent)" 数据存储. 这让它很适合高速计数聚合类任务。
自动分片(Automatic sharding):HBase 表通过region分布在集群中。数据增长时,region会自动分割并重新分布。
RegionServer 自动故障转移
Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。
MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.
Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.
Thrift/REST API:HBase 也支持Thrift和 REST 作为非Java 前端.
Block Cache 和 Bloom Filters: 对于大容量查询优化, HBase支持 Block Cache 和 Bloom Filters。
运维管理: HBase提供内置网页用于运维视角和JMX 度量.
前文提到Hbase是一个列式存储的数据库,那么什么是列式存储,它与传统的RDBMS采用的行式存储又有什么区别?列存储不同于传统的关系型数据库,其数据在表中是按行存储的,列方式所带来的重要好处之一就是,由于查询中的选择规则是通过列来定义的,因此整个数据库是自动索引化的。按列存储每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量,一个字段的数据聚集存储,那就更容易为这种聚集存储设计更好的压缩/解压算法。这张图讲述了传统的行存储和列存储的区别:
![](https://img-blog.csdn.net/20160715152459289?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
2.hbase架构
![](https://img-blog.csdn.net/20160715152532587?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
注:准确的说位于上图下半部分的组建应该是hdfs而非hadoop,hbase并不依赖于hadoop,但是它构建于hdfs之上。
Zookeeper:
Zookeeper Quorum存储-ROOT-表地址、HMaster地址
HRegionServer把自己以Ephedral方式注册到Zookeeper中,HMaster随时感知各个HRegionServer的健康状况
Zookeeper避免HMaster单点问题
HMaster:
HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的MasterElection机制保证总有一个Master在运行
主要负责Table和Region的管理工作:
1 管理用户对表的增删改查操作
2 管理HRegionServer的负载均衡,调整Region分布
3 Region Split后,负责新Region的分布
4 在HRegionServer停机后,负责失效HRegionServer上Region迁移
HRegionServer:
HBase中最核心的模块,主要负责响应用户I/O请求,向HDFS文件系统中读写数据
![](https://img-blog.csdn.net/20160715152614196?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
HRegionServer管理一些列HRegion对象;
每个HRegion对应Table中一个Region,HRegion由多个HStore组成;
每个HStore对应Table中一个Column Family的存储;
Column Family就是一个集中的存储单元,故将具有相同IO特性的Column放在一个Column Family会更高效
HStore:
HBase存储的核心。由MemStore和StoreFile组成。
MemStore是Sorted Memory Buffer。用户写入数据的流程:
![](https://img-blog.csdn.net/20160715152645247?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
Client写入 -> 存入MemStore,一直到MemStore满 -> Flush成一个StoreFile,直至增长到一定阈值 -> 出发Compact合并操作 -> 多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除 -> 当StoreFiles Compact后,逐步形成越来越大的StoreFile ->单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个Region,Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer 上,使得原先1个Region的压力得以分流到2个Region上
由此过程可知,HBase只是增加数据,所有的更新和删除操作,都是在Compact阶段做的,所以,用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能。
HLog
引入HLog原因:
在分布式系统环境中,无法避免系统出错或者宕机,一旦HRegionServer意外退出,MemStore中的内存数据就会丢失,引入HLog就是防止这种情况
工作机制:
每 个HRegionServer中都会有一个HLog对象,HLog是一个实现Write Ahead Log的类,每次用户操作写入Memstore的同时,也会写一份数据到HLog文件,HLog文件定期会滚动出新,并删除旧的文件(已持久化到 StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知,HMaster首先处理遗留的 HLog文件,将不同region的log数据拆分,分别放到相应region目录下,然后再将失效的region重新分配,领取到这些region的 HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。
HBase存储格式
HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,格式主要有两种:
1 HFile HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile
2 HLog File,HBase中WAL(Write Ahead Log) 的存储格式,物理上是Hadoop的Sequence File
HFile
![](https://img-blog.csdn.net/20160715152722853?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
HFile文件不定长,长度固定的块只有两个:Trailer和FileInfo
Trailer中指针指向其他数据块的起始点
File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN,AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等
Data Index和Meta Index块记录了每个Data块和Meta块的起始点
Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制
每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询
每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏
HFile里面的每个KeyValue对就是一个简单的byte数组。这个byte数组里面包含了很多项,并且有固定的结构。
![](https://img-blog.csdn.net/20160715152750635?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
KeyLength和ValueLength:两个固定的长度,分别代表Key和Value的长度
Key部分:Row Length是固定长度的数值,表示RowKey的长度,Row 就是RowKey
Column Family Length是固定长度的数值,表示Family的长度
接着就是Column Family,再接着是Qualifier,然后是两个固定长度的数值,表示Time Stamp和Key Type(Put/Delete)
Value部分没有这么复杂的结构,就是纯粹的二进制数据
![](https://img-blog.csdn.net/20160715152815432?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是“写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。
HLog Sequece File的Value是HBase的KeyValue对象,即对应HFile中的KeyValue
3.什么时候应该使用hbase
在学习一门新技术的时候首先需要明白,我们到底应该在什么时候使用它,然后才是怎么去使用它,Hbase并不适合所有问题。
首先,确信有足够多数据,如果有上亿或上千亿行数据,HBase是很好的备选。如果只有上千或上百万行,则用传统的RDBMS可能是更好的选择。因为所有数据可以在一两个节点保存,集群其他节点可能闲置。
其次,确信可以不依赖所有RDBMS的额外特性 (e.g., 列数据类型, 第二索引,事物,高级查询语言等.) 一个建立在RDBMS上应用,如不能仅通过改变一个JDBC驱动移植到HBase。相对于移植, 需考虑从RDBMS 到 HBase是一次完全的重新设计。
第三, 确信你有足够硬件。甚至 HDFS 在小于5个数据节点时,干不好什么事情 (根据如HDFS 块复制具有缺省值 3), 还要加上一个NameNode.
HBase 能在单独的笔记本上运行良好。但这应仅当成开发配置。
4.目录表(.meta.和-root-)
-ROOT- 保存 .META. 表存在哪里的踪迹. -ROOT- 表结构如下:
Key:
.META. region key (.META.,,1)
Values:
info:regioninfo (序列化.META.的 HRegionInfo 实例 )
info:server ( 保存 .META.的RegionServer的server:port)
info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)
.META. 保存系统中所有region列表。 .META.表结构如下:
Key:
Region key 格式 ([table],[region start key],[region id])
Values:
info:regioninfo (序列化.META.的 HRegionInfo 实例 )
info:server ( 保存 .META.的RegionServer的server:port)
info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)
以上是官网文档对于.meta.和-root-的描述,简而言之,-root-中存储了.meta.的位置,而在.meta.中保存了具体数据(region)的存储位置。如图:
![](https://img-blog.csdn.net/20160715152841448?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
Zookeeper中记录了-ROOT-表的location
客户端访问数据的流程:
Client -> Zookeeper -> -ROOT- -> .META.-> 用户数据表
多次网络操作,不过client端有cache缓存
a. 启动时序
1.启动时主服务器调用AssignmentManager.
2.AssignmentManager 在META中查找已经存在的区域分配。
3.如果区域分配还有效(如 RegionServer 还在线),那么分配继续保持。
4.如果区域分配失效,LoadBalancerFactory 被调用来分配区域。 DefaultLoadBalancer 将随机分配区域到RegionServer.
5.META 随RegionServer 分配更新(如果需要) , RegionServer 启动区域开启代码(RegionServer 启动时进程)
b.故障转移
当regionServer故障退出时:
1.区域立即不可获取,因为区域服务器退出。
2.主服务器会检测到区域服务器退出。
3.区域分配会失效并被重新分配,如同启动时序。
5.预写日志(wal)
每个RegionServer会将更新(Puts, Deletes)先记录到预写日志中(WAL),然后将其更新在Store的MemStore里面。这样就保证了HBase的写的可靠性。如果没有WAL,当RegionServer宕掉的时候,MemStore还没有flush,StoreFile还没有保存,数据就会丢失。HLog 是HBase的一个WAL实现,一个RegionServer有一个HLog实例。
WAL 保存在HDFS 的 /hbase/.logs/ 里面,每个region一个文件。
二、 数据模型
1.概念视图
以bigTable论文中的例子来说明,有一个名为webtable的表,包含两个列族:contents和anchor.在这个例子里面,anchor有两个列 (anchor:cssnsi.com,anchor:my.look.ca),contents仅有一列(contents:html)
Row Key
Time Stamp
ColumnFamily contents
ColumnFamily anchor
"com.cnn.www"
t9
anchor:cnnsi.com = "CNN"
"com.cnn.www"
t8
anchor:my.look.ca = "CNN.com"
"com.cnn.www"
t6
contents:html = "<html>..."
"com.cnn.www"
t5
contents:html = "<html>..."
"com.cnn.www"
t3
contents:html = "<html>..."
RowKey:行键,是表中每条记录的“主键”,方便快速查找,Rowkey的设计非常重要。
Column Family:列族,拥有一个名称(string),包含一个或者多个相关列
Column:属于某一个columnfamily,每条记录可动态添加
Version Number:类型为Long,默认值是系统时间戳,可由用户自定义
Value(Cell):一个cell由familyName:columnName唯一定义
2.物理视图
尽管在概念视图里,表可以被看成是一个稀疏的行的集合。但在物理上,它的是区分列族 存储的。新的columns可以不经过声明直接加入一个列族.
Row Key
Time Stamp
Column Family anchor
"com.cnn.www"
t9
anchor:cnnsi.com = "CNN"
"com.cnn.www"
t8
anchor:my.look.ca = "CNN.com"
Row Key
Time Stamp
ColumnFamily "contents:"
"com.cnn.www"
t6
contents:html = "<html>..."
"com.cnn.www"
t5
contents:html = "<html>..."
"com.cnn.www"
t3
contents:html = "<html>..."
值得注意的是在上面的概念视图中空白cell在物理上是不存储的,因为根本没有必要存储。因此若一个请求为要获取t8时间的contents:html,他的结果就是空。相似的,若请求为获取t9时间的anchor:my.look.ca,结果也是空。但是,如果不指明时间,将会返回最新时间的行,每个最新的都会返回。例如,如果请求为获取行键为"com.cnn.www",没有指明时间戳的话,活动的结果是t6下的contents:html,t9下的anchor:cnnsi.com和t8下anchor:my.look.ca。
对于hbase我一直有一个疑问,在hbase提供了修改和删除的接口,但是hdfs本身很难实现修改和删除(可以将文件块从hdfs中下载,进行修改再上传),那么hbase是如何实现快速的删除与修改呢?实际上在HBase中,修改和删除数据都是增加1个新版本的数据(时间戳为最新),旧版本的数据并没有发生变化,而实际上的修改和删除是在Hfile的合并阶段实现的。
三、 Hbase优化
1. 预先分区
默认情况下,在创建 HBase 表的时候会自动创建一个 Region 分区,当导入数据的时候,所有的 HBase 客户端都向这一个 Region 写数据,直到这个 Region 足够大了才进行切分。一种可以加快批量写入速度的方法是通过预先创建一些空的 Regions,这样当数据写入 HBase 时,会按照 Region 分区情况,在集群内做数据的负载均衡。
2. Rowkey优化
HBase 中 Rowkey 是按照字典序存储,因此,设计 Rowkey 时,要充分利用排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。
此外,Rowkey 若是递增的生成,建议不要使用正序直接写入 Rowkey,而是采用 reverse 的方式反转Rowkey,使得 Rowkey 大致均衡分布,这样设计有个好处是能将 RegionServer 的负载均衡,否则容易产生所有新数据都在一个 RegionServer 上堆积的现象,这一点还可以结合 table 的预切分一起设计。
3. 减少列族数量
不要在一张表里定义太多的 ColumnFamily。目前 Hbase 并不能很好的处理超过 2~3 个 ColumnFamily 的表。因为某个 ColumnFamily 在 flush 的时候,它邻近的 ColumnFamily 也会因关联效应被触发 flush,最终导致系统产生更多的 I/O。
4. 缓存策略
创建表的时候,可以通过 HColumnDescriptor.setInMemory(true) 将表放到 RegionServer 的缓存中,保证在读取的时候被 cache 命中。
5. 设置存储生命期
创建表的时候,可以通过 HColumnDescriptor.setTimeToLive(int timeToLive) 设置表中数据的存储生命期,过期数据将自动被删除。
6. 硬盘配置
每台 RegionServer 管理 10~1000 个 Regions,每个 Region 在 1~2G,则每台 Server 最少要 10G,最大要1000*2G=2TB,考虑 3 备份,则要 6TB。方案一是用 3 块 2TB 硬盘,二是用 12 块 500G 硬盘,带宽足够时,后者能提供更大的吞吐率,更细粒度的冗余备份,更快速的单盘故障恢复。
7. 分配合适的内存给RegionServer服务
在不影响其他服务的情况下,越大越好。例如在 HBase 的 conf 目录下的 hbase-env.sh 的最后添加 export HBASE_REGIONSERVER_OPTS="-Xmx16000m$HBASE_REGIONSERVER_OPTS”
其中 16000m 为分配给 RegionServer 的内存大小。
8. 写数据的备份数
备份数与读性能成正比,与写性能成反比,且备份数影响高可用性。有两种配置方式,一种是将 hdfs-site.xml拷贝到 hbase 的 conf 目录下,然后在其中添加或修改配置项 dfs.replication 的值为要设置的备份数,这种修改对所有的 HBase 用户表都生效,另外一种方式,是改写 HBase 代码,让 HBase 支持针对列族设置备份数,在创建表时,设置列族备份数,默认为 3,此种备份数只对设置的列族生效。
9. WAL(预写日志)
可设置开关,表示 HBase 在写数据前用不用先写日志,默认是打开,关掉会提高性能,但是如果系统出现故障(负责插入的 RegionServer 挂掉),数据可能会丢失。配置 WAL 在调用 JavaAPI 写入时,设置 Put 实例的WAL,调用 Put.setWriteToWAL(boolean)。
10. 批量写
HBase 的 Put 支持单条插入,也支持批量插入,一般来说批量写更快,节省来回的网络开销。在客户端调用JavaAPI 时,先将批量的 Put 放入一个 Put 列表,然后调用 HTable 的 Put(Put 列表) 函数来批量写。
11. 客户端一次从服务器拉取的数量
通过配置一次拉去的较大的数据量可以减少客户端获取数据的时间,但是它会占用客户端内存。有三个地方可进行配置:
1)在 HBase 的 conf 配置文件中进行配置 hbase.client.scanner.caching;
2)通过调用 HTable.setScannerCaching(intscannerCaching) 进行配置;
3)通过调用 Scan.setCaching(intcaching) 进行配置。三者的优先级越来越高。
12. RegionServer的请求处理I/O线程数
较少的 IO 线程适用于处理单次请求内存消耗较高的 Big Put 场景 (大容量单次 Put 或设置了较大 cache 的Scan,均属于 Big Put) 或 ReigonServer 的内存比较紧张的场景。
较多的 IO 线程,适用于单次请求内存消耗低,TPS 要求 (每秒事务处理量 (TransactionPerSecond)) 非常高的场景。设置该值的时候,以监控内存为主要参考。
在 hbase-site.xml 配置文件中配置项为 hbase.regionserver.handler.count。
13. Region的大小设置
配置项为 hbase.hregion.max.filesize,所属配置文件为 hbase-site.xml.,默认大小 256M。
在当前 ReigonServer 上单个 Reigon 的最大存储空间,单个 Region 超过该值时,这个 Region 会被自动 split成更小的 Region。小 Region 对 split 和 compaction 友好,因为拆分 Region 或 compact 小 Region 里的StoreFile 速度很快,内存占用低。缺点是 split 和 compaction 会很频繁,特别是数量较多的小 Region 不停地split, compaction,会导致集群响应时间波动很大,Region 数量太多不仅给管理上带来麻烦,甚至会引发一些Hbase 的 bug。一般 512M 以下的都算小 Region。大 Region 则不太适合经常 split 和 compaction,因为做一次 compact 和 split 会产生较长时间的停顿,对应用的读写性能冲击非常大。
此外,大 Region 意味着较大的 StoreFile,compaction 时对内存也是一个挑战。如果你的应用场景中,某个时间点的访问量较低,那么在此时做 compact 和 split,既能顺利完成 split 和 compaction,又能保证绝大多数时间平稳的读写性能。compaction 是无法避免的,split 可以从自动调整为手动。只要通过将这个参数值调大到某个很难达到的值,比如 100G,就可以间接禁用自动 split(RegionServer 不会对未到达 100G 的 Region 做split)。再配合 RegionSplitter 这个工具,在需要 split 时,手动 split。手动 split 在灵活性和稳定性上比起自动split 要高很多,而且管理成本增加不多,比较推荐 online 实时系统使用。内存方面,小 Region 在设置memstore 的大小值上比较灵活,大 Region 则过大过小都不行,过大会导致 flush 时 app 的 IO wait 增高,过小则因 StoreFile 过多影响读性能。
14. 操作系统参数
Linux系统最大可打开文件数一般默认的参数值是1024,如果你不进行修改并发量上来的时候会出现“Too Many Open Files”的错误,导致整个HBase不可运行,你可以用ulimit -n 命令进行修改,或者修改/etc/security/limits.conf和/proc/sys/fs/file-max 的参数,具体如何修改可以去Google 关键字 “linux limits.conf ”
15. Jvm配置
修改 hbase-env.sh 文件中的配置参数,根据你的机器硬件和当前操作系统的JVM(32/64位)配置适当的参数
HBASE_HEAPSIZE 4000 HBase使用的 JVM 堆的大小
HBASE_OPTS "‐server ‐XX:+UseConcMarkSweepGC"JVM GC 选项
HBASE_MANAGES_ZKfalse 是否使用Zookeeper进行分布式管理
16. 持久化
重启操作系统后HBase中数据全无,你可以不做任何修改的情况下,创建一张表,写一条数据进行,然后将机器重启,重启后你再进入HBase的shell中使用 list 命令查看当前所存在的表,一个都没有了。是不是很杯具?没有关系你可以在hbase/conf/hbase-default.xml中设置hbase.rootdir的值,来设置文件的保存位置指定一个文件夹,例如:<value>file:///you/hbase-data/path</value>,你建立的HBase中的表和数据就直接写到了你的磁盘上,同样你也可以指定你的分布式文件系统HDFS的路径例如:hdfs://NAMENODE_SERVER:PORT/HBASE_ROOTDIR,这样就写到了你的分布式文件系统上了。
17. 缓冲区大小
hbase.client.write.buffer
这个参数可以设置写入数据缓冲区的大小,当客户端和服务器端传输数据,服务器为了提高系统运行性能开辟一个写的缓冲区来处理它,这个参数设置如果设置的大了,将会对系统的内存有一定的要求,直接影响系统的性能。
18. 扫描目录表
hbase.master.meta.thread.rescanfrequency
定义多长时间HMaster对系统表 root 和 meta 扫描一次,这个参数可以设置的长一些,降低系统的能耗。
19. split/compaction时间间隔
hbase.regionserver.thread.splitcompactcheckfrequency
这个参数是表示多久去RegionServer服务器运行一次split/compaction的时间间隔,当然split之前会先进行一个compact操作.这个compact操作可能是minorcompact也可能是major compact.compact后,会从所有的Store下的所有StoreFile文件最大的那个取midkey.这个midkey可能并不处于全部数据的mid中.一个row-key的下面的数据可能会跨不同的HRegion。
20. 缓存在JVM堆中分配的百分比
hfile.block.cache.size
指定HFile/StoreFile 缓存在JVM堆中分配的百分比,默认值是0.2,意思就是20%,而如果你设置成0,就表示对该选项屏蔽。
21. ZooKeeper客户端同时访问的并发连接数
hbase.zookeeper.property.maxClientCnxns
这项配置的选项就是从zookeeper中来的,表示ZooKeeper客户端同时访问的并发连接数,ZooKeeper对于HBase来说就是一个入口这个参数的值可以适当放大些。
22. memstores占用堆的大小参数配置
hbase.regionserver.global.memstore.upperLimit
在RegionServer中所有memstores占用堆的大小参数配置,默认值是0.4,表示40%,如果设置为0,就是对选项进行屏蔽。
23. Memstore中缓存写入大小
hbase.hregion.memstore.flush.size
Memstore中缓存的内容超过配置的范围后将会写到磁盘上,例如:删除操作是先写入MemStore里做个标记,指示那个value, column 或 family等下是要删除的,HBase会定期对存储文件做一个major compaction,在那时HBase会把MemStore刷入一个新的HFile存储文件中。如果在一定时间范围内没有做major compaction,而Memstore中超出的范围就写入磁盘上了。
-------------------------------------------------------------------------------------------------------------------
- 概述
HBase是Apache Hadoop中的一个子项目,Hbase依托于Hadoop的HDFS作为最基本存储基础单元,通过使用hadoop的DFS工具就可以看到这些这些数据存储文件夹的结构,还可以通过Map/Reduce的框架(算法)对HBase进行操作
一、 hbase架构
1.概述。
HBase是Apache Hadoop的数据库,能够对大型数据提供随机、实时的读写访问。HBase的目标是存储并处理大型的数据。HBase是一个开源的,分布式的,多版本的,面向列的存储模型。它存储的是松散型数据。
上图是hadoop的生态系统描述,hadoop所有应用都是构建于hdfs(它提供高可靠的底层存储支持,几乎已经成为分布式文件存储系统事实上的工业标准)之上的分布式列存储系统,主要用于海量结构化数据存储。
HBase是一种NoSQL数据库. NoSQL是一个通用词表示数据库不是RDBMS ,后者支持 SQL 作为主要访问手段。有许多种 NoSQL 数据库: BerkeleyDB 是本地 NoSQL 数据库例子, 而 HBase 是大型分布式数据库。 技术上来说, HBase 更像是"数据存储(Data Store)" 多于 "数据库(Data Base)"。因为缺少很多RDBMS特性, 如列类型,第二索引,触发器,高级查询语言等
然而, HBase 有许多特征同时支持线性化和模块化扩充。 HBase 集群通过增加RegionServers进行扩充。 它可以放在普通的服务器中。例如,如果集群从10个扩充到20个RegionServer,存储空间和处理容量都同时翻倍。 RDBMS 也能很好扩充, 但仅对一个点 - 特别是对一个单独数据库服务器的大小 - 同时,为了更好的性能,需要特殊的硬件和存储设备。Hbase特性:
强一致性读写: HBase 不是 "最终一致性(eventually consistent)" 数据存储. 这让它很适合高速计数聚合类任务。
自动分片(Automatic sharding):HBase 表通过region分布在集群中。数据增长时,region会自动分割并重新分布。
RegionServer 自动故障转移
Hadoop/HDFS 集成: HBase 支持本机外HDFS 作为它的分布式文件系统。
MapReduce: HBase 通过MapReduce支持大并发处理, HBase 可以同时做源和目标.
Java 客户端 API: HBase 支持易于使用的 Java API 进行编程访问.
Thrift/REST API:HBase 也支持Thrift和 REST 作为非Java 前端.
Block Cache 和 Bloom Filters: 对于大容量查询优化, HBase支持 Block Cache 和 Bloom Filters。
运维管理: HBase提供内置网页用于运维视角和JMX 度量.
前文提到Hbase是一个列式存储的数据库,那么什么是列式存储,它与传统的RDBMS采用的行式存储又有什么区别?列存储不同于传统的关系型数据库,其数据在表中是按行存储的,列方式所带来的重要好处之一就是,由于查询中的选择规则是通过列来定义的,因此整个数据库是自动索引化的。按列存储每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量,一个字段的数据聚集存储,那就更容易为这种聚集存储设计更好的压缩/解压算法。这张图讲述了传统的行存储和列存储的区别:
2.hbase架构
注:准确的说位于上图下半部分的组建应该是hdfs而非hadoop,hbase并不依赖于hadoop,但是它构建于hdfs之上。
Zookeeper:
Zookeeper Quorum存储-ROOT-表地址、HMaster地址
HRegionServer把自己以Ephedral方式注册到Zookeeper中,HMaster随时感知各个HRegionServer的健康状况
Zookeeper避免HMaster单点问题
HMaster:
HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的MasterElection机制保证总有一个Master在运行
主要负责Table和Region的管理工作:
1 管理用户对表的增删改查操作
2 管理HRegionServer的负载均衡,调整Region分布
3 Region Split后,负责新Region的分布
4 在HRegionServer停机后,负责失效HRegionServer上Region迁移
HRegionServer:
HBase中最核心的模块,主要负责响应用户I/O请求,向HDFS文件系统中读写数据
HRegionServer管理一些列HRegion对象;
每个HRegion对应Table中一个Region,HRegion由多个HStore组成;
每个HStore对应Table中一个Column Family的存储;
Column Family就是一个集中的存储单元,故将具有相同IO特性的Column放在一个Column Family会更高效
HStore:
HBase存储的核心。由MemStore和StoreFile组成。
MemStore是Sorted Memory Buffer。用户写入数据的流程:
Client写入 -> 存入MemStore,一直到MemStore满 -> Flush成一个StoreFile,直至增长到一定阈值 -> 出发Compact合并操作 -> 多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除 -> 当StoreFiles Compact后,逐步形成越来越大的StoreFile ->单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个Region,Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer 上,使得原先1个Region的压力得以分流到2个Region上
由此过程可知,HBase只是增加数据,所有的更新和删除操作,都是在Compact阶段做的,所以,用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能。
HLog
引入HLog原因:
在分布式系统环境中,无法避免系统出错或者宕机,一旦HRegionServer意外退出,MemStore中的内存数据就会丢失,引入HLog就是防止这种情况
工作机制:
每 个HRegionServer中都会有一个HLog对象,HLog是一个实现Write Ahead Log的类,每次用户操作写入Memstore的同时,也会写一份数据到HLog文件,HLog文件定期会滚动出新,并删除旧的文件(已持久化到 StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知,HMaster首先处理遗留的 HLog文件,将不同region的log数据拆分,分别放到相应region目录下,然后再将失效的region重新分配,领取到这些region的 HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。
HBase存储格式
HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,格式主要有两种:
1 HFile HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile
2 HLog File,HBase中WAL(Write Ahead Log) 的存储格式,物理上是Hadoop的Sequence File
HFile
HFile文件不定长,长度固定的块只有两个:Trailer和FileInfo
Trailer中指针指向其他数据块的起始点
File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN,AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等
Data Index和Meta Index块记录了每个Data块和Meta块的起始点
Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制
每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询
每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏
HFile里面的每个KeyValue对就是一个简单的byte数组。这个byte数组里面包含了很多项,并且有固定的结构。
KeyLength和ValueLength:两个固定的长度,分别代表Key和Value的长度
Key部分:Row Length是固定长度的数值,表示RowKey的长度,Row 就是RowKey
Column Family Length是固定长度的数值,表示Family的长度
接着就是Column Family,再接着是Qualifier,然后是两个固定长度的数值,表示Time Stamp和Key Type(Put/Delete)
Value部分没有这么复杂的结构,就是纯粹的二进制数据
HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是“写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。
HLog Sequece File的Value是HBase的KeyValue对象,即对应HFile中的KeyValue
3.什么时候应该使用hbase
在学习一门新技术的时候首先需要明白,我们到底应该在什么时候使用它,然后才是怎么去使用它,Hbase并不适合所有问题。
首先,确信有足够多数据,如果有上亿或上千亿行数据,HBase是很好的备选。如果只有上千或上百万行,则用传统的RDBMS可能是更好的选择。因为所有数据可以在一两个节点保存,集群其他节点可能闲置。
其次,确信可以不依赖所有RDBMS的额外特性 (e.g., 列数据类型, 第二索引,事物,高级查询语言等.) 一个建立在RDBMS上应用,如不能仅通过改变一个JDBC驱动移植到HBase。相对于移植, 需考虑从RDBMS 到 HBase是一次完全的重新设计。
第三, 确信你有足够硬件。甚至 HDFS 在小于5个数据节点时,干不好什么事情 (根据如HDFS 块复制具有缺省值 3), 还要加上一个NameNode.
HBase 能在单独的笔记本上运行良好。但这应仅当成开发配置。
4.目录表(.meta.和-root-)
-ROOT- 保存 .META. 表存在哪里的踪迹. -ROOT- 表结构如下:
Key:
.META. region key (.META.,,1)
Values:
info:regioninfo (序列化.META.的 HRegionInfo 实例 )
info:server ( 保存 .META.的RegionServer的server:port)
info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)
.META. 保存系统中所有region列表。 .META.表结构如下:
Key:
Region key 格式 ([table],[region start key],[region id])
Values:
info:regioninfo (序列化.META.的 HRegionInfo 实例 )
info:server ( 保存 .META.的RegionServer的server:port)
info:serverstartcode ( 保存 .META.的RegionServer进程的启动时间)
以上是官网文档对于.meta.和-root-的描述,简而言之,-root-中存储了.meta.的位置,而在.meta.中保存了具体数据(region)的存储位置。如图:
Zookeeper中记录了-ROOT-表的location
客户端访问数据的流程:
Client -> Zookeeper -> -ROOT- -> .META.-> 用户数据表
多次网络操作,不过client端有cache缓存
a. 启动时序
1.启动时主服务器调用AssignmentManager.
2.AssignmentManager 在META中查找已经存在的区域分配。
3.如果区域分配还有效(如 RegionServer 还在线),那么分配继续保持。
4.如果区域分配失效,LoadBalancerFactory 被调用来分配区域。 DefaultLoadBalancer 将随机分配区域到RegionServer.
5.META 随RegionServer 分配更新(如果需要) , RegionServer 启动区域开启代码(RegionServer 启动时进程)
b.故障转移
当regionServer故障退出时:
1.区域立即不可获取,因为区域服务器退出。
2.主服务器会检测到区域服务器退出。
3.区域分配会失效并被重新分配,如同启动时序。
5.预写日志(wal)
每个RegionServer会将更新(Puts, Deletes)先记录到预写日志中(WAL),然后将其更新在Store的MemStore里面。这样就保证了HBase的写的可靠性。如果没有WAL,当RegionServer宕掉的时候,MemStore还没有flush,StoreFile还没有保存,数据就会丢失。HLog 是HBase的一个WAL实现,一个RegionServer有一个HLog实例。
WAL 保存在HDFS 的 /hbase/.logs/ 里面,每个region一个文件。
二、 数据模型
1.概念视图
以bigTable论文中的例子来说明,有一个名为webtable的表,包含两个列族:contents和anchor.在这个例子里面,anchor有两个列 (anchor:cssnsi.com,anchor:my.look.ca),contents仅有一列(contents:html)
Row Key | Time Stamp | ColumnFamily contents | ColumnFamily anchor |
"com.cnn.www" | t9 |
| anchor:cnnsi.com = "CNN" |
"com.cnn.www" | t8 |
| anchor:my.look.ca = "CNN.com" |
"com.cnn.www" | t6 | contents:html = "<html>..." |
|
"com.cnn.www" | t5 | contents:html = "<html>..." |
|
"com.cnn.www" | t3 | contents:html = "<html>..." |
|
RowKey:行键,是表中每条记录的“主键”,方便快速查找,Rowkey的设计非常重要。
Column Family:列族,拥有一个名称(string),包含一个或者多个相关列
Column:属于某一个columnfamily,每条记录可动态添加
Version Number:类型为Long,默认值是系统时间戳,可由用户自定义
Value(Cell):一个cell由familyName:columnName唯一定义
2.物理视图
尽管在概念视图里,表可以被看成是一个稀疏的行的集合。但在物理上,它的是区分列族 存储的。新的columns可以不经过声明直接加入一个列族.
Row Key | Time Stamp | Column Family anchor |
"com.cnn.www" | t9 | anchor:cnnsi.com = "CNN" |
"com.cnn.www" | t8 | anchor:my.look.ca = "CNN.com" |
Row Key | Time Stamp | ColumnFamily "contents:" |
"com.cnn.www" | t6 | contents:html = "<html>..." |
"com.cnn.www" | t5 | contents:html = "<html>..." |
"com.cnn.www" | t3 | contents:html = "<html>..." |
值得注意的是在上面的概念视图中空白cell在物理上是不存储的,因为根本没有必要存储。因此若一个请求为要获取t8时间的contents:html,他的结果就是空。相似的,若请求为获取t9时间的anchor:my.look.ca,结果也是空。但是,如果不指明时间,将会返回最新时间的行,每个最新的都会返回。例如,如果请求为获取行键为"com.cnn.www",没有指明时间戳的话,活动的结果是t6下的contents:html,t9下的anchor:cnnsi.com和t8下anchor:my.look.ca。
对于hbase我一直有一个疑问,在hbase提供了修改和删除的接口,但是hdfs本身很难实现修改和删除(可以将文件块从hdfs中下载,进行修改再上传),那么hbase是如何实现快速的删除与修改呢?实际上在HBase中,修改和删除数据都是增加1个新版本的数据(时间戳为最新),旧版本的数据并没有发生变化,而实际上的修改和删除是在Hfile的合并阶段实现的。
三、 Hbase优化
1. 预先分区
默认情况下,在创建 HBase 表的时候会自动创建一个 Region 分区,当导入数据的时候,所有的 HBase 客户端都向这一个 Region 写数据,直到这个 Region 足够大了才进行切分。一种可以加快批量写入速度的方法是通过预先创建一些空的 Regions,这样当数据写入 HBase 时,会按照 Region 分区情况,在集群内做数据的负载均衡。
2. Rowkey优化
HBase 中 Rowkey 是按照字典序存储,因此,设计 Rowkey 时,要充分利用排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。
此外,Rowkey 若是递增的生成,建议不要使用正序直接写入 Rowkey,而是采用 reverse 的方式反转Rowkey,使得 Rowkey 大致均衡分布,这样设计有个好处是能将 RegionServer 的负载均衡,否则容易产生所有新数据都在一个 RegionServer 上堆积的现象,这一点还可以结合 table 的预切分一起设计。
3. 减少列族数量
不要在一张表里定义太多的 ColumnFamily。目前 Hbase 并不能很好的处理超过 2~3 个 ColumnFamily 的表。因为某个 ColumnFamily 在 flush 的时候,它邻近的 ColumnFamily 也会因关联效应被触发 flush,最终导致系统产生更多的 I/O。
4. 缓存策略
创建表的时候,可以通过 HColumnDescriptor.setInMemory(true) 将表放到 RegionServer 的缓存中,保证在读取的时候被 cache 命中。
5. 设置存储生命期
创建表的时候,可以通过 HColumnDescriptor.setTimeToLive(int timeToLive) 设置表中数据的存储生命期,过期数据将自动被删除。
6. 硬盘配置
每台 RegionServer 管理 10~1000 个 Regions,每个 Region 在 1~2G,则每台 Server 最少要 10G,最大要1000*2G=2TB,考虑 3 备份,则要 6TB。方案一是用 3 块 2TB 硬盘,二是用 12 块 500G 硬盘,带宽足够时,后者能提供更大的吞吐率,更细粒度的冗余备份,更快速的单盘故障恢复。
7. 分配合适的内存给RegionServer服务
在不影响其他服务的情况下,越大越好。例如在 HBase 的 conf 目录下的 hbase-env.sh 的最后添加 export HBASE_REGIONSERVER_OPTS="-Xmx16000m$HBASE_REGIONSERVER_OPTS”
其中 16000m 为分配给 RegionServer 的内存大小。
8. 写数据的备份数
备份数与读性能成正比,与写性能成反比,且备份数影响高可用性。有两种配置方式,一种是将 hdfs-site.xml拷贝到 hbase 的 conf 目录下,然后在其中添加或修改配置项 dfs.replication 的值为要设置的备份数,这种修改对所有的 HBase 用户表都生效,另外一种方式,是改写 HBase 代码,让 HBase 支持针对列族设置备份数,在创建表时,设置列族备份数,默认为 3,此种备份数只对设置的列族生效。
9. WAL(预写日志)
可设置开关,表示 HBase 在写数据前用不用先写日志,默认是打开,关掉会提高性能,但是如果系统出现故障(负责插入的 RegionServer 挂掉),数据可能会丢失。配置 WAL 在调用 JavaAPI 写入时,设置 Put 实例的WAL,调用 Put.setWriteToWAL(boolean)。
10. 批量写
HBase 的 Put 支持单条插入,也支持批量插入,一般来说批量写更快,节省来回的网络开销。在客户端调用JavaAPI 时,先将批量的 Put 放入一个 Put 列表,然后调用 HTable 的 Put(Put 列表) 函数来批量写。
11. 客户端一次从服务器拉取的数量
通过配置一次拉去的较大的数据量可以减少客户端获取数据的时间,但是它会占用客户端内存。有三个地方可进行配置:
1)在 HBase 的 conf 配置文件中进行配置 hbase.client.scanner.caching;
2)通过调用 HTable.setScannerCaching(intscannerCaching) 进行配置;
3)通过调用 Scan.setCaching(intcaching) 进行配置。三者的优先级越来越高。
12. RegionServer的请求处理I/O线程数
较少的 IO 线程适用于处理单次请求内存消耗较高的 Big Put 场景 (大容量单次 Put 或设置了较大 cache 的Scan,均属于 Big Put) 或 ReigonServer 的内存比较紧张的场景。
较多的 IO 线程,适用于单次请求内存消耗低,TPS 要求 (每秒事务处理量 (TransactionPerSecond)) 非常高的场景。设置该值的时候,以监控内存为主要参考。
在 hbase-site.xml 配置文件中配置项为 hbase.regionserver.handler.count。
13. Region的大小设置
配置项为 hbase.hregion.max.filesize,所属配置文件为 hbase-site.xml.,默认大小 256M。
在当前 ReigonServer 上单个 Reigon 的最大存储空间,单个 Region 超过该值时,这个 Region 会被自动 split成更小的 Region。小 Region 对 split 和 compaction 友好,因为拆分 Region 或 compact 小 Region 里的StoreFile 速度很快,内存占用低。缺点是 split 和 compaction 会很频繁,特别是数量较多的小 Region 不停地split, compaction,会导致集群响应时间波动很大,Region 数量太多不仅给管理上带来麻烦,甚至会引发一些Hbase 的 bug。一般 512M 以下的都算小 Region。大 Region 则不太适合经常 split 和 compaction,因为做一次 compact 和 split 会产生较长时间的停顿,对应用的读写性能冲击非常大。
此外,大 Region 意味着较大的 StoreFile,compaction 时对内存也是一个挑战。如果你的应用场景中,某个时间点的访问量较低,那么在此时做 compact 和 split,既能顺利完成 split 和 compaction,又能保证绝大多数时间平稳的读写性能。compaction 是无法避免的,split 可以从自动调整为手动。只要通过将这个参数值调大到某个很难达到的值,比如 100G,就可以间接禁用自动 split(RegionServer 不会对未到达 100G 的 Region 做split)。再配合 RegionSplitter 这个工具,在需要 split 时,手动 split。手动 split 在灵活性和稳定性上比起自动split 要高很多,而且管理成本增加不多,比较推荐 online 实时系统使用。内存方面,小 Region 在设置memstore 的大小值上比较灵活,大 Region 则过大过小都不行,过大会导致 flush 时 app 的 IO wait 增高,过小则因 StoreFile 过多影响读性能。
14. 操作系统参数
Linux系统最大可打开文件数一般默认的参数值是1024,如果你不进行修改并发量上来的时候会出现“Too Many Open Files”的错误,导致整个HBase不可运行,你可以用ulimit -n 命令进行修改,或者修改/etc/security/limits.conf和/proc/sys/fs/file-max 的参数,具体如何修改可以去Google 关键字 “linux limits.conf ”
15. Jvm配置
修改 hbase-env.sh 文件中的配置参数,根据你的机器硬件和当前操作系统的JVM(32/64位)配置适当的参数
HBASE_HEAPSIZE 4000 HBase使用的 JVM 堆的大小
HBASE_OPTS "‐server ‐XX:+UseConcMarkSweepGC"JVM GC 选项
HBASE_MANAGES_ZKfalse 是否使用Zookeeper进行分布式管理
16. 持久化
重启操作系统后HBase中数据全无,你可以不做任何修改的情况下,创建一张表,写一条数据进行,然后将机器重启,重启后你再进入HBase的shell中使用 list 命令查看当前所存在的表,一个都没有了。是不是很杯具?没有关系你可以在hbase/conf/hbase-default.xml中设置hbase.rootdir的值,来设置文件的保存位置指定一个文件夹,例如:<value>file:///you/hbase-data/path</value>,你建立的HBase中的表和数据就直接写到了你的磁盘上,同样你也可以指定你的分布式文件系统HDFS的路径例如:hdfs://NAMENODE_SERVER:PORT/HBASE_ROOTDIR,这样就写到了你的分布式文件系统上了。
17. 缓冲区大小
hbase.client.write.buffer
这个参数可以设置写入数据缓冲区的大小,当客户端和服务器端传输数据,服务器为了提高系统运行性能开辟一个写的缓冲区来处理它,这个参数设置如果设置的大了,将会对系统的内存有一定的要求,直接影响系统的性能。
18. 扫描目录表
hbase.master.meta.thread.rescanfrequency
定义多长时间HMaster对系统表 root 和 meta 扫描一次,这个参数可以设置的长一些,降低系统的能耗。
19. split/compaction时间间隔
hbase.regionserver.thread.splitcompactcheckfrequency
这个参数是表示多久去RegionServer服务器运行一次split/compaction的时间间隔,当然split之前会先进行一个compact操作.这个compact操作可能是minorcompact也可能是major compact.compact后,会从所有的Store下的所有StoreFile文件最大的那个取midkey.这个midkey可能并不处于全部数据的mid中.一个row-key的下面的数据可能会跨不同的HRegion。
20. 缓存在JVM堆中分配的百分比
hfile.block.cache.size
指定HFile/StoreFile 缓存在JVM堆中分配的百分比,默认值是0.2,意思就是20%,而如果你设置成0,就表示对该选项屏蔽。
21. ZooKeeper客户端同时访问的并发连接数
hbase.zookeeper.property.maxClientCnxns
这项配置的选项就是从zookeeper中来的,表示ZooKeeper客户端同时访问的并发连接数,ZooKeeper对于HBase来说就是一个入口这个参数的值可以适当放大些。
22. memstores占用堆的大小参数配置
hbase.regionserver.global.memstore.upperLimit
在RegionServer中所有memstores占用堆的大小参数配置,默认值是0.4,表示40%,如果设置为0,就是对选项进行屏蔽。
23. Memstore中缓存写入大小
hbase.hregion.memstore.flush.size
Memstore中缓存的内容超过配置的范围后将会写到磁盘上,例如:删除操作是先写入MemStore里做个标记,指示那个value, column 或 family等下是要删除的,HBase会定期对存储文件做一个major compaction,在那时HBase会把MemStore刷入一个新的HFile存储文件中。如果在一定时间范围内没有做major compaction,而Memstore中超出的范围就写入磁盘上了。
HBase是一个构建在HDFS上的分布式列存储系统;
HBase是基于Google BigTable模型开发的,典型的key/value系统;
HBase是Apache Hadoop生态系统中的重要一员,主要用于海量结构化数据存储;
从逻辑上讲,HBase将数据按照表、行和列进行存储。
与hadoop一样,Hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加计算和存储能力。
Hbase表的特点
大:一个表可以有数 十亿行 ,上 百万列 ;
无模式:每行都有一个可排序的主键和任意多的列,列可以根据需要动态的增加,同一张表中 不同的行可以有截然不同的列 ;
面向列:面向列(族)的存储和权限控制, 列(族)独立检索 ;
稀疏: 空(null)列并不占用存储空间 ,表可以设计的非常稀疏;
数据多版本:每个单元中的数据可以有多个版本,默认情况下版本号自动分配,是单元格插入时的时间戳;
数据类型单一:Hbase中的数据都是字符串,没有类型。
- Hbase数据模型
Hbase逻辑视图
注意上图中的英文说明
Hbase基本概念
RowKey:是Byte array,是表中每条记录的“主键”,方便快速查找,Rowkey的设计非常重要。
Column Family:列族,拥有一个名称(string),包含一个或者多个相关列
Column:属于某一个columnfamily,familyName:columnName,每条记录可动态添加
Version Number:类型为Long,默认值是系统时间戳,可由用户自定义
Value(Cell):Byte array
- Hbase物理模型
每个column family存储在HDFS上的一个单独文件中,空值不会被保存。
Key 和 Version number在每个 column family中均有一份;
HBase 为每个值维护了多级索引,即:<key, column family, column name, timestamp>
物理存储:
1、Table中所有行都按照row key的字典序排列;
2、Table在行的方向上分割为多个Region;
3、Region按大小分割的,每个表开始只有一个region,随着数据增多,region不断增大,当增大到一个阀值的时候,region就会等分会两个新的region,之后会有越来越多的region;
4、Region是Hbase中分布式存储和负载均衡的最小单元,不同Region分布到不同RegionServer上。
5、Region虽然是分布式存储的最小单元,但并不是存储的最小单元。Region由一个或者多个Store组成,每个store保存一个columns family;每个Strore又由一个memStore和0至多个StoreFile组成,StoreFile包含HFile;memStore存储在内存中,StoreFile存储在HDFS上。![]()
- HBase架构及基本组件
Hbase基本组件说明:
Client
包含访问HBase的接口,并维护cache来加快对HBase的访问,比如region的位置信息
Master
为Region server分配region
负责Region server的负载均衡
发现失效的Region server并重新分配其上的region
管理用户对table的增删改查操作
Region Server
Regionserver维护region,处理对这些region的IO请求
Regionserver负责切分在运行过程中变得过大的region
Zookeeper作用
通过选举,保证任何时候,集群中只有一个master,Master与RegionServers 启动时会向ZooKeeper注册
存贮所有Region的寻址入口
实时监控Region server的上线和下线信息。并实时通知给Master
存储HBase的schema和table元数据
默认情况下,HBase 管理ZooKeeper 实例,比如, 启动或者停止ZooKeeper
Zookeeper的引入使得Master不再是单点故障
Write-Ahead-Log(WAL)
该机制用于数据的容错和恢复:
每个HRegionServer中都有一个HLog对象,HLog是一个实现Write Ahead Log的类,在每次用户操作写入MemStore的同时,也会写一份数据到HLog文件中(HLog文件格式见后续),HLog文件定期会滚动出新的,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的 HLog文件,将其中不同Region的Log数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取 到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复
HBase容错性
Master容错 :Zookeeper重新选择一个新的Master
无Master过程中,数据读取仍照常进行;
无master过程中,region切分、负载均衡等无法进行;
RegionServer容错 :定时向Zookeeper汇报心跳,如果一旦时间内未出现心跳,Master将该RegionServer上的Region重新分配到其他RegionServer上,失效服务器上“预写”日志由主服务器进行分割并派送给新的RegionServer
Zookeeper容错 :Zookeeper是一个可靠地服务,一般配置3或5个Zookeeper实例
Region定位流程:
寻找RegionServer
ZooKeeper--> -ROOT-(单Region)--> .META.--> 用户表
-ROOT-表包含.META.表所在的region列表,该表只会有一个Region;
Zookeeper中记录了-ROOT-表的location。
.META.表包含所有的用户空间region列表,以及RegionServer的服务器地址。
- Hbase使用场景
storing large amounts of data(100s of TBs)
need high write throughput
need efficient random access(key lookups) within large data sets
need to scale gracefully with data
for structured and semi-structured data
don't need fullRDMS capabilities(cross row/cross table transaction, joins,etc.)
大数据量存储,大数据量高并发操作
需要对数据随机读写操作
读写访问均是非常简单的操作
- Hbase与HDFS对比
两者都具有良好的容错性和扩展性,都可以扩展到成百上千个节点;
HDFS适合批处理场景
不支持数据随机查找
不适合增量数据处理
不支持数据更新
-------------------------------------------------------------------------------------------------------------------------------
region split流程
- region先更改ZK中该region的状态为SPLITING。
- Master检测到region状态改变。
- region会在存储目录下新建.split文件夹用于保存split后的daughter region信息。
- Parent region关闭数据写入并触发flush操作,保证所有写入Parent region的数据都能持久化。
- 在.split文件夹下新建两个region,称之为daughter A、daughter B。
- Daughter A、Daughter B拷贝到HBase根目录下,形成两个新的region。
- Parent region通知修改.META.表后下线,不再提供服务。
- Daughter A、Daughter B上线,开始向外提供服务。
- 如果开启了balance_switch服务,split后的region将会被重新分布。
上面1 ~ 9就是region split的整个过程,split过程非常快,速度基本会在秒级内,那么在这么快的时间内,region中的数据怎么被重新组织的?
其实,split只是简单的把region从逻辑上划分成两个,并没有涉及到底层数据的重组,split完成后,Parent region并没有被销毁,只是被做下线处理,不再对外部提供服务。而新产生的region Daughter A和Daughter B,内部的数据只是简单的到Parent region数据的索引,Parent region数据的清理在Daughter A和Daughter B进行major compact以后,发现已经没有到其内部数据的索引后,Parent region才会被真正的清理。
1. hbase简介
1.1.什么是hbase
HBASE是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模结构化存储集群。
HBASE的目标是存储并处理大型的数据,更具体来说是仅需使用普通的硬件配置,就能够处理由成千上万的行和列所组成的大型数据。
HBASE是Google Bigtable的开源实现,但是也有很多不同之处。比如:Google Bigtable利用GFS作为其文件存储系统,HBASE利用Hadoop HDFS作为其文件存储系统;Google运行MAPREDUCE来处理Bigtable中的海量数据,HBASE同样利用Hadoop MapReduce来处理HBASE中的海量数据;Google Bigtable利用Chubby作为协同服务,HBASE利用Zookeeper作为对应。
上图描述了HadoopEcoSystem中的各层系统,其中HBase位于结构化存储层,Hadoop HDFS为HBase提供了高可靠性的底层存储支持,Hadoop MapReduce为HBase提供了高性能的计算能力,Zookeeper为HBase提供了稳定服务和failover机制。
此外,Pig和Hive还为HBase提供了高层语言支持,使得在HBase上进行数据统计处理变的非常简单。 Sqoop则为HBase提供了方便的RDBMS数据导入功能,使得传统数据库数据向HBase中迁移变的非常方便。
作为NoSQL家庭的一员,HBase的出现弥补了Hadoop只能离线批处理的不足,能够存储小文件,能够提供海量数据的随机检索,同时能保证一定的性能。而这些特性也完善了整个Hadoop生态系统,泛化其大数据的处理能力,结合其高性能、稳定、扩展性好的忒行,给使用大数据的企业带来了福音。
1.2. 与传统数据库的对比
1、传统数据库遇到的问题:
1) 数据量很大的时候无法存储
2) 没有很好的备份机制
3) 数据达到一定数量开始缓慢,很大的话基本无法支撑
2、HBASE优势:
1)线性扩展,随着数据量增多可以通过节点扩展进行支撑
2)数据存储在hdfs上,备份机制健全
3)通过zookeeper协调查找数据,访问速度块。
Hbase表的特点:
1.容量巨大
HBase的单表可以有百亿行、百万列,数据矩阵横向和纵向两个维度所支持的数据量级都非常弹性。传统的关系型数据库,如Oracle和MySQL等,如果数据记录在亿级别,查询和写入的性能都会指数级下降,所以更大的数据量级对传统数据库来讲是一种灾难。而HBase对于存储百亿、千亿甚至更多的数据都不存在任何问题。对于高维数据,百万量级的列没有任何问题。有的读者可能关心更加多的列:千万和亿级别,这种非常特殊的应用场景,并不是说HBase不支持,而是这种情况下访问单个Rowkey可能造成访问超时,如果限定某个列则不会出现这种问题。
2.面向列
HBase是面向列的存储和权限控制,并支持列独立检索。可能不清晰什么是列存储,下面做一下简单介绍。列存储不同于传统的关系型数据库,其数据在表中是按某列存储的,这样在查询只需要少数几个字段的时候,能大大减少读取的数据量,一个字段的数据聚集存储,那就更容易为这种聚集存储设计更好的压缩和解压算法。图1-2描述了行存储与列存储的区别。
图1-2 行列存储和列式存储的区别
列式存储不但解决了数据稀疏性问题,最大程度上节省存储开销,而且在查询发生时,仅检索查询涉及的列,能够大量降低磁盘IO。这些特性也支撑HBase能够保证一定的读写性能。
3.稀疏性
在大多数情况下,采用传统行式存储的数据往往是稀疏的,即存在大量为空(NULL)的列,而这些列都是占用存储空间的,这就造成了存储空间的浪费。对于HBase来讲,为空的列并不占用存储空间,因此,表可以设计得非常稀疏。
4.扩展性
HBase底层文件存储依赖HDFS,从“基因”上决定了其具备可扩展性。这种遗传的可扩展性就如同OOP中的继承,“父类”HDFS的扩展性遗传到HBase框架中。这是最底层的关键点。同时,HBase的Region和RegionServer的概念对应的数据可以分区,分区后数据可以位于不同的机器上,所以在HBase核心架构层面也具备可扩展性。HBase的扩展性是热扩展,在不停止现有服务的前提下,可以随时添加或者减少节点。
5.高可靠性
HBase提供WAL和Replication机制。前者保证了数据写入时不会因集群异常而导致写入数据的丢失;后者保证了在集群出现严重问题时,数据不会发生丢失或者损坏。而且HBase底层使用HDFS,HDFS本身的副本机制很大程度上保证了HBase的高可靠性。同时,协调服务的Zookeeper组件,是经过工业验证的,具备高可用性和高可靠性。
6.高性能
底层的LSM数据结构和Rowkey有序排列等架构上的独特设计,使得HBase具备非常高的写入性能。Region划分、索引和缓存使得HBase在海量数据下具备一定的随机读取性能,该性能针对Rowkey酌查询能够到毫秒级别。同时,HBase对于高并发的场景也具备很好的适应能力。该特性也是业界众多公司选取HBase作为存储数据库非常重要的一点。
Hbase的优缺点:
1) 列的可以动态增加,并且列为空就不存储数据,节省存储空间.
2) Hbase自动切分数据,使得数据存储自动具有水平scalability.
3) Hbase可以提供高并发读写操作的支持
Hbase的缺点:
l 不能支持条件查询,只支持按照Row key来查询.
l 暂时不能支持Master server的故障切换,当Master宕机后,整个存储系统就会挂掉.
补充:
1. 数据类型,Hbase只有简单的字符类型,所有的类型都是交由用户自己处理,它只保存字符串。而关系数据库有丰富的类型和存储方式。
2. 数据操作:HBase只有很简单的插入、查询、删除、清空等操作,表和表之间是分离的,没有复杂的表和表之间的关系,而传统数据库通常有各式各样的函数和连接操作。
3. 存储模式:HBase是基于列存储的,每个列族都由几个文件保存,不同的列族的文件时分离的。而传统的关系型数据库是基于表格结构和行模式保存的
4. 数据维护,HBase的更新操作不应该叫更新,它实际上是插入了新的数据,而传统数据库是替换修改
5. 可伸缩性,Hbase这类分布式数据库就是为了这个目的而开发出来的,所以它能够轻松增加或减少硬件的数量,并且对错误的兼容性比较高。而传统数据库通常需要增加中间层才能实现类似的功能
1.3. hbase集群中的角色
l 一个或者多个主节点,Hmaster
l 多个从节点,HregionServer
HBase数据模型
Table & Column Family
Row Key | Timestamp | Column Family | |
URI | Parser | ||
r1 | t3 | url=http://www.taobao.com | title=天天特价 |
t2 | host=taobao.com |
| |
t1 |
|
| |
r2 | t5 | url=http://www.alibaba.com | content=每天… |
t4 | host=alibaba.com |
|
l Row Key: 行键,Table的主键,Table中的记录按照Row Key排序
l Timestamp: 时间戳,每次数据操作对应的时间戳,可以看作是数据的version number
l Column Family:列簇,Table在水平方向有一个或者多个Column Family组成,一个Column Family中可以由任意多个Column组成,即Column Family支持动态扩展,无需预先定义Column的数量以及类型,所有Column均以二进制格式存储,用户需要自行进行类型转换。
Table & Region
当Table随着记录数不断增加而变大后,会逐渐分裂成多份splits,成为regions,一个region由[startkey,endkey)表示,不同的region会被Master分配给相应的RegionServer进行管理:
-ROOT- && .META. Table
HBase中有两张特殊的Table,-ROOT-和.META.
l META.:记录了用户表的Region信息,.META.可以有多个regoin
l ROOT-:记录了.META.表的Region信息,-ROOT-只有一个region
l Zookeeper中记录了-ROOT-表的location
Client访问用户数据之前需要首先访问zookeeper,然后访问-ROOT-表,接着访问.META.表,最后才能找到用户数据的位置去访问,中间需要多次网络操作,不过client端会做cache缓存。
MapReduce on HBase
在HBase系统上运行批处理运算,最方便和实用的模型依然是MapReduce,如下图:
HBase Table和Region的关系,比较类似HDFS File和Block的关系,HBase提供了配套的TableInputFormat和TableOutputFormat API,可以方便的将HBase Table作为Hadoop MapReduce的Source和Sink,对于MapReduceJob应用开发人员来说,基本不需要关注HBase系统自身的细节。
HBase系统架构
Client
HBaseClient使用HBase的RPC机制与HMaster和HRegionServer进行通信,对于管理类操作,Client与HMaster进行RPC;对于数据读写类操作,Client与HRegionServer进行RPC
Zookeeper
l Zookeeper Quorum中除了存储了-ROOT-表的地址和HMaster的地址,HRegionServer也会把自己以Ephemeral方式注册到Zookeeper中,使得HMaster可以随时感知到各个HRegionServer的健康状态。此外,Zookeeper也避免了HMaster的单点问题;
l 通过选举,保证任何时候,集群中只有一个master,Master与RegionServers启动时会向ZooKeeper注册
l 存贮所有Region的寻址入口
l 实时监控Regionserver的上线和下线信息。并实时通知给Master
l 存储HBase的schema和table元数据
l 默认情况下,HBase 管理ZooKeeper 实例,比如, 启动或者停止ZooKeeper
l Zookeeper的引入使得Master不再是单点故障
HMaster
HMaster没有单点问题,HBase中可以启动多个HMaster,通过Zookeeper的MasterElection机制保证总有一个Master运行,HMaster在功能上主要负责Table和Region的管理工作:
1. 管理用户对Table的增、删、改、查操作
2. 管理HRegionServer的负载均衡,调整Region分布
3. 在Region Split后,负责新Region的分配
4. 在HRegionServer停机后,负责失效HRegionServer 上的Regions迁移
HRegionServer
HRegionServer主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。
HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个Region,HRegion中由多个HStore组成。每个HStore对应了Table中的一个ColumnFamily的存储,可以看出每个Column Family其实就是一个集中的存储单元,因此最好将具备共同IO特性的column放在一个ColumnFamily中,这样最高效。
HStore存储是HBase存储的核心了,其中由两部分组成,一部分是MemStore,一部分是StoreFiles。MemStore是Sorted Memory Buffer,用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile),当StoreFile文件数量增长到一定阈值,会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进行版本合并和数据删除,因此可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的写操作只要进入内存中就可以立即返回,保证了HBaseI/O的高性能。当StoreFiles Compact后,会逐步形成越来越大的StoreFile,当单个StoreFile大小超过一定阈值后,会触发Split操作,同时把当前Region Split成2个Region,父Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上。下图描述了Compaction和Split的过程:
在理解了上述HStore的基本原理后,还必须了解一下HLog的功能,因为上述的HStore在系统正常工作的前提下是没有问题的,但是在分布式系统环境中,无法避免系统出错或者宕机,因此一旦HRegionServer意外退出,MemStore中的内存数据将会丢失,这就需要引入HLog了。每个HRegionServer中都有一个HLog对象,HLog是一个实现WriteAhead Log的类,在每次用户操作写入MemStore的同时,也会写一份数据到HLog文件中(HLog文件格式见后续),HLog文件定期会滚动出新的,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的 HLog文件,将其中不同Region的Log数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取 到这些region的HRegionServer在Load Region的过程中,会发现有历史HLog需要处理,因此会Replay HLog中的数据到MemStore中,然后flush到StoreFiles,完成数据恢复。
HBase存储格式
HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,主要包括上述提出的两种文件类型:
1. HFile, HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile
2. HLog File,HBase中WAL(Write Ahead Log) 的存储格式,物理上是Hadoop的Sequence File
HFile
下图是HFile的存储格式:
首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。正如图中所示的,Trailer中有指针指向其他数据块的起始点。File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN,LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。Data Index和Meta Index块记录了每个Data块和Meta块的起始点。
Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制。每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询。每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成, Magic内容就是一些随机数字,目的是防止数据损坏。后面会详细介绍每个KeyValue对的内部构造。
HFile里面的每个KeyValue对就是一个简单的byte数组。但是这个byte数组里面包含了很多项,并且有固定的结构。我们来看看里面的具体结构:
开始是两个固定长度的数值,分别表示Key的长度和Value的长度。紧接着是Key,开始是固定长度的数值,表示RowKey的长度,紧接着是RowKey,然后是固定长度的数值,表示Family的长度,然后是Family,接着是Qualifier,然后是两个固定长度的数值,表示Time Stamp和Key Type(Put/Delete)。Value部分没有这么复杂的结构,就是纯粹的二进制数据了。
HLogFile
上图中示意了HLog文件的结构,其实HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是“写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。
HLogSequece File的Value是HBase的KeyValue对象,即对应HFile中的KeyValue,可参见上文描述。
HBase的安装部署
1.下载并tar开hbase-1.2.5-bin.tar.gz
2.移动到/soft下
$>mv hbase-1.2.5-bin /data
3.配置环境变量
[etc/environment]
HBASE_HOME=/data/hbase-1.2.5-bin
Path="...:/data/hbase-1.2.5-bin/bin"
4.配置hbase的本地模式
<property>
<name>hbase.rootdir</name>
<value>file:///home/neworigin/hbase/root</value>
</property>
5.start-hbase.sh启动
6. Shell 练习
用shell连接你的Hbase
$ ./bin/hbase shell
HBase Shell; enter 'help<RETURN>'for list of supported commands.
Type "exit<RETURN>" toleave the HBase Shell
Version: 0.90.0, r1001068, Fri Sep 2413:55:42 PDT 2010
hbase(main):001:0>
输入 help 然后 <RETURN> 可以看到一列shell命令。这里的帮助很详细,要注意的是表名,行和列需要加引号。
创建一个名为 test 的表,这个表只有一个 column family 为 cf。可以列出所有的表来检查创建情况,然后插入些值。
hbase(main):003:0> create 'test','cf'
0 row(s) in 1.2200 seconds
hbase(main):003:0> list 'table'
test
1 row(s) in 0.0550 seconds
hbase(main):004:0> put 'test','row1', 'cf:a', 'value1'
0 row(s) in 0.0560 seconds
hbase(main):005:0> put 'test','row2', 'cf:b', 'value2'
0 row(s) in 0.0370 seconds
hbase(main):006:0> put 'test','row3', 'cf:c', 'value3'
0 row(s) in 0.0450 seconds
以上我们分别插入了3行。第一个行key为row1, 列为 cf:a, 值是 value1。Hbase中的列是由 column family前缀和列的名字组成的,以冒号间隔。例如这一行的列名就是a.
检查插入情况.
Scan这个表,操作如下
hbase(main):007:0> scan 'test'
ROW COLUMN+CELL
row1 column=cf:a, timestamp=1288380727188,value=value1
row2 column=cf:b, timestamp=1288380738440,value=value2
row3 column=cf:c, timestamp=1288380747365,value=value3
3 row(s) in 0.0590 seconds
Get一行,操作如下
hbase(main):008:0> get 'test', 'row1'
COLUMN CELL
cf:a timestamp=1288380727188, value=value1
1 row(s) in 0.0400 seconds
disable 再drop 这张表,可以清除你刚刚的操作
hbase(main):012:0> disable 'test'
0 row(s) in 1.0930 seconds
hbase(main):013:0> drop 'test'
0 row(s) in 0.0770 seconds
关闭shell
hbase(main):014:0> exit
运行停止脚本来停止HBase.
$ ./bin/stop-hbase.sh
stopping hbase...............
hbase数据模型
hbase数据模型
Row Key
与nosql数据库们一样,row key是用来检索记录的主键。访问HBASE table中的行,只有三种方式:
1.通过单个row key访问
2.通过row key的range(正则)
3.全表扫描
Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在HBASE内部,row key保存为字节数组。存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)
Columns Family
列簇 :HBASE表中的每个列,都归属于某个列族。列族是表的schema的一部 分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如 courses:history,courses:math都属于courses 这个列族。
Cell
由{row key, columnFamily, version} 唯一确定的单元。cell中 的数据是没有类型的,全部是字节码形式存贮。
关键字:无类型、字节码
Time Stamp
HBASE 中通过rowkey和columns确定的为一个存贮单元称为cell。每个 cell都保存 着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由HBASE(在数据写入时自动 )赋值,此时时间戳是精确到毫秒 的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版 本冲突,就必须自己生成具有唯一性的时间戳。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,HBASE提供了两种数据版本回收方式。一是保存数据的最后n个版本,二是保存最近一段 时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。
命令
名称 | 命令表达式 |
创建表 | create '表名', '列族名1','列族名2','列族名N' |
查看所有表 | list |
描述表 | describe ‘表名’ |
判断表存在 | exists '表名' |
判断是否禁用启用表 | is_enabled '表名' is_disabled ‘表名’ |
添加记录 | put ‘表名’, ‘rowKey’, ‘列族 : 列‘ , '值' |
查看记录rowkey下的所有数据 | get '表名' , 'rowKey' |
查看表中的记录总数 | count '表名' |
获取某个列族 | get '表名','rowkey','列族' |
获取某个列族的某个列 | get '表名','rowkey','列族:列’ |
删除记录 | delete ‘表名’ ,‘行名’ , ‘列族:列' |
删除整行 | deleteall '表名','rowkey' |
删除一张表 | 先要屏蔽该表,才能对该表进行删除 第一步 disable ‘表名’ ,第二步 drop '表名' |
清空表 | truncate '表名' |
查看所有记录 | scan "表名" |
查看某个表某个列中所有数据 | scan "表名" , {COLUMNS=>'列族名:列名'} |
更新记录 | 就是重写一遍,进行覆盖,hbase没有修改,都是追加 |
HBase运行模式:单机,伪分布式,完全分布式
1.单机模式
在单机模式中,Hbase使用本地文件系统,而不是HDFS ,所以的服务和zooKeeper都运作在一个JVM中。zookeep监听一个端口,这样客户端就可以连接Hbase了。
2.伪分布式
伪分布式模式是一个相对简单的分布式模式。这个模式是用来测试的。不能把这个模式用于生产环节,也不能用于测试性能。
<property>
<name>hbase.rootdir</name>
<value>hdfs://s100:8020/hbase</value>
</property>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
注意:
让Hbase自己创建 hbase.rootdir 目录,如果你自己建这个目录,会有一个warning,Hbase会试图在里面进行migration操作,但是缺少必须的文件。
3.完全分布式模式
要想运行完全分布式模式,你要进行如下配置,先在 hbase-site.xml, 加一个属性hbase.cluster.distributed 设置为 true 然后把 hbase.rootdir 设置为HDFS的NameNode的位置。
hbase依赖zookeeper
1、 保存Hmaster的地址和backup-master地址
hmaster:
a) 管理HregionServer
b) 做增删改查表的节点
c) 管理HregionServer中的表分配
2、 保存表-ROOT-的地址
hbase默认的根表,检索表。
3、 HRegionServer列表
表的增删改查数据。
和hdfs交互,存取数据。
zk组件
--------------------------------------
1.client
向server周期性发送信息,表明自己还活着,server向client回应确认消息,
client没有收响应,自动重定向消息到其他server
2.server
一个zk节点,向client提供所有服务,通知client,server是alive的
3.ensemble
全体,一组zk节点,需要最小值3
4.leader
领袖,特殊的zk节点,zk集群启动时,推选leader,leader在follower故障进行处理
5.follower
随从,听命于leader的指令
zk的namespace的等级结构(zk data model)
---------------------------------------
1.驻留内存
2.树上的每个节点都是Znode
3.每个znode都有name,而且可分割
4.每个znode存放的数据不能超过1M
5.每个节点都有state对象
a.version :与之关联的数据发生改变,版本增加。
b.acl :action control list
c.timestamp
d.datalength
zk znode节点类型
---------------------------------------
1.持久节点
2.临时节点
3.顺序节点
安装zookeeper集群
1.在s100上面上传zk压缩包
>cp/mnt/hgfs/BigData/第九天/zookeeper-3.4.10.tar.gz /data/
解压
>cd /data
>tar -xzvfzookeeper-3.4.10.tar.gz
2.发送到其他节点
>xsync/data/zookeeper-3.4.10
3.配置环境
[/etc/environment]
ZOOKEEPER_HOME=/data/zookeeper-3.4.10
Path=”$Path:/data/zookeeper-3.4.10/bin”
4.重启
>sudo reboot
5.测试
>zk
6. 修改配置文件
>cd/data/zookeeper-3.4.10/conf
>cp zoo_sample.cfgzoo.cfg
>sudo nanozoo.cfg
dataDir=/home/neworigin/zookeeper
server.1=s100:2888:3888
server.2=s101:2888:3888
server.3=s102:2888:3888
server.X=host:port1:port2的意思, X 表示当前 host 所运行的服务的 zookeeper 服务的 id(在接下来填写 myid 时需要用到), port1 表示
zookeeper 中的 follower 连接到leader 的端口号, port2 表示leadership 时所用的端口号。
7.发送配置文件
>xsync zoo.cfg
8. 创建文件夹
>xcall mkdir -p/home/neworigin/zookeeper
>cd/home/neworigin/zookeeper
[s100]
>echo 1 > myid
[s101]
echo 2 > myid
[s102]
echo 3 > myid
文件内容为 zoo.cfg 中 master 所对应的 server.X。
9. 启动集群(s100、s101、s102一起启动zk,尽量避免启动时间差距大)
>zkServer.shstart
>xcall jps查看进程
------------s100-----------------
3862 QuorumPeerMain
4351 Jps
------------s101-----------------
3650 QuorumPeerMain
3980 Jps
------------s102-----------------
3961 Jps
3791 QuorumPeerMain
------------s103-----------------
3883 Jps
[查看状态 leader or follower]
>zkServer.shstatus
[暂停]
>zkServer.sh stop
a.配置[conf/hbase-site.xml]
#hbase conf
<property>
<name>hbase.rootdir</name>
<value>hdfs://s100:8020/hbase</value>
</property>
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
#zk conf
<property>
<name>hbase.zookeeper.property.clientPort</name>
<value>2181</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>s100,s101, s102</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/home/neworigin/hbase/zk</value>
</property>
完全分布式模式的还需要修改conf/regionservers. 在 “regionservers” 列出了你希望运行的全部 HRegionServer,一行写一个host (就像Hadoop里面的 slaves 一样).列在这里的server会随着集群的启动而启动,集群的停止而停止.
[conf/regionservers]
s100
s101
s102
2.修改hbase/conf/hbase-env.sh配置脚本
[conf/hbase-env.sh]
export HBASE_MANAGES_ZK=false
4.分发配置
[hbase安装配置目录]
$>-- 删除/hbase/docs
$>xsync/data/hbase-1.2.5
5.重启客户机
启动hbase集群,一定要在master主机启动s100
注意:启动hbase之前,必须保证hadoop集群和zookeeper集群是可用的。
$>start-hbase.sh
6.查看进程
$>xcall jps
hmaster //master
hregionserver //regionserver
hquorumpeer //zk
7.查看webui
http://hmaster:16010/ //masterwebui
http://hmaster:16030/ //regionserverwebui
8.HBase停止集群报错,pid: No such file or directory
在hbase-env.sh中修改pid文件的存放路径
export HBASE_PID_DIR=/home/neworigin/hbase/pids
1.4. HBase Shell
HBase包含可以与HBase进行通信的Shell。 HBase使用Hadoop文件系统来存储数据。它拥有一个主服务器和区域服务器。数据存储将在区域(表)的形式。这些区域被分割并存储在区域服务器。
主服务器管理这些区域服务器,所有这些任务发生在HDFS。下面给出的是一些由HBase Shell支持的命令。
1.5. 通用命令
status: 提供HBase的状态,例如,服务器的数量。
version: 提供正在使用HBase版本。
table_help: 表引用命令提供帮助。
whoami: 提供有关用户的信息。
·
1.6. 数据定义语言
这些是关于HBase在表中操作的命令。
· create: 创建一个表。
· list: 列出HBase的所有表。
· disable: 禁用表。
· is_disabled: 验证表是否被禁用。
· enable: 启用一个表。
· is_enabled: 验证表是否已启用。
· describe: 提供了一个表的描述。
· alter: 改变一个表。
· exists: 验证表是否存在。
· drop: 从HBase中删除表。
· drop_all: 丢弃在命令中给出匹配“regex”的表。
· Java Admin API: 在此之前所有的上述命令,Java提供了一个通过API编程来管理实现DDL功能。在这个org.apache.hadoop.hbase.client包中有HBaseAdmin和HTableDescriptor 这两个重要的类提供DDL功能。
1.7. 数据操纵语言
· put: 把指定列在指定的行中单元格的值在一个特定的表。
· get: 取行或单元格的内容。
· delete: 删除表中的单元格值。
· deleteall: 删除给定行的所有单元格。
· scan: 扫描并返回表数据。
· count: 计数并返回表中的行的数目。
· truncate: 禁用,删除和重新创建一个指定的表。
· Java client API: 在此之前所有上述命令,Java提供了一个客户端API来实现DML功能,CRUD(创建检索更新删除)操作更多的是通过编程,在org.apache.hadoop.hbase.client包下。 在此包HTable 的 Put和Get是重要的类。
1.8. 启动HBase Shell
要访问HBase shell,必须导航进入到HBase的主文件夹。
cd /usr/localhost/
cd Hbase
可以使用“hbase shell”命令来启动HBase的交互shell,如下图所示。
./bin/hbase shell
如果已成功在系统中安装HBase,那么它会给出 HBase shell 提示符,如下图所示。
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version 0.94.23, rf42302b28aceaab773b15f234aa8718fff7eea3c, Wed Aug 27
00:54:09 UTC 2014
hbase(main):001:0>
要 退出交互shell命令,在任何时候键入 exit 或使用<Ctrl + C>。进一步处理检查shell功能之前,使用 list 命令用于列出所有可用命令。list是用来获取所有HBase 表的列表。首先,验证安装HBase在系统中使用如下所示。
hbase(main):001:0> list
当输入这个命令,它给出下面的输出。
hbase(main):001:0> list
TABLE
2. HBase常用命令
HBase常用命令status,version, table_help和whoami。本章将介绍了这些命令。
2.1. status
命令返回包括在系统上运行的服务器的细节和系统的状态。它的语法如下:
hbase(main):009:0> status
如果执行这个命令,它会返回下面的输出
hbase(main):009:0> status
3 servers, 0 dead, 1.3333 average load
2.2. version
该命令返回HBase系统使用的版本。它的语法如下:
hbase(main):010:0> version
如果执行这个命令,它会返回下面的输出。
hbase(main):009:0> version
0.98.8-hadoop2, r6cfc8d064754251365e070a10a82eb169956d5fe, Fri Nov 14
18:26:29 PST 2014
2.3. table_help
此命令将引导如何使用表引用的命令。下面给出的是使用这个命令的语法。
hbase(main):02:0> table_help
当使用此命令时,它显示帮助主题表相关的命令。下面给出是此命令的部分输出。
hbase(main):002:0> table_help
Help for table-reference commands.
You can either create a table via 'create' and then manipulate the table
via commands like 'put', 'get', etc.
See the standard help information for how to use each of these commands.
However, as of 0.96, you can also get a reference to a table, on which
you can invoke commands.
For instance, you can get create a table and keep around a reference to
it via:
hbase> t = create 't', 'cf'
…...
2.4. whoami
该命令返回HBase用户详细信息。如果执行这个命令,返回当前HBase用户,如下图所示
hbase(main):008:0> whoami
hadoop (auth:SIMPLE)
groups: hadoop
3. HBase创建表
可以使用命令创建一个表,在这里必须指定表名和列族名。在HBase shell中创建表的语法如下所示。
create
‘<table name>’,’<column family>’
3.1.1.示例
下面给出的是一个表名为emp的样本模式。它有两个列族:“personal data”和“professional data”。
Row key | personal data | professional data |
|
|
|
|
|
|
在HBase shell创建该表如下所示。
hbase(main):002:0> create 'emp', 'personal data',
’professional data’
它会给下面的输出。
0 row(s) in 1.1300 seconds
=> Hbase::Table - emp
3.2. 验证创建
可以验证是否已经创建,使用 list 命令如下所示。在这里,可以看到创建的emp表。
hbase(main):002:0> list
TABLE
emp
2 row(s) in 0.0340 seconds
4. HBase列出表
list 是用来列出HBase中所有表的命令。下面给出了 list 命令的语法。
hbase(main):001:0 > list
当输入这个命令,并在HBase提示符下执行,它会显示HBase中的所有表的列表,如下图所示。
hbase(main):001:0> list
TABLE
emp
在这里,可以看到一个名为表emp。
HBase禁用表
要删除表或改变其设置,首先需要使用 disable 命令关闭表。使用 enable 命令,可以重新启用它。
下面给出的语法是用来禁用一个表:
disable
‘emp’
下面给出的是一个例子,说明如何禁用表。
hbase(main):025:0> disable 'emp'
0 row(s) in 1.2760 seconds
4.1. 验证
禁用表之后,仍然可以通过 list 和exists命令查看到。无法扫描到它存在,它会给下面的错误。
hbase(main):028:0> scan 'emp'
ROW COLUMN+CELL
ERROR: emp is disabled.
4.2. is_disabled
这个命令是用来查看表是否被禁用。它的语法如下。
hbase> is_disabled 'table name'
下面的例子验证表名为emp是否被禁用。如果禁用,它会返回true,如果没有,它会返回false。
hbase(main):031:0> is_disabled 'emp'
true
0 row(s) in 0.0440 seconds
4.3. disable_all
此命令用于禁用所有匹配给定正则表达式的表。disable_all命令的语法如下。
hbase> disable_all 'r.*'
假设有5个表在HBase,即raja, rajani, rajendra, rajesh 和 raju。下面的代码将禁用所有以 raj 开始的表。
hbase(main):002:0> disable_all 'raj.*'
raja
rajani
rajendra
rajesh
raju
Disable the above 5 tables (y/n)?
y
5 tables successfully disabled
5. HBase启用表
启用表的语法:
enable
‘emp’
给出下面是一个例子,使一个表启用。
hbase(main):005:0> enable 'emp'
0 row(s) in 0.4580 seconds
5.1. 验证
启用表之后,扫描。如果能看到的模式,那么证明表已成功启用。
hbase(main):006:0> scan 'emp'
ROW COLUMN+CELL
1 column=personal data:city, timestamp=1417516501, value=hyderabad
1 column=personal data:name, timestamp=1417525058, value=ramu
1 column=professional data:designation, timestamp=1417532601, value=manager
1 column=professional data:salary, timestamp=1417524244109, value=50000
2 column=personal data:city, timestamp=1417524574905, value=chennai
2 column=personal data:name, timestamp=1417524556125, value=ravi
2 column=professional data:designation, timestamp=14175292204, value=sr:engg
2 column=professional data:salary, timestamp=1417524604221, value=30000
3 column=personal data:city, timestamp=1417524681780, value=delhi
3 column=personal data:name, timestamp=1417524672067, value=rajesh
3 column=professional data:designation, timestamp=14175246987, value=jr:engg
3 column=professional data:salary, timestamp=1417524702514, value=25000
3 row(s) in 0.0400 seconds
5.2. is_enabled
此命令用于查找表是否被启用。它的语法如下:
hbase> is_enabled 'table name'
下面的代码验证表emp是否启用。如果启用,它将返回true,如果没有,它会返回false。
hbase(main):031:0> is_enabled 'emp'
true
0 row(s) in 0.0440 seconds
6. HBase表描述和修改
6.1. 描述
该命令返回表的说明。它的语法如下:
hbase> describe 'table name'
下面给出的是对emp表的 describe 命令的输出。
hbase(main):006:0> describe 'emp'
DESCRIPTION
ENABLED
'emp', {NAME => 'READONLY', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER
=> 'ROW', REPLICATION_SCOPE => '0', COMPRESSION => 'NONE', VERSIONS =>
'1', TTL true
=> 'FOREVER', MIN_VERSIONS => '0', KEEP_DELETED_CELLS => 'false',
BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME
=> 'personal
data', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW',
REPLICATION_SCOPE => '0', VERSIONS => '5', COMPRESSION => 'NONE',
MIN_VERSIONS => '0', TTL
=> 'FOREVER', KEEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536',
IN_MEMORY => 'false', BLOCKCACHE => 'true'}, {NAME => 'professional
data', DATA_BLO
CK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0',
VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL =>
'FOREVER', K
EEP_DELETED_CELLS => 'false', BLOCKSIZE => '65536', IN_MEMORY =>
'false', BLOCKCACHE => 'true'}, {NAME => 'table_att_unset',
DATA_BLOCK_ENCODING => 'NO
NE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', COMPRESSION =>
'NONE', VERSIONS => '1', TTL => 'FOREVER', MIN_VERSIONS => '0',
KEEP_DELETED_CELLS
=> 'false', BLOCKSIZE => '6
6.2. 修改
alter用于更改现有表的命令。使用此命令可以更改列族的单元,设定最大数量和删除表范围运算符,并从表中删除列家族。
6.2.1.更改列族单元格的最大数目
下面给出的语法来改变列家族单元的最大数目。
hbase> alter 't1', NAME => 'f1', VERSIONS => 5
在下面的例子中,单元的最大数目设置为5。
hbase(main):003:0> alter 'emp', NAME => 'personal data', VERSIONS => 5
Updating all regions with the new schema...
0/1 regions updated.
1/1 regions updated.
Done.
0 row(s) in 2.3050 seconds
6.3. 表范围运算符
使用alter,可以设置和删除表范围,运算符,如MAX_FILESIZE,READONLY,MEMSTORE_FLUSHSIZE,DEFERRED_LOG_FLUSH等。
6.3.1.设置只读
下面给出的是语法,是用以设置表为只读。
hbase>alter 't1', READONLY(option)
在下面的例子中,我们已经设置表emp为只读。
hbase(main):006:0> alter 'emp', READONLY
Updating all regions with the new schema...
0/1 regions updated.
1/1 regions updated.
Done.
0 row(s) in 2.2140 seconds
6.3.2.删除表范围运算符
也可以删除表范围运算。下面给出的是语法,从emp表中删除“MAX_FILESIZE”。
hbase> alter 't1', METHOD => 'table_att_unset', NAME => 'MAX_FILESIZE'
6.3.3.删除列族
使用alter,也可以删除列族。下面给出的是使用alter删除列族的语法。
hbase> alter
‘ table name ’, ‘delete’ => ‘ column family ’
下面给出的是一个例子,从“emp”表中删除列族。
假设在HBase中有一个employee表。它包含以下数据:
hbase(main):006:0> scan 'employee'
ROW COLUMN+CELL
row1 column=personal:city, timestamp=1418193767, value=hyderabad
row1 column=personal:name, timestamp=1418193806767, value=raju
row1 column=professional:designation, timestamp=1418193767, value=manager
row1 column=professional:salary, timestamp=1418193806767, value=50000
1 row(s) in 0.0160 seconds
现在使用alter命令删除指定的 professional 列族。
hbase(main):007:0> alter 'employee','delete'=>'professional'
Updating all regions with the new schema...
0/1 regions updated.
1/1 regions updated.
Done.
0 row(s) in 2.2380 seconds
现在验证该表中变更后的数据。观察列族“professional”也没有了,因为前面已经被删除了。
hbase(main):003:0> scan 'employee'
ROW COLUMN+CELL
row1 column=personal:city, timestamp=14181936767, value=hyderabad
row1 column=personal:name, timestamp=1418193806767, value=raju
1 row(s) in 0.0830 seconds
HBase Exists
可以使用exists命令验证表的存在。下面的示例演示了如何使用这个命令。
hbase(main):024:0> exists 'emp'
Table emp does exist
0 row(s) in 0.0750 seconds
==================================================================
hbase(main):015:0> exists 'student'
Table student does not exist
0 row(s) in 0.0480 seconds
7. HBase删除表
用drop命令可以删除表。在删除一个表之前必须先将其禁用。
hbase(main):018:0> disable 'emp'
0 row(s) in 1.4580 seconds
hbase(main):019:0> drop 'emp'
0 row(s) in 0.3060 seconds
使用exists 命令验证表是否被删除。
hbase(main):020:0> exists 'emp'
Table emp does not exist
0 row(s) in 0.0730 seconds
7.1. drop_all
这个命令是用来在给出删除匹配“regex”表。它的语法如下:
hbase> drop_all
‘t.*’
注意:要删除表,则必须先将其禁用。
7.1.1.示例
假设有一些表的名称为raja, rajani,rajendra, rajesh, 和 raju。
hbase(main):017:0> list
TABLE
raja
rajani
rajendra
rajesh
raju
9 row(s) in 0.0270 seconds
所有这些表以字母raj开始。首先使用disable_all命令禁用所有这些表如下所示。
hbase(main):002:0> disable_all 'raj.*'
raja
rajani
rajendra
rajesh
raju
Disable the above 5 tables (y/n)?
y
5 tables successfully disabled
现在,可以使用 drop_all 命令删除它们,如下所示。
hbase(main):018:0> drop_all 'raj.*'
raja
rajani
rajendra
rajesh
raju
Drop the above 5 tables (y/n)?
y
5 tables successfully dropped
8. HBase关闭
8.1. exit
可以通过键入exit命令退出shell。
hbase(main):021:0> exit
8.2. 停止HBase
要停止HBase,浏览进入到HBase主文件夹,然后键入以下命令。
./bin/stop-hbase.sh
9. HBase创建数据
在HBase表中创建的数据,可以下面的命令和方法:
· put 命令,
· add() - Put类的方法
· put() - HTable 类的方法.
作为一个例子,我们将在HBase中创建下表。
使用put命令,可以插入行到一个表。它的语法如下:
put
’<table name>’,’row1’,’<colfamily:colname>’,’<value>’
9.1.1.插入第一行
将第一行的值插入到emp表如下所示。
hbase(main):005:0> put 'emp','1','personal data:name','raju'
0 row(s) in 0.6600 seconds
hbase(main):006:0> put 'emp','1','personal data:city','hyderabad'
0 row(s) in 0.0410 seconds
hbase(main):007:0> put 'emp','1','professional
data:designation','manager'
0 row(s) in 0.0240 seconds
hbase(main):007:0> put 'emp','1','professional data:salary','50000'
0 row(s) in 0.0240 seconds
以相同的方式使用put命令插入剩余的行。如果插入完成整个表格,会得到下面的输出。
hbase(main):022:0> scan 'emp'
ROW COLUMN+CELL
1 column=personal data:city, timestamp=1417524216501, value=hyderabad
1 column=personal data:name, timestamp=1417524185058, value=ramu
1 column=professional data:designation, timestamp=1417524232601,
value=manager
1 column=professional data:salary, timestamp=1417524244109, value=50000
2 column=personal data:city, timestamp=1417524574905, value=chennai
2 column=personal data:name, timestamp=1417524556125, value=ravi
2 column=professional data:designation, timestamp=1417524592204,
value=sr:engg
2 column=professional data:salary, timestamp=1417524604221, value=30000
3 column=personal data:city, timestamp=1417524681780, value=delhi
3 column=personal data:name, timestamp=1417524672067, value=rajesh
3 column=professional data:designation, timestamp=1417524693187,
value=jr:engg
3 column=professional data:salary, timestamp=1417524702514,
value=25000
10. HBase更新数据
可以使用put命令更新现有的单元格值。按照下面的语法,并注明新值,如下图所示。
put
‘table name’,’row ’,'Column family:column name',’new value’
新给定值替换现有的值,并更新该行。
10.1.1. 示例
假设HBase中有一个表emp拥有下列数据
hbase(main):003:0> scan 'emp'
ROW COLUMN+CELL
row1 column=personal:name, timestamp=1418051555, value=raju
row1 column=personal:city, timestamp=1418275907, value=Hyderabad
row1 column=professional:designation, timestamp=14180555,value=manager
row1 column=professional:salary, timestamp=1418035791555,value=50000
1 row(s) in 0.0100 seconds
以下命令将更新名为“Raju'员工的城市值为'Delhi'。
hbase(main):002:0> put 'emp','row1','personal:city','Delhi'
0 row(s) in 0.0400 seconds
更新后的表如下所示,观察这个城市Raju的值已更改为“Delhi”。
hbase(main):003:0> scan 'emp'
ROW COLUMN+CELL
row1 column=personal:name, timestamp=1418035791555, value=raju
row1 column=personal:city, timestamp=1418274645907, value=Delhi
row1 column=professional:designation, timestamp=141857555,value=manager
row1 column=professional:salary, timestamp=1418039555, value=50000
1 row(s) in 0.0100 seconds
11. HBase读取数据
get命令和HTable类的get()方法用于从HBase表中读取数据。使用 get 命令,可以同时获取一行数据。它的语法如下:
get
’<table name>’,’row1’
下面的例子说明如何使用get命令。扫描emp表的第一行。
hbase(main):012:0> get 'emp', '1'
COLUMN CELL
personal : city timestamp=1417521848375, value=hyderabad
personal : name timestamp=1417521785385, value=ramu
professional: designation timestamp=1417521885277, value=manager
professional: salary timestamp=1417521903862, value=50000
4 row(s) in 0.0270 seconds
11.1. 读取指定列
下面给出的是语法,使用get方法读取指定列。
hbase>get 'table name',
‘rowid’, {COLUMN => ‘column family:column name ’}
下面给出的示例,是用于读取HBase表中的特定列。
hbase(main):015:0> get 'emp', 'row1', {COLUMN=>'personal:name'}
COLUMN CELL
personal:name timestamp=1418035791555, value=raju
1 row(s) in 0.0080 seconds
12. HBase删除数据
12.1. 从表删除特定单元格
使用 delete 命令,可以在一个表中删除特定单元格。 delete 命令的语法如下:
delete
‘<table name>’, ‘<row>’, ‘<column name >’, ‘<time stamp>’
下面是一个删除特定单元格和例子。在这里,我们删除salary
hbase(main):006:0> delete 'emp', '1', 'personal data:city',
1417521848375
0 row(s) in 0.0060 seconds
12.2. 删除表的所有单元格
使用“deleteall”命令,可以删除一行中所有单元格。下面给出是 deleteall 命令的语法。
deleteall
‘<table name>’, ‘<row>’,
这里是使用“deleteall”命令删去 emp 表 row1 的所有单元的一个例子。
hbase(main):007:0> deleteall 'emp','1'
0 row(s) in 0.0240 seconds
使用scan命令验证表。表被删除后的快照如下。
hbase(main):022:0> scan 'emp'
ROW COLUMN+CELL
2 column=personal data:city, timestamp=1417524574905, value=chennai
2 column=personal data:name, timestamp=1417524556125, value=ravi
2 column=professional data:designation, timestamp=1417524204, value=sr:engg
2 column=professional data:salary, timestamp=1417524604221, value=30000
3 column=personal data:city, timestamp=1417524681780, value=delhi
3 column=personal data:name, timestamp=1417524672067, value=rajesh
3 column=professional data:designation, timestamp=1417523187, value=jr:engg
3 column=professional data:salary, timestamp=1417524702514, value=25000
13. HBase扫描
scan 命令用于查看HTable数据。使用 scan 命令可以得到表中的数据。它的语法如下:
scan
‘<table name>’
下面的示例演示了如何使用scan命令从表中读取数据。在这里读取的是emp表。
hbase(main):010:0> scan 'emp'
ROW COLUMN+CELL
1 column=personal data:city, timestamp=1417521848375, value=hyderabad
1 column=personal data:name, timestamp=1417521785385, value=ramu
1 column=professional data:designation, timestamp=1417585277,value=manager
1 column=professional data:salary, timestamp=1417521903862, value=50000
1 row(s) in 0.0370 seconds
14. HBase计数和截断
14.1. count
可以使用count命令计算表的行数量。它的语法如下:
count
‘<table name>’
删除第一行后,表emp就只有两行。验证它,如下图所示。
hbase(main):023:0> count 'emp'
2 row(s) in 0.090 seconds
=> 2
14.2. truncate
此命令将禁止删除并重新创建一个表。truncate 的语法如下:
hbase> truncate 'table name'
下面给出是 truncate 命令的例子。在这里,我们已经截断了emp表。
hbase(main):011:0> truncate 'emp'
Truncating 'one' table (it may take a while):
- Disabling table...
- Truncating table...
0 row(s) in 1.5950 seconds
截断表之后,使用scan 命令来验证。会得到表的行数为零。
hbase(main):017:0> scan
‘emp’
ROW COLUMN+CELL
0 row(s) in 0.3110 seconds
15. HBase安全
我们可以授予和撤销HBase用户的权限。也有出于安全目的,三个命令:grant, revoke 和 user_permission.。
15.1. grant
grant命令授予特定的权限,如读,写,执行和管理表给定一个特定的用户。 grant命令的语法如下:
hbase> grant <user> <permissions> [<table> [<column family> [<column; qualifier>]]
我们可以从RWXCA组,其中给予零个或多个特权给用户
· R - 代表读取权限
· W - 代表写权限
· X - 代表执行权限
· C - 代表创建权限
· A - 代表管理权限
下面给出是为用户“Tutorialspoint'授予所有权限的例子。
hbase(main):018:0> grant 'Tutorialspoint', 'RWXCA'
15.2. revoke
revoke命令用于撤销用户访问表的权限。它的语法如下:
hbase> revoke <user>
下面的代码撤消名为“Tutorialspoint”用户的所有权限。
hbase(main):006:0> revoke 'Tutorialspoint'
15.3. user_permission
此命令用于列出特定表的所有权限。user_permission的语法如下:
hbase>user_permission
‘tablename’
下面的代码列出了“emp”表的所有用户权限。
hbase(main):013:0> user_permission 'emp'
ERROR:DISABLED: Security features are not available???
HBase API操作
HBase是用Java编写的,因此它提供JavaAPI和HBase通信。 Java API是与HBase通信的最快方法。下面给出的是引用Java API管理,涵盖用于管理表的任务。
15.4. HBaseAdmin类
HBaseAdmin是一个类表示管理。这个类属于org.apache.hadoop.hbase.client包。使用这个类,可以执行管理员任务。使用Connection.getAdmin()方法来获取管理员的实例。
方法及说明
S.No. | 方法及说明 |
1 | void createTable(HTableDescriptor desc) 创建一个新的表 |
2 | void createTable(HTableDescriptor desc, byte[][] splitKeys) 创建一个新表使用一组初始指定的分割键限定空区域 |
3 | void deleteColumn(byte[] tableName, String columnName) 从表中删除列 |
4 | void deleteColumn(String tableName, String columnName) 删除表中的列 |
5 | void deleteTable(String tableName) 删除表 |
15.5. Descriptor类
这个类包含一个HBase表,如详细信息:
· 所有列族的描述,
· 如果表是目录表,
· 如果表是只读的,
· 存储的最大尺寸,
· 当区域分割发生,
· 与之相关联的协同处理器等
15.5.1. 构造函数
S.No. | 构造函数和总结 |
1 | HTableDescriptor(TableName name) 构造一个表描述符指定TableName对象。 |
15.5.2. 方法及说明
S.No. | 方法及描述 |
1 | HTableDescriptor addFamily(HColumnDescriptor family) 将列家族给定的描述符 |
创建表
15.6. 使用JavaAPI创建一个表
可以使用HBaseAdmin类的createTable()方法创建表在HBase中。这个类属于org.apache.hadoop.hbase.client 包。下面给出的步骤是来使用JavaAPI创建表在HBase中。
15.6.1. 第1步:实例化HBaseAdmin
这个类需要配置对象作为参数,因此初始实例配置类传递此实例给HBaseAdmin。
Configuration conf = HBaseConfiguration.create();
HBaseAdmin admin = new HBaseAdmin(conf);
15.6.2. 第2步:创建TableDescriptor
HTableDescriptor类是属于org.apache.hadoop.hbase。这个类就像表名和列族的容器一样。
//creating table descriptor
HTableDescriptor table = new HTableDescriptor(toBytes("Table name"));
//creating column family descriptor
HColumnDescriptor family = new HColumnDescriptor(toBytes("column family"));
//adding coloumn family to HTable
table.addFamily(family);
15.6.3. 第3步:通过执行管理
使用HBaseAdmin类的createTable()方法,可以在管理模式执行创建的表。
admin.createTable(table);
下面给出的是完整的程序,通过管理员创建一个表。
import java.io.IOException;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.conf.Configuration;
public class CreateTable {
public static void main(String[] args) throws IOException {
// Instantiating configuration class
Configuration con = HBaseConfiguration.create();
// Instantiating HbaseAdmin class
HBaseAdmin admin = new HBaseAdmin(con);
// Instantiating table descriptor class
HTableDescriptor tableDescriptor = new
TableDescriptor(TableName.valueOf("emp"));
// Adding column families to table descriptor
tableDescriptor.addFamily(new HColumnDescriptor("personal"));
tableDescriptor.addFamily(new HColumnDescriptor("professional"));
// Execute the table through admin
admin.createTable(tableDescriptor);
System.out.println(" Table created ");
}
}
编译和执行上述程序如下所示。
$javac CreateTable.java
$java CreateTable
下面列出的是输出:
Table created
16. HBase列出表
16.1. 使用JavaAPI列出表
按照下面给出的步骤来使用Java API从HBase获得表的列表。
16.1.1. 第1步
在类HBaseAdmin中有一个方法叫 listTables(),列出HBase中所有的表的列表。这个方法返回HTableDescriptor对象的数组。
//creating a configuration object
Configuration conf = HBaseConfiguration.create();
//Creating HBaseAdmin object
HBaseAdmin admin = new HBaseAdmin(conf);
//Getting all the list of tables using HBaseAdmin object
HTableDescriptor[] tableDescriptor =admin.listTables();
16.1.2. 第1步
就可以得到使用HTableDescriptor类长度可变的HTableDescriptor[]数组的长度。从该对象使用getNameAsString()方法获得表的名称。运行'for'循环而获得HBase表的列表。
下面给出的是使用Java API程序列出所有HBase中表的列表。
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class ListTables {
public static void main(String args[])throws MasterNotRunningException, IOException{
// Instantiating a configuration class
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
// Getting all the list of tables using HBaseAdmin object
HTableDescriptor[] tableDescriptor =admin.listTables();
// printing all the table names.
for (int i=0; i<tableDescriptor.length;i++ ){
System.out.println(tableDescriptor[i].getNameAsString());
}
}
}
编译和执行上述程序如下所示。
$javac ListTables.java
$java ListTables
下面列出的是输出:
User
emp
17. HBase禁用表
17.1. 禁用表使用Java API
要验证一个表是否被禁用,使用isTableDisabled()方法和disableTable()方法禁用一个表。这些方法属于HBaseAdmin类。按照下面给出禁用表中的步骤。
17.1.1. 第1步
HBaseAdmin类的实例如下所示。
// Creating configuration object
Configuration conf = HBaseConfiguration.create();
// Creating HBaseAdmin object
HBaseAdmin admin = new HBaseAdmin(conf);
17.1.2. 第2步
使用isTableDisabled()方法验证表是否被禁用,如下图所示。
Boolean b = admin.isTableDisabled("emp");
17.1.3. 第3步
如果表未禁用,禁用它,如下图所示。
if(!b){
admin.disableTable("emp");
System.out.println("Table disabled");
}
下面给出的是完整的程序,以验证表是否被禁用;如果没有,那么如何禁用它?
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class DisableTable{
public static void main(String args[]) throws MasterNotRunningException, IOException{
// Instantiating configuration class
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
// Verifying weather the table is disabled
Boolean bool = admin.isTableDisabled("emp");
System.out.println(bool);
// Disabling the table using HBaseAdmin object
if(!bool){
admin.disableTable("emp");
System.out.println("Table disabled");
}
}
}
编译和执行上述程序如下所示。
$javac DisableTable.java
$java DsiableTable
下面列出的是输出:
false
Table disabled
18. HBase启用表
18.1. 使用JavaAPI启用表
要验证一个表是否被启用,使用isTableEnabled()方法;并且使用enableTable()方法使一个表启用。这些方法属于HBaseAdmin类。按照下面给出启用表的步骤。
18.1.1. 第1步
HBaseAdmin类的实例如下所示。
// Creating configuration object
Configuration conf = HBaseConfiguration.create();
// Creating HBaseAdmin object
HBaseAdmin admin = new HBaseAdmin(conf);
18.1.2. 第2步
使用isTableEnabled()方法验证表是否被启用,如下所示。
Boolean bool=admin.isTableEnabled("emp");
18.1.3. 第3步
如果表未禁用,那么禁用它,如下图所示
if(!bool){
admin.enableTable("emp");
System.out.println("Table enabled");
}
下面给出的是完整的程序,以验证表是否已启用,如果它不是,那么启用它。
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class EnableTable{
public static void main(String args[]) throws MasterNotRunningException, IOException{
// Instantiating configuration class
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
// Verifying weather the table is disabled
Boolean bool = admin.isTableEnabled("emp");
System.out.println(bool);
// Disabling the table using HBaseAdmin object
if(!bool){
admin.enableTable("emp");
System.out.println("Table Enabled");
}
}
}
编译和执行上述程序如下所示。
$javac EnableTable.java
$java EnableTable
下面列出的是输出:
false
Table Enabled
19. HBase表描述和修改
19.1. 使用JavaAPI添加一列族
可以使用HBAseAdmin类的addColumn方法添加一列家族的表。按照下面给出的步骤将一个列族添加到表中。
19.1.1. 第1步
实例化HBaseAdmin类。
// Instantiating configuration object
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
19.1.2. 第2步
addColumn() 方法需要一个表名和一个HColumnDescriptorclass对象。因此需要实例化HColumnDescriptor类。 HColumnDescriptor依次构造函数需要一个列族名称用于添加。在这里加入了一个名为“contactDetails”到 “employee”表的列族。
// Instantiating columnDescriptor object
HColumnDescriptor columnDescriptor = new
HColumnDescriptor("contactDetails");
19.1.3. 第3步
使用addColumn方法添加列族。通过表名和HColumnDescriptor类对象作为这个方法的参数。
// Adding column family
admin.addColumn("employee", new HColumnDescriptor("columnDescriptor"));
下面给出的是一个完整的程序,用于添加一列族到现有的表。
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class AddColoumn{
public static void main(String args[]) throws MasterNotRunningException, IOException{
// Instantiating configuration class.
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class.
HBaseAdmin admin = new HBaseAdmin(conf);
// Instantiating columnDescriptor class
HColumnDescriptor columnDescriptor = new HColumnDescriptor("contactDetails");
// Adding column family
admin.addColumn("employee", columnDescriptor);
System.out.println("coloumn added");
}
}
编译和执行上述程序,如下所示
$javac AddColumn.java
$java AddColumn
上述编译只有已经设置“.bashrc”中的类路径。如果还没有,请按照下面编译给出.java文件的程序。
//if "/home/home/hadoop/hbase " is your Hbase home folder then.
$javac -cp /home/hadoop/hbase/lib/*: Demo.java
如果一切顺利,它会生成以下的输出:
column added
19.2. 使用JavaAPI删除列族
可以使用HBAseAdmin类的deleteColumn()方法删除列族。按照下面给出的步骤添加一个列族到表中。
19.2.1. 第1步
实例化HBaseAdmin类。
// Instantiating configuration object
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
19.2.2. 第2步
使用deleteColumn()方法添加列族。传递表名和列族名作为这个方法的参数。
// Deleting column family
admin.deleteColumn("employee", "contactDetails");
下面给出的是从现有表中删除列族的完整的程序。
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class DeleteColoumn{
public static void main(String args[]) throws MasterNotRunningException, IOException{
// Instantiating configuration class.
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class.
HBaseAdmin admin = new HBaseAdmin(conf);
// Deleting a column family
admin.deleteColumn("employee","contactDetails");
System.out.println("coloumn deleted");
}
}
编译和执行上述程序如下所示。
$javac DeleteColumn.java
$java DeleteColumn
下面列出的是输出:
column deleted
20. HBase Exists
20.1. 使用JavaAPI验证表的存在
可以使用HBaseAdmin类的tableExists()方法验证表在HBase中是否存在。按照下面给出的步骤验证HBase表存在。
20.1.1. 第1步
Instantiate the HBaseAdimn class
// Instantiating configuration object
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
20.1.2. 第2步
使用tableExists()方法来验证表的存在。
下面给出的是使用java程序中的JavaAPI来测试一个HBase表的存在。
import java.io.IOException;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class TableExists{
public static void main(String args[])throws IOException{
// Instantiating configuration class
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
// Verifying the existance of the table
boolean bool = admin.tableExists("emp");
System.out.println( bool);
}
}
编译和执行上述程序如下所示。
$javac TableExists.java
$java TableExists
下面列出的是输出:
true
21. HBase删除表
21.1. 使用JavaAPI删除表
可以使用 HBaseAdmin 类的deleteTable()方法删除表。按照下面给出是使用Java API来删除表中的步骤。
21.1.1. 第1步
实例化HBaseAdmin类。
// creating a configuration object
Configuration conf = HBaseConfiguration.create();
// Creating HBaseAdmin object
HBaseAdmin admin = new HBaseAdmin(conf);
21.1.2. 第2步
使用HBaseAdmin类的disableTable()方法禁止表。
admin.disableTable("emp1");
21.1.3. 第3步
现在使用HBaseAdmin类的deleteTable()方法删除表。
admin.deleteTable("emp12");
下面给出的是完整的Java程序用于删除HBase表。
import java.io.IOException;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class DeleteTable {
public static void main(String[] args) throws IOException {
// Instantiating configuration class
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
// disabling table named emp
admin.disableTable("emp12");
// Deleting emp
admin.deleteTable("emp12");
System.out.println("Table deleted");
}
}
编译和执行上述程序如下所示。
$javac DeleteTable.java
$java DeleteTable
下面是输出结果:
Table deleted
Hbase关闭
21.2. 使用JavaAPI停止HBase
可以使用HBaseAdmin类的shutdown()方法关闭HBase。按照下面给出关闭HBase的步骤:
21.2.1. 第1步
实例化HbaseAdmin类。
// Instantiating configuration object
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin object
HBaseAdmin admin = new HBaseAdmin(conf);
21.2.2. 第2步
使用HBaseAdmin类的shutdown()方法关闭HBase。
admin.shutdown();
下面给出的是停止HBase的程序。
import java.io.IOException;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class ShutDownHbase{
public static void main(String args[])throws IOException {
// Instantiating configuration class
Configuration conf = HBaseConfiguration.create();
// Instantiating HBaseAdmin class
HBaseAdmin admin = new HBaseAdmin(conf);
// Shutting down HBase
System.out.println("Shutting down hbase");
admin.shutdown();
}
}
编译和执行上述程序如下所示。
$javac ShutDownHbase.java
$java ShutDownHbase
下面是输出结果:
Shutting down hbase
22. HBase客户端API
介绍用于对HBase表上执行CRUD操作的HBase Java客户端API。 HBase是用Java编写的,并具有Java原生API。因此,它提供了编程访问数据操纵语言(DML)。
22.1. HBaseConfiguration类
添加 HBase 的配置到配置文件。这个类属于org.apache.hadoop.hbase包。
22.1.1. 方法及说明
S.No. | 方法及说明 |
1 | static org.apache.hadoop.conf.Configuration create() 此方法创建使用HBase的资源配置 |
22.2. HTable类
HTable表示HBase表中HBase的内部类。它用于实现单个HBase表进行通信。这个类属于org.apache.hadoop.hbase.client类。
22.2.1. 构造函数
S.No. | 构造函数 |
1 | HTable() |
2 | HTable(TableName tableName, ClusterConnection connection, ExecutorService pool) 使用此构造方法,可以创建一个对象来访问HBase表。 |
22.2.2. 方法及说明
S.No. | 构造函数 |
1 | void close() 释放HTable的所有资源 |
2 | void delete(Delete delete) 删除指定的单元格/行 |
3 | boolean exists(Get get) 使用这个方法,可以测试列的存在,在表中,由Get指定获取。 |
4 | Result get(Get get) 检索来自一个给定的行某些单元格。 |
5 | org.apache.hadoop.conf.Configuration getConfiguration() 返回此实例的配置对象。 |
6 | TableName getName() 返回此表的表名称实例。 |
7 | HTableDescriptor getTableDescriptor() 返回此表的表描述符。 |
8 | byte[] getTableName() 返回此表的名称。 |
9 | void put(Put put) 使用此方法,可以将数据插入到表中。 |
22.3. Put类
此类用于为单个行执行PUT操作。它属于org.apache.hadoop.hbase.client包。
22.3.1. 构造函数
S.No. | 构造函数和描述 |
1 | Put(byte[] row) 使用此构造方法,可以创建一个将操作指定行。 |
2 | Put(byte[] rowArray, int rowOffset, int rowLength) 使用此构造方法,可以使传入的行键的副本,以保持到本地。 |
3 | Put(byte[] rowArray, int rowOffset, int rowLength, long ts) 使用此构造方法,可以使传入的行键的副本,以保持到本地。 |
4 | Put(byte[] row, long ts) 使用此构造方法,我们可以创建一个Put操作指定行,用一个给定的时间戳。 |
22.3.2. 方法
S.No. | 方法及描述 |
1 | Put add(byte[] family, byte[] qualifier, byte[] value) 添加指定的列和值到 Put 操作。 |
2 | Put add(byte[] family, byte[] qualifier, long ts, byte[] value) 添加指定的列和值,使用指定的时间戳作为其版本到Put操作。 |
3 | Put add(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value) 添加指定的列和值,使用指定的时间戳作为其版本到Put操作。 |
4 | Put add(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value) 添加指定的列和值,使用指定的时间戳作为其版本到Put操作。 |
22.4. Get类
此类用于对单行执行get操作。这个类属于org.apache.hadoop.hbase.client包。
22.4.1. 构造函数
S.No. | 构造函数和描述 |
1 | Get(byte[] row) 使用此构造方法,可以为指定行创建一个Get操作。 |
2 | Get(Get get) |
22.4.2. 方法
S.No. | 构造函数和描述 |
1 | Get addColumn(byte[] family, byte[] qualifier) 检索来自特定列家族使用指定限定符 |
2 | Get addFamily(byte[] family) 检索从指定系列中的所有列。 |
22.5. Delete类
这个类用于对单行执行删除操作。要删除整行,实例化一个Delete对象用于删除行。这个类属于org.apache.hadoop.hbase.client包。
22.5.1. 构造函数
S.No. | 构造方法和描述 |
1 | Delete(byte[] row) 创建一个指定行的Delete操作。 |
2 | Delete(byte[] rowArray, int rowOffset, int rowLength) 创建一个指定行和时间戳的Delete操作。 |
3 | Delete(byte[] rowArray, int rowOffset, int rowLength, long ts) 创建一个指定行和时间戳的Delete操作。 |
4 | Delete(byte[] row, long timestamp) 创建一个指定行和时间戳的Delete操作。 |
22.5.2. 方法
S.No. | 构造方法和描述 |
1 | Delete addColumn(byte[] family, byte[] qualifier) 删除指定列的最新版本。 |
2 | Delete addColumns(byte[] family, byte[] qualifier, long timestamp) 删除所有版本具有时间戳小于或等于指定的时间戳的指定列。 |
3 | Delete addFamily(byte[] family) 删除指定的所有列族的所有版本。 |
4 | Delete addFamily(byte[] family, long timestamp) 删除指定列具有时间戳小于或等于指定的时间戳的列族。 |
22.6. Result类
这个类是用来获取Get或扫描查询的单行结果。
22.6.1. 构造函数
S.No. | 构造函数 |
1 | Result() 使用此构造方法,可以创建无Key Value的有效负载空的结果;如果调用Cells()返回null。 |
22.6.2. 方法
S.No. | 方法及描述 |
1 | byte[] getValue(byte[] family, byte[] qualifier) 此方法用于获取指定列的最新版本 |
2 | byte[] getRow() 此方法用于检索对应于从结果中创建行的行键。 |
23. HBase创建数据
23.1. 使用JavaAPI插入数据
可以使用Put 类的add()方法将数据插入到HBase。可以使用HTable类的put()方法保存数据。这些类属于org.apache.hadoop.hbase.client包。下面给出的步骤是在一个HBase表创建数据。
23.1.1. 第1步:实例化配置类
Configuration类增加了 HBase 配置文件到它的对象。使用HbaseConfiguration类的create()方法,如下图所示的配置对象。
Configuration conf = HbaseConfiguration.create();
23.1.2. 第2步:实例化HTable类
有一类名为HTable,在HBase中实现了Table。这个类用于单个HBase表进行通信。在这个类实例接受配置对象和表名作为参数。可以实例HTable类,如下图所示。
HTable hTable = new HTable(conf, tableName);
23.1.3. 第3步:实例化Put类
为了将数据插入到HBase表中,需要使用add()方法和变体。这种方法属于Put类,因此实例化Put类。这个类必须要以字符串格式的列名插入数据。可以实例Put类,如下图所示。
Put p = new Put(Bytes.toBytes("row1"));
23.1.4. 第4步:插入数据
Put类的add()方法用于插入数据。它需要代表列族,分别为:列限定符(列名称)3字节阵列,以及要插入的值。使用add()方法将数据插入HBase表如下图所示。
p.add(Bytes.toBytes("coloumn family "), Bytes.toBytes("column
name"),Bytes.toBytes("value"));
23.1.5. 第5步:保存数据到表中
插入所需的行后,HTable类put实例的put()方法添加,如下所示保存更改。
hTable.put(p);
23.1.6. 第6步:关闭HTable实例
创建在HBase的表数据之后,使用close()方法,如下所示关闭HTable实例。
hTable.close();
下面给出的是在HBase的表创建数据的完整程序。
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
public class InsertData{
public static void main(String[] args) throws IOException {
// Instantiating Configuration class
Configuration config = HBaseConfiguration.create();
// Instantiating HTable class
HTable hTable = new HTable(config, "emp");
// Instantiating Put class
// accepts a row name.
Put p = new Put(Bytes.toBytes("row1"));
// adding values using add() method
// accepts column family name, qualifier/row name ,value
p.add(Bytes.toBytes("personal"),
Bytes.toBytes("name"),Bytes.toBytes("raju"));
p.add(Bytes.toBytes("personal"),
Bytes.toBytes("city"),Bytes.toBytes("hyderabad"));
p.add(Bytes.toBytes("professional"),Bytes.toBytes("designation"),
Bytes.toBytes("manager"));
p.add(Bytes.toBytes("professional"),Bytes.toBytes("salary"),
Bytes.toBytes("50000"));
// Saving the put Instance to the HTable.
hTable.put(p);
System.out.println("data inserted");
// closing HTable
hTable.close();
}
}
编译和执行上述程序如下所示。
$javac InsertData.java
$java InsertData
下面列出的是输出结果:
data inserted
24. HBase更新数据
24.1. 使用JavaAPI更新数据
使用put()方法将特定单元格更新数据。按照下面给出更新表的现有单元格值的步骤。
24.1.1. 第1步:实例化Configuration类
Configuration类增加了HBase的配置文件到它的对象。使用HbaseConfiguration类的create()方法,如下图所示的配置对象。
Configuration conf = HbaseConfiguration.create();
24.1.2. 第2步:实例化HTable类
有一类叫HTable,实现在HBase中的Table类。此类用于单个HBase的表进行通信。在这个类实例,它接受配置对象和表名作为参数。实例化HTable类,如下图所示。
HTable hTable = new HTable(conf, tableName);
24.1.3. 第3步:实例化Put类
要将数据插入到HBase表中,使用add()方法和它的变体。这种方法属于Put类,因此实例化Put类。这个类必须以字符串格式的列名插入数据。可以实例化Put类,如下图所示。
Put p = new Put(Bytes.toBytes("row1"));
24.1.4. 第4步:更新现有的单元格
Put 类的add()方法用于插入数据。它需要表示列族,列限定符(列名称)3字节阵列,并要插入的值。将数据插入HBase表使用add()方法,如下图所示。
p.add(Bytes.toBytes("coloumn family "), Bytes.toBytes("column
name"),Bytes.toBytes("value"));
p.add(Bytes.toBytes("personal"),
Bytes.toBytes("city"),Bytes.toBytes("Delih"));
24.1.5. 第5步:保存表数据
插入所需的行后,HTable类实例的put()方法添加如下所示保存更改。
hTable.put(p);
24.1.6. 第6步:关闭HTable实例
创建在HBase的表数据之后,使用close()方法,如下所示关闭HTable实例。
hTable.close();
下面给出的是完整的程序,在一个特定的表更新数据。
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
public class UpdateData{
public static void main(String[] args) throws IOException {
// Instantiating Configuration class
Configuration config = HBaseConfiguration.create();
// Instantiating HTable class
HTable hTable = new HTable(config, "emp");
// Instantiating Put class
//accepts a row name
Put p = new Put(Bytes.toBytes("row1"));
// Updating a cell value
p.add(Bytes.toBytes("personal"),
Bytes.toBytes("city"),Bytes.toBytes("Delih"));
// Saving the put Instance to the HTable.
hTable.put(p);
System.out.println("data Updated");
// closing HTable
hTable.close();
}
}
编译和执行上述程序如下所示。
$javac UpdateData.java
$java UpdateData
下面列出的是输出结果:
data Updated
25. HBase读取数据
25.1. 使用JavaAPI读取数据
从一个HBase表中读取数据,要使用HTable类的get()方法。这种方法需要Get类的一个实例。按照下面从HBase表中检索数据给出的步骤。
25.1.1. 第1步:实例化Configuration类
Configuration类增加了HBase的配置文件到它的对象。使用HbaseConfiguration类的create()方法,如下图所示的配置对象。
Configuration conf = HbaseConfiguration.create();
25.1.2. 第2步:实例化HTable类
有一类叫HTable,实现在HBase中的Table类。此类用于单个HBase的表进行通信。在这个类实例,它接受配置对象和表名作为参数。实例化HTable类,如下图所示。
HTable hTable = new HTable(conf, tableName);
25.1.3. 第3步:实例化获得类
可以从HBase表使用HTable类的get()方法检索数据。此方法提取从一个给定的行的单元格。它需要一个 Get 类对象作为参数。创建如下图所示。
Get get = new Get(toBytes("row1"));
25.1.4. 第4步:读取数据
当检索数据,可以通过ID得到一个单列,或得到一组行一组行ID,或者扫描整个表或行的子集。
可以使用Get类的add方法变种检索HBase表中的数据。
从特定的列族获取指定的列,使用下面的方法。
get.addFamily(personal)
要得到一个特定的列族的所有列,使用下面的方法。
get.addColumn(personal, name)
25.1.5. 第5步:获取结果
获取结果通过Get类实例的HTable类的get方法。此方法返回Result类对象,其中保存所请求的结果。下面给出的是get()方法的使用。
Result result = table.get(g);
25.1.6. 第6步:从Result实例读值
Result 类提供getValue()方法从它的实例读出值。如下图所示,使用它从Result 实例读出值。
byte [] value =
result.getValue(Bytes.toBytes("personal"),Bytes.toBytes("name"));
byte [] value1 =
result.getValue(Bytes.toBytes("personal"),Bytes.toBytes("city"));
下面给出的是从一个HBase表中读取值的完整程序
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.util.Bytes;
public class RetriveData{
public static void main(String[] args) throws IOException, Exception{
// Instantiating Configuration class
Configuration config = HBaseConfiguration.create();
// Instantiating HTable class
HTable table = new HTable(config, "emp");
// Instantiating Get class
Get g = new Get(Bytes.toBytes("row1"));
// Reading the data
Result result = table.get(g);
// Reading values from Result class object
byte [] value = result.getValue(Bytes.toBytes("personal"),Bytes.toBytes("name"));
byte [] value1 = result.getValue(Bytes.toBytes("personal"),Bytes.toBytes("city"));
// Printing the values
String name = Bytes.toString(value);
String city = Bytes.toString(value1);
System.out.println("name: " + name + " city: " + city);
}
}
编译和执行上述程序如下所示。
$javac RetriveData.java
$java RetriveData
下面列出的是输出:
name: Raju city: Delhi
26. HBase删除数据
26.1. 使用JavaAPI删除数据
可以从使用HTable类的delete()方法删除HBase表数据。按照下面给出从表中删除数据的步骤。
26.1.1. 第1步:实例化Configuration类
Configuration类增加了HBase配置文件到它的对象。可以创建使用HbaseConfiguration类的create()方法,如下图所示的Configuration 对象。
Configuration conf = HbaseConfiguration.create();
26.1.2. 第2步:实例化HTable类
有一个类叫HTable,实现在HBase中的Table类。此类用于单个HBase的表进行通信。在这个类实例,它接受配置对象和表名作为参数。实例化HTable类,如下图所示。
HTable hTable = new HTable(conf, tableName);
26.1.3. 第3步:实例化Delete 类
通过传递将要删除的行的行ID,在字节数组格式实例化Delete类。也可以通过构造时间戳和Rowlock。
Delete delete = new Delete(toBytes("row1"));
26.1.4. 第4步:选择删除数据
可以使用Delete类的delete方法删除数据。这个类有各种删除方法。选择使用这些方法来删除列或列族。这里显示Delete类方法的用法在下面的例子。
delete.deleteColumn(Bytes.toBytes("personal"), Bytes.toBytes("name"));
delete.deleteFamily(Bytes.toBytes("professional"));
26.1.5. 第5步:删除数据
通过HTable类实例的delete()方法,如下所示删除所选数据。
table.delete(delete);
26.1.6. 第6步:关闭HTable实例
删除数据后,关闭HTable实例。
table.close();
下面给出的是从HBase表中删除的数据的完整程序。
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.util.Bytes;
public class DeleteData {
public static void main(String[] args) throws IOException {
// Instantiating Configuration class
Configuration conf = HBaseConfiguration.create();
// Instantiating HTable class
HTable table = new HTable(conf, "employee");
// Instantiating Delete class
Delete delete = new Delete(Bytes.toBytes("row1"));
delete.deleteColumn(Bytes.toBytes("personal"), Bytes.toBytes("name"));
delete.deleteFamily(Bytes.toBytes("professional"));
// deleting the data
table.delete(delete);
// closing the HTable object
table.close();
System.out.println("data deleted.....");
}
}
编译和执行上述程序如下所示。
$javac Deletedata.java
$java DeleteData
下面列出的是输出:
data deleted
27. HBase扫描
27.1. 使用JavaAPI扫描
使用Java API扫描整个表的数据的完整程序如下:
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
public class ScanTable{
public static void main(String args[]) throws IOException{
// Instantiating Configuration class
Configuration config = HBaseConfiguration.create();
// Instantiating HTable class
HTable table = new HTable(config, "emp");
// Instantiating the Scan class
Scan scan = new Scan();
// Scanning the required columns
scan.addColumn(Bytes.toBytes("personal"), Bytes.toBytes("name"));
scan.addColumn(Bytes.toBytes("personal"), Bytes.toBytes("city"));
// Getting the scan result
ResultScanner scanner = table.getScanner(scan);
// Reading values from scan result
for (Result result = scanner.next(); result != null; result = Scanner.next())
System.out.println("Found row : " + result);
//closing the scanner
scanner.close();
}
}
编译和执行上述程序如下所示。
$javac ScanTable.java
$java ScanTable
下面列出的是输出:
Found row :
keyvalues={row1/personal:city/1418275612888/Put/vlen=5/mvcc=0,
row1/personal:name/1418035791555/Put/vlen=4/mvcc=0}
28. HBase内置过滤器
HBase为筛选数据提供了一组过滤器,通过这个过滤器可以在HBase中的数据的多个维度(行,列,数据版本)上进行对数据的筛选操作,也就是说过滤器最终能够筛选的数据能够细化到具体的一个存储单元格上(由行键,列明,时间戳定位)。通常来说,通过行键,值来筛选数据的应用场景较多。
1. RowFilter:筛选出匹配的所有的行,对于这个过滤器的应用场景,是非常直观的:使用BinaryComparator可以筛选出具有某个行键的行,或者通过改变比较运算符(下面的例子中是CompareFilter.CompareOp.EQUAL)来筛选出符合某一条件的多条数据,以下就是筛选出行键为row1的一行数据:
Filter rf = new RowFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("row1"))); // OK 筛选出匹配的所有的行
2. PrefixFilter:筛选出具有特定前缀的行键的数据。这个过滤器所实现的功能其实也可以由RowFilter结合RegexComparator来实现,不过这里提供了一种简便的使用方法,以下过滤器就是筛选出行键以row为前缀的所有的行:
Filter pf = new PrefixFilter(Bytes.toBytes("row")); // OK 筛选匹配行键的前缀成功的行
3. KeyOnlyFilter:这个过滤器唯一的功能就是只返回每行的行键,值全部为空,这对于只关注于行键的应用场景来说非常合适,这样忽略掉其值就可以减少传递到客户端的数据量,能起到一定的优化作用:
Filter kof = new KeyOnlyFilter(); // OK 返回所有的行,但值全是空
4. RandomRowFilter:从名字上就可以看出其大概的用法,本过滤器的作用就是按照一定的几率(<=0会过滤掉所有的行,>=1会包含所有的行)来返回随机的结果集,对于同样的数据集,多次使用同一个RandomRowFilter会返回不通的结果集,对于需要随机抽取一部分数据的应用场景,可以使用此过滤器:
Filter rrf = new RandomRowFilter((float) 0.8); // OK 随机选出一部分的行
5. InclusiveStopFilter:扫描的时候,我们可以设置一个开始行键和一个终止行键,默认情况下,这个行键的返回是前闭后开区间,即包含起始行,但不包含终止行,如果我们想要同时包含起始行和终止行,那么我们可以使用此过滤器:
Filter isf = new InclusiveStopFilter(Bytes.toBytes("row1")); // OK 包含了扫描的上限在结果之内
6. FirstKeyOnlyFilter:如果你只想返回的结果集中只包含第一列的数据,那么这个过滤器能够满足你的要求。它在找到每行的第一列之后会停止扫描,从而使扫描的性能也得到了一定的提升:
Filter fkof = new FirstKeyOnlyFilter(); // OK 筛选出第一个每个第一个单元格
7. ColumnPrefixFilter:顾名思义,它是按照列名的前缀来筛选单元格的,如果我们想要对返回的列的前缀加以限制的话,可以使用这个过滤器:
Filter cpf = new ColumnPrefixFilter(Bytes.toBytes("qual1")); // OK 筛选出前缀匹配的列
8. ValueFilter:按照具体的值来筛选单元格的过滤器,这会把一行中值不能满足的单元格过滤掉,如下面的构造器,对于每一行的一个列,如果其对应的值不包含ROW2_QUAL1,那么这个列就不会返回给客户端:
Filter vf = new ValueFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator("ROW2_QUAL1")); // OK 筛选某个(值的条件满足的)特定的单元格
9. ColumnCountGetFilter:这个过滤器来返回每行最多返回多少列,并在遇到一行的列数超过我们所设置的限制值的时候,结束扫描操作:
Filter ccf = new ColumnCountGetFilter(2); // OK 如果突然发现一行中的列数超过设定的最大值时,整个扫描操作会停止
10. SingleColumnValueFilter:用一列的值决定这一行的数据是否被过滤。在它的具体对象上,可以调用setFilterIfMissing(true)或者setFilterIfMissing(false),默认的值是false,其作用是,对于咱们要使用作为条件的列,如果这一列本身就不存在,那么如果为true,这样的行将会被过滤掉,如果为false,这样的行会包含在结果集中。
SingleColumnValueFilter scvf = new SingleColumnValueFilter(
Bytes.toBytes("colfam1"),
Bytes.toBytes("qual2"),
CompareFilter.CompareOp.NOT_EQUAL,
new SubstringComparator("BOGUS"));
scvf.setFilterIfMissing(false);
scvf.setLatestVersionOnly(true); // OK
11. SingleColumnValueExcludeFilter:这个与10种的过滤器唯一的区别就是,作为筛选条件的列的不会包含在返回的结果中。
12. SkipFilter:这是一种附加过滤器,其与ValueFilter结合使用,如果发现一行中的某一列不符合条件,那么整行就会被过滤掉:
Filter skf = new SkipFilter(vf); // OK 发现某一行中的一列需要过滤时,整个行就会被过滤掉
13. WhileMatchFilter:这个过滤器的应用场景也很简单,如果你想要在遇到某种条件数据之前的数据时,就可以使用这个过滤器;当遇到不符合设定条件的数据的时候,整个扫描也就结束了:
Filter wmf = new WhileMatchFilter(rf); // OK 类似于Python itertools中的takewhile
14. FilterList:用于综合使用多个过滤器。其有两种关系:FilterList.Operator.MUST_PASS_ONE和FilterList.Operator.MUST_PASS_ALL,默认的是FilterList.Operator.MUST_PASS_ALL,顾名思义,它们分别是AND和OR的关系,并且FilterList可以嵌套使用FilterList,使我们能够表达更多的需求:
List<Filter> filters = new ArrayList<Filter>();
filters.add(rf);
filters.add(vf);
FilterList fl = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters); // OK 综合使用多个过滤器, AND 和 OR 两种关系
HBase shell使用
1.值匹配(谁的值=user2)
Hbase>scan 'emp',FILTER=>"ValueFilter(=,'binary:user2')"
2.谁的值包含user
Hbase>scan 'emp',FILTER=>"ValueFilter(=,'substring:user')"
3.查找(column为'name')值包含2的用户
Hbase>scan 'emp',FILTER=>"ColumnPrefixFilter('name') ANDValueFilter(=,'substring:2')"
4.通过搜索进来的(column为s)值包含123或者222的用户
Hbase>scan'emp', FILTER=>"ColumnPrefixFilter('name') AND (ValueFilter(=,'substring:2') OR ValueFilter(=,'substring:3') )"
5.rowkey为user1开头的
Hbase>scan 'test1', FILTER => "PrefixFilter (‘row’)"
6.FirstKeyOnlyFilter: 一个rowkey可以有多个version,同一个rowkey的同一个column也会有多个的值, 只拿出key中的第一个column的第一个version;KeyOnlyFilter: 只要key,不要value
Hbase>scan 'emp', FILTER=>"FirstKeyOnlyFilter()"
7.从user1|ts2开始,找到所有的rowkey以user1开头的
Hbase>scan 'emp', {STARTROW=>'row1', FILTER =>"PrefixFilter ('row1')"}
8.从user1|ts2开始,找到所有的到rowkey以user2开头
Hbase>scan'test1', {STARTROW=>'user1|ts2', STOPROW=>'user2'}
9. 查询rowkey里面包含ts3的
Hbase>import org.apache.hadoop.hbase.filter.CompareFilter
Hbase>importorg.apache.hadoop.hbase.filter.SubstringComparator
Hbase>importorg.apache.hadoop.hbase.filter.RowFilter
Hbase>scan 'emp', {FILTER =>RowFilter.new(CompareFilter::CompareOp.valueOf('EQUAL'), SubstringComparator.new('row1'))}
查询rowkey里面包含ts的
Hbase>importorg.apache.hadoop.hbase.filter.CompareFilter
Hbase>importorg.apache.hadoop.hbase.filter.SubstringComparator
Hbase>import org.apache.hadoop.hbase.filter.RowFilter
scan 'test1', {FILTER => RowFilter.new(CompareFilter::CompareOp.valueOf('EQUAL'),SubstringComparator.new('ts'))}
加入一条测试数据
put 'emp', 'user2|err', 'personal:name', 'sku999'
查询rowkey里面以user开头的,新加入的测试数据并不符合正则表达式的规则,故查询不出来importorg.apache.hadoop.hbase.filter.RegexStringComparator
import org.apache.hadoop.hbase.filter.CompareFilter
import org.apache.hadoop.hbase.filter.SubstringComparator
import org.apache.hadoop.hbase.filter.RowFilter
scan 'emp', {FILTER => RowFilter.new(CompareFilter::CompareOp.valueOf('EQUAL'),RegexStringComparator.new('^user\d'))}
hbase zkcli 的使用
hbase zkcli
ls /
[hbase, zookeeper]
[zk: hadoop000:2181(CONNECTED) 1] ls /hbase
[meta-region-server, backup-masters, table, draining,region-in-transition, running, table-lock, master, namespace, hbaseid,online-snapshot, replication, splitWAL, recovering-regions, rs]
[zk: hadoop000:2181(CONNECTED) 2] ls /hbase/table
[member, test1, hbase:meta, hbase:namespace]
[zk: hadoop000:2181(CONNECTED) 3] ls /hbase/table/test1
[]
[zk: hadoop000:2181(CONNECTED) 4] get /hbase/table/test1
?master:60000}l$??lPBUF
cZxid = 0x107
ctime = Wed Aug 27 14:52:21 HKT 2014
mZxid = 0x10b
mtime = Wed Aug 27 14:52:22 HKT 2014
pZxid = 0x107
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 31
numChildren = 0
29. hbase原理
29.1. 体系图
29.1.1. 写流程
1、 client向hregionserver发送写请求。
2、 hregionserver将数据写到hlog(write ahead log)。为了数据的持久化和恢复。
3、 hregionserver将数据写到内存(memstore)
4、 反馈client写成功。
29.1.2. 数据flush过程
1、 当memstore数据达到阈值(默认是64M),将数据刷到硬盘,将内存中的数据删除,同时删除Hlog中的历史数据。
2、 并将数据存储到hdfs中。
3、 在hlog中做标记点。
29.1.3. 数据合并过程
1、 当数据块达到4块,hmaster将数据块加载到本地,进行合并
2、 当合并的数据超过256M,进行拆分,将拆分后的region分配给不同的hregionserver管理
3、 当hregionser宕机后,将hregionserver上的hlog拆分,然后分配给不同的hregionserver加载,修改.META.
4、 注意:hlog会同步到hdfs
29.1.4. hbase的读流程
1、 通过zookeeper和-ROOT- .META.表定位hregionserver。
2、 数据从内存和硬盘合并后返回给client
3、 数据块会缓存
29.1.5. hmaster的职责
1、管理用户对Table的增、删、改、查操作;
2、记录region在哪台Hregion server上
3、在Region Split后,负责新Region的分配;
4、新机器加入时,管理HRegionServer的负载均衡,调整Region分布
5、在HRegion Server宕机后,负责失效HRegion Server 上的Regions迁移。
29.1.6. hregionserver的职责
HRegion Server主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBASE中最核心的模块。
HRegion Server管理了很多table的分区,也就是region。
29.1.7. client职责
Client
HBASE Client使用HBASE的RPC机制与HMaster和RegionServer进行通信
管理类操作:Client与HMaster进行RPC;
数据读写类操作:Client与HRegionServer进行RPC。
30. Hadoop与mr的交互:
1. 拷贝hbase jar到hadoop classpath中.
>cd/data/hbase-1.2.5/lib/
>cp */data/hadoop-2.7.0/share/hadoop/common/lib/
>分发
30.1. 实现方法
Hbase对MapReduce提供支持,它实现了TableMapper类和TableReducer类,我们只需要继承这两个类即可。
1、写个mapper继承TableMapper<Text, IntWritable>
参数:Text:mapper的输出key类型; IntWritable:mapper的输出value类型。
其中的map方法如下:
map(ImmutableBytesWritablekey, Result value,Context context)
参数:key:rowKey;value: Result,一行数据; context上下文
2、写个reduce继承TableReducer<Text, IntWritable, ImmutableBytesWritable>
参数:Text:reducer的输入key; IntWritable:reduce的输入value;
ImmutableBytesWritable:reduce输出到hbase中的rowKey类型。
其中的reduce方法如下:
reduce(Textkey, Iterable<IntWritable> values,Context context)
参数: key:reduce的输入key;values:reduce的输入value;
30.2. 准备表
1、建立数据来源表‘word’,包含一个列族‘content’
向表中添加数据,在列族中放入列‘info’,并将短文数据放入该列中,如此插入多行,行键为不同的数据即可
>create 'word','content'
put 'word','row1','content:info','hello'
put 'word','row2','content:info','hello'
put 'word','row3','content:info','hello'
put 'word','row4','content:info','world'
put 'word','row5','content:info','world'
put 'word','row6','content:info','world'
put 'word','row7','content:info','me'
put 'word','row8','content:info','me'
put 'word','row9','content:info','a'
put 'word','row10','content:info','b'
2、建立输出表‘stat’,包含一个列族‘content’
3、通过Mr操作Hbase的‘word’表,对‘content:info’中的短文做词频统计,并将统计结果写入‘stat’表的‘content:info中’,行键为单词
30.3. 实现
package com.itcast.hbase;
import java.io.IOException; import java.util.ArrayList; import java.util.List;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil; import org.apache.hadoop.hbase.mapreduce.TableMapper; import org.apache.hadoop.hbase.mapreduce.TableReducer; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; /** * mapreduce操作hbase * @author wilson * */ public class HBaseMr { /** * 创建hbase配置 */ static Configuration config = null; static { config = HBaseConfiguration.create(); config.set("hbase.zookeeper.quorum", "slave1,slave2,slave3"); config.set("hbase.zookeeper.property.clientPort", "2181"); } /** * 表信息 */ public static final String tableName = "word";//表名1 public static final String colf = "content";//列族 public static final String col = "info";//列 public static final String tableName2 = "stat";//表名2 /** * 初始化表结构,及其数据 */ public static void initTB() { HTable table=null; HBaseAdmin admin=null; try { admin = new HBaseAdmin(config);//创建表管理 /*删除表*/ if (admin.tableExists(tableName)||admin.tableExists(tableName2)) { System.out.println("table is already exists!"); admin.disableTable(tableName); admin.deleteTable(tableName); admin.disableTable(tableName2); admin.deleteTable(tableName2); } /*创建表*/ HTableDescriptor desc = new HTableDescriptor(tableName); HColumnDescriptor family = new HColumnDescriptor(colf); desc.addFamily(family); admin.createTable(desc); HTableDescriptor desc2 = new HTableDescriptor(tableName2); HColumnDescriptor family2 = new HColumnDescriptor(colf); desc2.addFamily(family2); admin.createTable(desc2); /*插入数据*/ table = new HTable(config,tableName); table.setAutoFlush(false); table.setWriteBufferSize(5); List<Put> lp = new ArrayList<Put>(); Put p1 = new Put(Bytes.toBytes("1")); p1.add(colf.getBytes(), col.getBytes(), ("The Apache Hadoop software library is a framework").getBytes()); lp.add(p1); Put p2 = new Put(Bytes.toBytes("2"));p2.add(colf.getBytes(),col.getBytes(),("The common utilities that support the other Hadoop modules").getBytes()); lp.add(p2); Put p3 = new Put(Bytes.toBytes("3")); p3.add(colf.getBytes(), col.getBytes(),("Hadoop by reading the documentation").getBytes()); lp.add(p3); Put p4 = new Put(Bytes.toBytes("4")); p4.add(colf.getBytes(), col.getBytes(),("Hadoop from the release page").getBytes()); lp.add(p4); Put p5 = new Put(Bytes.toBytes("5")); p5.add(colf.getBytes(), col.getBytes(),("Hadoop on the mailing list").getBytes()); lp.add(p5); table.put(lp); table.flushCommits(); lp.clear(); } catch (Exception e) { e.printStackTrace(); } finally { try { if(table!=null){ table.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * MyMapper 继承 TableMapper * TableMapper<Text,IntWritable> * Text:输出的key类型, * IntWritable:输出的value类型 */ public static class MyMapper extends TableMapper<Text, IntWritable> { private static IntWritable one = new IntWritable(1); private static Text word = new Text(); @Override //输入的类型为:key:rowKey; value:一行数据的结果集Result protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException { //获取一行数据中的colf:col String words = Bytes.toString(value.getValue(Bytes.toBytes(colf), Bytes.toBytes(col)));// 表里面只有一个列族,所以我就直接获取每一行的值 //按空格分割 String itr[] = words.toString().split(" "); //循环输出word和1 for (int i = 0; i < itr.length; i++) { word.set(itr[i]); context.write(word, one); } } } /** * MyReducer 继承 TableReducer * TableReducer<Text,IntWritable> * Text:输入的key类型, * IntWritable:输入的value类型, * ImmutableBytesWritable:输出类型,表示rowkey的类型 */ public static class MyReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable> { @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { //对mapper的数据求和 int sum = 0; for (IntWritable val : values) {//叠加 sum += val.get(); } // 创建put,设置rowkey为单词 Put put = new Put(Bytes.toBytes(key.toString())); // 封装数据 put.add(Bytes.toBytes(colf), Bytes.toBytes(col),Bytes.toBytes(String.valueOf(sum))); //写到hbase,需要指定rowkey、put context.write(new ImmutableBytesWritable(Bytes.toBytes(key.toString())),put); } }
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { config.set("df.default.name", "hdfs://master:9000/");//设置hdfs的默认路径 config.set("hadoop.job.ugi", "hadoop,hadoop");//用户名,组 config.set("mapred.job.tracker", "master:9001");//设置jobtracker在哪 //初始化表 initTB();//初始化表 //创建job Job job = new Job(config, "HBaseMr");//job job.setJarByClass(HBaseMr.class);//主类 //创建scan Scan scan = new Scan(); //可以指定查询某一列 scan.addColumn(Bytes.toBytes(colf), Bytes.toBytes(col)); //创建查询hbase的mapper,设置表名、scan、mapper类、mapper的输出key、mapper的输出value TableMapReduceUtil.initTableMapperJob(tableName, scan, MyMapper.class,Text.class, IntWritable.class, job); //创建写入hbase的reducer,指定表名、reducer类、job TableMapReduceUtil.initTableReducerJob(tableName2, MyReducer.class, job); System.exit(job.waitForCompletion(true) ? 0 : 1); } } |
1. 垃圾回收优化
用户可以通过向hbase-env.sh文件中添加HBASE_OPTS或者HBASE_REGIONSERVER_OPT来设置垃圾回收相关选项,后者仅仅影响region服务器进程,也是推荐的修改方式。
增加新生代大小, 减小新生代垃圾回收次数
-XX:MaxNewSize=8g -XX:NewSize=8g
修改垃圾回收策略
-XX:+UseParNewGC
设置年轻代使用Parallel New Collector策略,这将停止运行Java进程而去清空年轻代堆。与老年代相比年轻代很小,所以这个过程话费时间很短,通常几百毫秒。
-XX:+UseConcMarkSweepGC
以上策略如果用于年老代会造成region server几秒钟甚至数分钟停顿,如果停顿时间超过zookeeper会话超时限制,这个服务器会被master认为已经崩溃,并且随后会被抛弃。
这种情况可以使用并行标记回收策略(Concurrent Mark-Sweep Collector, CMS)来缓解, 不同之处在于其工作时试图在不停止运行Java进程的情况下异步并行的完成工作。这种策略将增加CPU的负担,但是却可以避免重写老生代碎片时的停顿--除非发生提示失败,这种失败会迫使垃圾回收暂停运行Java进程并进行内存整理。
2. 本地memstore分配缓冲区
由于memstore不断创建和释放内存空间, 就会在年老代Heap上产生孔洞。申请新空间时,由于碎片过多导致没有足够大的连续空间分配,JRE会退回到使用(stop the world)垃圾回收器,这样会导致其重写整个堆空间并压缩剩余的可用对象。
MSLAB(memstore-local allocation buffers)是许多固定大小的缓冲区,用来存储大小不同的keyvalue实例。当一个缓冲区不能放下一个新加入的keyvalue时,系统就认为这个缓冲区已经占满了,然后创建一个新的固定大小的缓冲区。 一旦这些缓冲区对象被回收,他们将在堆中留下固定大小的孔洞,之后调用固定大小的新对象将会重新使用这些孔洞,这样就不需要JRE停止压缩回收堆内存了。
但是mslab也有一些副作用,比如更加浪费堆空间;使用缓冲区需要额外的内存复制工作,比直接使用keyvalue实例要稍微慢一点
配置hbase-site.xml中的hbase.hregion.memstore.mslab.enabled 默认值 true
3 压缩
除非存储已经压缩过的内容如JPEG图像,对于其它场景来说,压缩通常会带来较好的性能,因为CPU压缩和解压的时间比从磁盘读取和写入更多数据消耗的时间更短。
算法 | 压缩比 % | 压缩 MB/S | 解压 MB/S |
GZIP | 13.4 | 21 | 118 |
LZO | 20.5 | 135 | 410 |
Zippy/Snappy | 22.2 | 172 | 409 |
默认Hbase对文件是没有压缩的,查看 describe 'tablename'
4. 优化拆分和合并
通常Hbase是自动处理Region拆分的,一旦它们到了预定的阈值,region将被拆分成两个,之后它们可以接受新的数据并继续增长。 当用户的region大小已恒定速度增长时,region拆分会在同一时间发生,因为同时需要压缩region中的存储文件,这个过程会重写拆分之后的数据,这将引起IO上升,称之为“拆分和并风暴”。
与其依赖自动拆分,不如关闭这个行为调用split和major_compace命令手动拆分。
为防止自动拆分可设置hbase.hregion.max.filesize的值为一个比较大的值,比如100GB。然后用客户端实现一个调用split()和 majorCompact()的客户端,也可以使用shell交互的调用相关命令,或者使用cron定时的调用它们。
另一种方法是创建表时进行预拆分
create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
5.负载均衡
master内置了一个叫做均衡器的特性,默认情况下每5分钟(通过hbase.balancer.period设置)运行一次。 一旦均衡器启动,它会尝试均匀分配region到所有region服务器。用户可通过shell的balance_switch命令来更改均衡器的开启或关闭状态。
除了依赖均衡器自动完成工作,用户还可以使用move命令显示地将region移动到另一个region server上。
6. 合并region
当用户删除大量数据并且想减少每个服务器管理的region数量,可以使用merge_region命令合并相邻的region。
7 客户端API最佳实践
禁止自动刷新
put.setAutoFlush(false)
使用扫描缓存
scan.setCaching(1000);
限定扫描范围
尽量只在一个Family中扫描
关闭ResultScanner
一定要在try catch 的 finally 中关闭ResultScanner
快缓存用法
scan.setCacheBlocks() 对于那些频繁访问的行,建议使用块缓存
优化获取行健的方式当用户仅需要获取需要的行健时,在Scan中用setFilter()方法添加一个带MUST_PASS_ALL的FilterList。FilterList中包含FirstKeyFilter和KeyOnlyFilter两个过滤器。使用以上的组合过滤器将会把发现的第一个keyvalue行健返回给客户端。
关闭put上的WAL
当需要存入的数据对准确度要求不是很高时,使用Put的writeToWAL(false)来关闭WAL。
1、assign分配一个region,请谨慎使用。assign't1,,1389754486055.3f6aacebea6c8b2b66ac7d1d565a358d.'
2、balance_switch设置balancer是否开启,会返回之前balancer的状态
3、balancer执行均衡region的操作,如果成功发出指令将返回true,否则返回false,比如处于rit状态时无法做均衡
4、close_region关闭region,可以直接让rs操作close_region'3f6aacebea6c8b2b66ac7d1d565a358d', 'node104,60020,1390292741528',也可以让master操作close_region't1,,1389754486055.3f6aacebea6c8b2b66ac7d1d565a358d.'
5、compact执行紧缩操作,可以表,region或列族粒度compact 't1',compact 'r1',compact 't1', 'c1',compact 'r1', 'c1'
6、flush将memstore中数据刷入hdfs,可以表,也可以regionflush 'TABLENAME',flush 'REGIONNAME'
7、hlog_roll对wal进行轮转,需传入rs完整名字hlog_roll'node104,60020,1390292741528'
8、major_compact使用方式桶compact,区别只是minor和major之间的差异,前者只是文件的合并,后者还会对过期数据,删除数据进行清理
9、move移动region被托管的地方,可以指定rs,也可以任由其随机选move 'ENCODED_REGIONNAME',move 'ENCODED_REGIONNAME', 'SERVER_NAME'
10、split拆分region,可以传入表名,可以传入region全名,可以指定分割点split 'tableName',split 'tableName', 'splitKey'
11、unassign将现有region下线,同时在新的地方打开,可以传入true强制执行unassign't1,,1389754486055.3f6aacebea6c8b2b66ac7d1d565a358d.', true
12、zk_dump将zk目前的信息dump出来