组件基于Presto Connector接口规范实现,用来给Presto增加查询HBase的功能。
相比其他开源版本的HBase Connector,我们的性能要快10到100倍以上。
此版本基于 dev_prestodb-0.220_0.1.2 分支开发
变化:
connector.name=hbase
hbase.config.resources=/path/hbase/conf/hbase-site.xml,/path/hbase/conf/core-site.xml
meta-dir=/path/presto-hbase-connector/example
列转行,一般用于 HBase 一个 row key 包含多个带有序列的COL,可以支持列转的行。如
row1 a_1:va1 b_1:va2 dt_1:1 col:val
a_2:va4 b_2:va5 dt_2:2 col:val
a_3:va6 b_3:va7 dt_3:3 col:val
row2 a_1:va8 b_1:va9 dt_1:3 col:val
如上数据,对于 row1 有 3 行,row2 有 1 行。 在 hbase 中是 2 个rowkey,但是在 presto 我们希望能查询出 4 行数据。如:
rowkey a b dt col
row1 va1 va2 1 val
row1 va4 va5 2 val
row1 va6 va7 3 val
row2 va8 va9 3 val
列转行对于 HBase ROW-KEY 多值的,但是需要在 schema 上展开来查询时非常有效。由 explodeColumn 配置按照那个列展开。如 上述数据 dt 列。 可知第一行数据有:1,2,3 个取值,则在获取Hbase result 时会组装 a_${num}, b_${num}, c_${num} 列进行取值。
当然,优先采用json设置的列名进行,如果cell 中没有改名称的列,则会采用上述行转列进行。
环境 | 明细 |
---|---|
数据量 | 事件表500万条数据,90个字段 |
节点数 | 3 |
硬件 | 16逻辑核 64G内存(其中Presto和HBase各占16G内存) 4T*2硬盘 |
功能点 | 易观 | 其他 |
---|---|---|
加盐查询 | 支持 | 不支持 |
拼接StartKey/EndKey | 支持 | 不支持 |
批量Get查询 | 支持 | 不支持 |
谓词下推(Filter) | 支持 | 不支持 |
ClientSideScan | 支持 | 支持 |
Insert | 支持 | 支持 |
Delete | 支持 | 支持 |
建表语句 | 后续支持 | 支持 |
mvn clean package
在{Presto_Config_Dir}/catalog目录下创建hbase.properties文件。配置完成后同步到所有worker节点。
以下是一个比较简单的配置样例,供大家参考:
connector.name=hbase
hbase.config.resources=/path/hbase/conf/hbase-site.xml
meta-dir=/path/presto-hbase-connector/example
参数说明如下:
connector.name
该配置固定设置为hbase。
meta-dir
存放HBase表元数据信息的目录。
hbase.config.resources
配置 hbase site 文件地址的绝对路径或者相对路径 hbase-site.xml
完成hbase.properties的配置之后,需要在{meta-dir}目录创建HBase的namespace目录结构
目录结构的样例如下:
--meta-dir:
--namespace_a:
table_a1.json
table_a2.json
--namespace_b:
table_b1.json
table_b2.json
--default:
table_c.json
这个例子中分别定义了namespace_a:table_a1、namespace_a:table_a2、namespace_b:table_b1、namespace_b:table_b2以及default命名空间下的 table_c这5张表。
此版本支持了 hbase 表名称模糊匹配 meta的能力。如hbase表,gxdata_202007 每月一张HBase表,如果没有 gxdata_202007.json 则会匹配 gxdata.json。意味着:如果没有确切名字的meta描述信息,则会采用前缀匹配,尽可能的获取 meta json。
namespace目录创建完成之后,我们需要配置表结构json文件,下面讲解一下json文件中的属性:
表json:
属性名 | 描述 |
---|---|
tableName | 表名 |
schemaName | Namespace |
rowKeyFormat | RowKey是由哪些字段组成,用英文逗号分隔。字段组成有序。 |
rowKeySeparator | RowKey字段之间的分隔符,默认是\001 |
rowKeySaltUpperAndLower | RowKey的盐值范围。上界和下界不能小于0,值之间用英文逗号分隔。例如:0,29 |
describe | 表格描述 |
columns | 字段列表 |
storageType | 序列化类型。默认 text。支持 binary、text |
columns json:
属性名 | 描述 |
---|---|
family | 列族名 |
columnName | 字段名 |
isRowKey | 是否行键 |
type | 字段类型(大小写不敏感): string、int、bigint、double、boolean(用int存储,0代表false,1代表false)、array< string > |
comment | 字段备注 |
说明:isRowKey为true,表示我们把表的行键抽象成为了一个具体的字段。无论是查询、写入还是其他各种复杂操作,他在表面上与一个普通的字段没有任何区别,只不过在底层他作为表的行键有着其特殊的含义。
RowKey字段的类型必须为varchar。
以下是一个简单的json文件示例:
{
"tableName": "t_event_test",
"schemaName": "db_test",
"rowKeyFormat": "xwhat,xwho",
"rowKeySaltUpperAndLower": "0,29",
"describe": "Table for test!",
"columns": [{
"family": "",
"columnName": "rowkey",
"comment": "The RowKey column of table!",
"type": "varchar",
"isRowKey": true
}, {
"family": "f",
"columnName": "xwho",
"comment": "Column for test!",
"type": "varchar",
"isRowKey": false
}, {
"family": "f",
"columnName": "ds",
"comment": "Column for test!",
"type": "varchar",
"isRowKey": false
}],
"rowKeySeparator": "-"
}
根据表的namespace找到它在{meta-dir}中对应的目录,按照上述说明创建以表名命名的json文件。
完成上述步骤之后,接下来需要编译组件jar包
// 下载组件源码
// 使用maven构建组件的jar包
mvn clean package
在{plugin.dir}目录下创建插件目录hbase(目录名称可任意设置)。
将构建好的prestodb-hbase-{version.num}.jar拷贝到该目录下,并同步到所有的worker节点上。
在dev_0.1.1版本支持了写入操作。写入操作需要用户以字段拼接或者固定值的方式,指定数据的row_key。如下:
insert into hbase.db_test.test_event(row_key, xwho, distinct_id, ds, xwhen, xwhat, attri_1) select '01-test_rowkey' as row_key, xwho, distinct_id, ds, xwhen, xwhat, attri_1 from hbase.db_test.test_event_v2 where xwhen=1562057346821;
insert into hbase.db_test.test_event(row_key, xwho, distinct_id, ds, xwhen, xwhat, attri_1) select concat('01-', xwho, '-', xwhat, '-', xwhen) as row_key, xwho, distinct_id, ds, xwhen, xwhat, attri_1 from hbase.db_test.test_event_v2 where xwhat='login';
在meta_0.1.1版本支持了删除操作。删除操作不需要用户在sql中指明数据的row_key的值,但是要求所操作的表在定义其元数据的json文件中,必须设置了row_key字段。connector在筛选出所要删除的数据时,会获取到数据的row_key,然后根据row_key的值删除指定的数据。sql示例如下:
delete from hbase.db_test.test_event where xwhen >= 1562139516028;
盐值就是指给每个RowKey增加一组可逆向还原的随机数字作为前缀。这样可以将数据分散到多个region存储,查询时也可以通过多线程并发查找。在presto中就可以利用这个机制将数据切分成多个split并发查找。经过验证,使用盐值可以使性能提升几十倍以上。
在组件中使用盐值需要在json文件中设置以下两个属性:
rowKeySaltUpperAndLower
该属性用来定义盐值的数值范围,如果设置为"0,29",则会从00到29依次生成30对startKey和endKey,每一对startKey和endKey会交给一个split去做数据扫描。如下:
(00, 00|)
(01, 01|)
(02, 02|)
......
(29, 29|)
rowKeySeparator
RowKey的不同组成部分之间的分隔符,默认是\001
这是指根据RowKey是由哪些字段组成的,以及当前查询的谓词来拼接查询StartKey和EndKey。
例如,当RowKey的构成如下:
xwhat-xwho
而SQL是:
select xwhat, xwho, date, xwhen from t_event_test where xwhat='login' and xwho in ('drew', 'george');
这样就会生成如下两对StartKey和EndKey:
(login-drew, login-drew|)
(login-george, login-george|)
要实现这样的查询优化机制,我们需要配置以下两个参数:
rowKeyFormat
定义RowKey是由哪些字段有序组成。以刚才的例子来说,这里应该配置为"xwhat,xwho"
rowKeySeparator
RowKey的不同组成部分之间的分隔符,默认是\001。以刚才的例子来说,这里应该配置为"-"
如果想查看sql具体切分出了哪些split,可以将日志级别设置为info,在server.log中查看。
批量get就是HBase的API中将所要查询的多个RowKey封装成一个List< Get >,然后请求这个列表以获取数据的查询方式。
这种查询方式使用起来非常便利,可以直接将要查询的RowKey作为等值匹配的查询条件放到SQL中即可。
select * from t_event_test where rk in ('rk1', 'rk2', 'rk3');
当系统解析谓词时,会根据字段名是否与RowKey字段一致判断是否执行这一查询模式。
使用这个查询模式,要求必须在表的json文件中通过isRowKey指定RowKey字段。
注意:因为我们定义的RowKey字段是虚拟字段,所以对它做除等值查询之外的其他类型的查询都是没有逻辑意义的。
ClientSideRegionScanner是HBase在0.96版本新增的Scanner,他可以在Client端直接扫描HDFS上的数据文件,不需要发送请求给RegionServer,再由RegionServer扫描HDFS上的文件。 这样减少了RegionServer的负担,并且即使RegionServer处于不可用状态也不影响查询。同时,因为是直接读取HDFS,所以在负载较为均衡的集群中,可以基本实现本地读策略,避免了很多网络负载。
下图是本组件使用ClientSideRegionScanner与普通RegionScanner的性能对比,通过比较可以得出,大部分查询都有了30%以上的提升,尤其是接近全表扫描的查询性能提升更为明显:
使用ClientSide查询需要设置以下三个参数:
hbase-rootdir
这个参数与hbase-site.xml的hbase.rootdir保持一致即可。
enable-clientSide-scan
是否开启ClientSide查询模式。
clientside-querymode-tablenames
定义哪些表需要使用ClientSide查询,表名之间用英文','间隔,例如:
namespace_a:table_a,namespace_a:table_b,namespace_b:table_c
如果所有表都要使用ClientSide查询,可以配置成*。
除以上三个参数之外,打包时还需要将运行环境中hadoop的core-site.xml和hdfs-site.xml两个配置文件拷贝到project的src/main/resources目录下
需要注意的是,ClientSideRegionScanner的查询是依赖Snapshot的,所以为了查询能获取到最新的数据,每次查询时都会自动创建一个命名规则如下的Snapshot:
"ss-" + schemaName + "." + tableName + "-" + System.nanoTime()
HBase最大可支持的Snapshot数为65536个,所以在使用ClientSideRegionScanner时最好能够做到定时清理过期Snapshot。
你需要解决以下几个问题:
这是因为在Presto的classPath中缺少hadoop-common-2.7.3.jar这个jar包。因为我们是基于ambari搭建的presto,所以需要将这个jar包拷贝到/usr/lib/presto/lib目录下。
经过定位发现Presto加载插件的类是采用自定义的PluginClassLoader,而SnappyCodec是采用AppClassLoader加载的。二者classLoader不同导致父类和子类不具备父子继承关系。
修改hbase-common-1.1.2.jar中代码,将SnappyCodec改为使用PluginClassLoader的方式加载解决了这个问题。需要修改的代码为hbase-common模块的org.apache.hadoop.hbase.io.compress.Compression类,修改方法如下:
/**
* Returns the classloader to load the Codec class from.
*/
private static ClassLoader getClassLoaderForCodec() {
/*修改前:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = Compression.class.getClassLoader();
}*/
// 修改后:
ClassLoader cl = Compression.class.getClassLoader();
if (cl == null) {
cl = Thread.currentThread().getContextClassLoader();
}
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
}
if (cl == null) {
throw new RuntimeException("A ClassLoader to load the Codec could not be determined");
}
用修改后的代码重新install maven仓库,再重新打组件jar包即可。
这是需要在jvm中增加hadoop的native snappy库。可以在presto的jvm.config中,增加如下配置:
-Djava.library.path={path to hadoop native lib}
这是因为guava在v20.0以上的版本去掉了com.google.common.base.Objects中实现的内部类ToStringHelper,以及几个toStringHelper的方法。
可以从低版本中将这些删除的代码增加到高版本的guava源码中,重新编译更新maven库中的guava-24.1-jre.jar之后,再重新构建presto-hbase.jar包。
并将guava-24.1-jre.jar上传到PrestoWorker的lib目录中。
或者使用maven的shade插件来解决这类jar包冲突的问题。
将guava的com.google.common.base.Stopwatch类中的构造函数改为public即可。
或者使用shade来解决这类jar包冲突的问题。
权限不足,因为hbase对自身的数据文件权限都是hbase用户之下,而我们通过presto查询使用的是presto用户,需要给presto用户授予读取权限。
你需要解决以下几个问题:
在presto-hbase-connector模块中增加如下dependency:
<dependency>
<groupId>com.facebook.presto.hadoop</groupId>
<artifactId>hadoop-apache2</artifactId>
<version>2.7.4-1</version>
</dependency>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。