写在前面:HBase的文档非常详细,如果想深入了解HBase, 从它的官方文档肯定可以学到很多内容,这篇笔记主要供日后回顾,记录一下这次简单使用Hbase的过程中了解到的一些信息。
Hbase简介
HBase是一个分布式可扩展的大数据存储。它是一个开源非关系型数据库,基于Google在2006年发表的论文BigTable开发,可实时读写大规模数据。Hbase官网对其简介如下。
Apache HBase® is the Hadoop database, a distributed, scalable, big data store. Use Apache HBase® when you need random, realtime read/write access to your Big Data. This project’s goal is the hosting of very large tables – billions of rows X millions of columns – atop clusters of commodity hardware. Apache HBase® is an open-source, distributed, versioned, non-relational database modeled after Google’s Bigtable: A Distributed Storage System for Structured Data by Chang et al. Just as Bigtable leverages the distributed data storage provided by the Google File System, Apache HBase® provides Bigtable-like capabilities on top of Hadoop and HDFS.
什么场景下适合Hbase呢?
在HBase官方文档中明确指出HBase并不适合解决所有问题。
满足如下三个条件时,Hbase是一个很好的选择。
- 确保拥有足够的数据。如果拥有数亿或数十亿行数据,那么HBase是一个不错的选择。如果只有几千/几百万行数据,使用传统的RDBMS(关系型数据库)可能是一个更好的选择,因为数据量不多时所有数据可能最终都集中在一个节点(或两个节点)上,而集群的其余部分可能处于闲置状态。
- 确保可以不使用RDBMS提供的所有额外功能(例如,带类型的数据列、二级索引、事务、高级查询语言等)。例如,基于RDBMS构建的应用程序不能通过简单地更改JDBC驱动程序来“移植”到HBase。从RDBMS迁移到HBase将是一次彻底的重新设计,而不仅仅只是移植。
- 确保拥有足够的硬件。即使是HDFS,在少于5个DataNode(由于HDFS块复制等原因,默认值为3)加上一个 NameNode 的情况下也表现不佳。
虽然HBase可以在笔记本电脑上独立运行得很好,但这应该仅被视为开发配置。
更多关于HBase的原理可以参考架构这一章。
Hbase数据模型
在Hbase中,数据是存储在表(table)中的,表包括列和行,这与关系型数据库的术语是一样的,但是用关系型数据库来作类比对理解HBase无益,将其理解为多维字典(multi-dimentional map)更形象。
Hbase数据模型包括的术语有:
- Table:Hbase的table由多个行(row)组成
- Row:在Hbase中一个行由一个或多个有值的列组成。Row按照字母进行排序,因此Rowkey的设计非常重要。这种设计方式可以让有关系的行的距离比较近。想象一下row key是网站域名的场景,在设计时可以将网站的域名反转,比如(org.apache.www, org.apache.mail, org.apache.jira),这样的话所有的Apache的域名在表中的距离就挨得比较近。
- Column:列由列簇加上列簇的限定词组成,一般是"列簇:列簇的限定词",即列簇和列簇的限定词之间由冒号分隔。创建表的时候不用指定列簇的限定词。
- Column Family:列簇在物理上包含了许多的列与列的值,每个列簇都有一些存储的属性可配置。例如是否使用缓存,压缩类型,存储版本数等。在表中,每一行都有相同的列簇,尽管有些列簇什么东西也没有存。下面是几个核心属性。 - VERSIONS:对于每一个Rowkey对应的数据,在定义表结构时可以指定最多存储几个版本的数据,默认是一个版本,也就是如果有新写入的数据会覆盖之前已经写入的数据。- BLOCKCACHE :是否开启BlockCache,默认为true。如果该值为true,数据Block从HFile加载出来之后会被放入读缓存,供后续读请求读取。如果设置为false,表示数据Block在任何时候都不会放入读缓存。在两种场景下可以将此值设置为false——一种场景是数据量很大且读取没有任何热点;另一种场景是表数据仅供OLAP分析,没有OLTP需求- TTL (Time To Live) :数据失效时间。TTL是HBase非常重要的一个特性,可以让数据自动过期失效,不需要用户手动删除。大多数大数据业务最关心最新数据,对历史数据关注度会逐渐降低,这种背景下让数据自动失效不仅不影响业务服务,反而会降低存储成本、减少数据量规模,进而提升查询性能。需要注意的是,HBase TTL过期的数据是通过Compaction机制进行删除的,因此会出现失效时间到期之后,数据还存在于系统之中但查询不出来的情况。- BLOOMFILTER :布隆过滤器类型,可选项为NONE、ROW和ROWCOL,默认为ROW。布隆过滤器过滤部分肯定不存在待查找的KV来提高随机读的效率。ROW模式表示仅仅根据rowkey就可以判断待查找数据是否存在于HFile中,而ROWCOL模式只对指定列的随机读有优化作用,如果用户只根据rowkey定位所有数据,而没有具体指定列查找,ROWCOL模式就不会有任何效果。通常建议选择ROW模式。- BLOCKSIZE :文件块大小。Block是HBase系统文件层面写入、读取的最小粒度,默认块大小为64K。对于不同的业务数据,块大小的合理设置对读写性能有很大的影响。通常来说,如果业务请求以get请求为主,可以考虑将块设置较小;如果以scan请求为主,可以将块调大;默认的64K块大小是在scan和get之间取得的一个平衡。(默认块大小适用于多种数据使用模式,调整块大小是比较高级的操作。配置错误将对性能产生负面影响。因此建议在调整之后进行测试,根据测试结果决定是否可以线上使用)- COMPRESSION :压缩算法,可选项为NONE、SNAPPY、LZ4和ZLTD。压缩最直接、最重要的作用是减少数据存储成本,理论上SNAPPY算法的压缩率可以达到5∶1甚至更高,但是根据测试数据不同,压缩率可能并没有达到理论值,另一方面,压缩/解压缩需要消耗大量计算资源,对系统CPU资源需求较高。生产环境一般推荐使用SNAPPY。- DFS_REPLICATION :数据Block在HDFS上存储的副本数,默认为HDFS文件系统设置值。DFS_REPLICATION可以让不同列簇数据在HDFS上拥有不同的副本数,这是非常个性化、非常有意义的一个配置。比如在默认备份数为3的情况下,某个列簇数据非常重要而且数据量不大,那就可以将DFS_REPLICATION设置为5,增加数据的绝对可靠性;同理,如果某个列簇数据不重要但数据量非常大,在业务允许的情况下可以将DFS_REPLICATION设置为2,甚至为1。- IN_MEMORY :如果表中某些列的数据量不大,但是进行get和scan操作的频率又特别高,同时业务方还希望get和scan操作的延迟更低,此时采用IN_MEMORY效果比较好。
- Column Qualifier:列簇的限定词,理解为列的唯一标识。创建表时列簇是固定的,但是列的限定词是可以改变的,每一行可能有不同的列的限定词。 Cell:Cell是由row,column family,column qualifier,时间戳与值组成的,一般表达某个值的版本。 Timestamp:时间戳一般写在value的旁边,代表某个值的版本号,默认的时间戳是你写入数据的那一刻,但是你也可以在写入数据的时候指定不同的时间戳。
- Namespace:命名空间有点类似于关系数据库中的一个数据库。
更多数据模型信息参考HBase数据模型这一章。
rowkey设计,主要避免出现查询热点(hotspot)。
Hbase单机安装和HBase Shell使用
单机版HBase安装文档:https://hbase.apache.org/book.html#quickstart。
安装完HBase之后,就可以用
hbase shell
命令来启动Hbase Shell来熟悉HBase的使用了。(
hbase shell
命令在HBase安装目录的*bin/*目录下,如果不确定HBase安装目录,可以试试看“/usr/local/hbase/”)
下面是使用Hbase Shell进行一些基本的操作。
$ ./bin/hbase shell
hbase(main):001:0>
输入
help
可以显示出HBase shell的基本使用信息和一些示例命令,注意在使用时要将表名、行名、列名等用单引号给包括起来。
## 创建命名空间
create_namespace 'test'## 列出命名空间
list_namespace
## 创建表,如果不指定命名空间,使用默认命名空间default
create 'test:device_report', {NAME =>'info', VERSIONS =>10, BLOCKCACHE => true, TTL =>2592000}## 判断表是否存在
exists 'test:device_report'
list 'test:device_report'## 列出所有表
list
## 查看表详细信息,包括默认的设置值
describe 'test:device_report'## 计数
count 'test:device_report'## 写入数据
put 'test:device_report','row_key1','info:value','AAAVVADw'
put 'test:device_report','row_key1','info:value','AAAVVQDw'
put 'test:device_report','row_key1','info:value','AAAVVhD'## 一次扫描所有数据。 默认情况下,通过 hbase shell 的 scan 或 get 等命令获取的中文内容都是 16 进制的,无法直观的查看数据。通过 {FORMATTER => 'toString'} 可以将 16 进制中文转换成 utf-8 格式的中文的
scan 'test:device_report', {FORMATTER =>'toString', VERSIONS =>10}## 获取单行数据
get 'test:device_report', 'row_key1'## 删除表之前先要禁用表,禁用后的表可以再通过 enable来恢复
disable 'test:device_report'## 删除表。
drop 'test:device_report'
Python客户端HappyBase
Python连接HBase用的比较多的客户端是HappyBase,它基于Python Thrift库来连接HBase提供的Thrift网关。
默认启动HBase时,HBase的thrift服务没有启动,可通过如下命令在HBase的安装目录来启动。
## 方法1:直接启动, 不指定端口的话,使用默认端口9090
./bin/hbase thrift start -p<port_number>## 方法2:启动并在后台运行
./bin/hbase-daemon.sh start thrift
启动HBase的thrift服务之后,用HappyBase连接Hbase时报错
thriftpy2.transport.base.TTransportException: TTransportException(type=4, message='TSocket read 0 bytes')
,在网上搜到博客说这是因为thrift的sever端和client端的协议不匹配造成的,Python要使用TCompactProtocol(高效的二进制序列化协议),不能使用默认的TBinaryProtocol(默认的简单的二进制序列化协议)。解决的办法如下:
- 在HBase的安装目录修改"conf/hbase-site.xml",增加TFramedTransport和TCompactProtocal功能
<property><name>hbase.regionserver.thrift.framed</name><value>true</value></property><property><name>hbase.regionserver.thrift.compact</name><value>true</value></property>
- 重启thrift:
./bin/hbase-daemon.sh restart thrift
。 - 在用HappyBase建立连接时增加protocol和transport参数。
connection = happybase.Connection(host=out_host, protocol='compact', transport='framed')
这样设置之后,就可以Happybase就可以正常连接到Hbase了,但是每进行一次写入或者读取数据后,如果不进行重新连接,就会又报错
thriftpy2.transport.base.TTransportException: TTransportException(type=4, message='TSocket read 0 bytes')
,前面提到的博客的解决办法是通过设置如下参数(
hbase.thrift.connection.max-idletime
的单位是秒,
hbase.thrift.server.socket.read.timeout
的单位是毫秒。也就是将连接空闲时间和读取超时时间设置的非常大)。
<property><name>hbase.thrift.server.socket.read.timeout</name><value>86400000</value></property><property><name>hbase.thrift.connection.max-idletime</name><value>31104000</value></property>
Happybase部分示例代码如下,更多用法参见其文档。
import happybase
out_host ='xx.xx.xx.xx'
out_port =9090# 默认端口就是9090
connection = happybase.Connection(host=out_host, port=out_put, protocol='compact', transport='framed')
connection.open()# 手动与hBase建立socket连接,如果设置了autoconnect为True,文档里说可以不进行手动建立连接了## 查看库里的表print(connection.tables())
table_name ='xxx'
table = connection.table(name=table_name)#scan的使用 row_prefix前缀参数: 不能和row_start和row_stop一起使用
res = table.scan(row_prefix='xxx', columns='xx:xx',**kwargs)## 通过scan扫描一些数据, 注:包里的scan没有sersions参数,无法获取多个version的数据,默认只返回最新版本的数据for key, data in table.scan(limit=10):print(key, data)## 取指定行的书
row = table.row(b'row-key')print(row[b'info:value'])## 使用cells可以将多个version的数据返回,默认会将所有的version都返回,也可以通过versions参数指定返回版本数。
row_key =b'xx'
versions = table.cells(row_key,b'info:value')## 写入数据
row_key =b'row-key'
table.put(row_key,{b'info:value':b'AAAVXRD'})
table.put(row_key,{b'info:value':b'AAAVXgD'})# 删除某一行数据
table.delete(b'row-key')
connection.close()
版权归原作者 chencjiajy 所有, 如有侵权,请联系我们删除。