From ce0da37a38febb1096c7776d06bd871ad4a187c9 Mon Sep 17 00:00:00 2001 From: "wupeng@analysys.com.cn" Date: Fri, 31 May 2019 15:15:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BD=BF=E7=94=A8ClientSideR?= =?UTF-8?q?egionScanner=E7=9A=84=E6=9F=A5=E8=AF=A2=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 198 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 165 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index bebbb62..4bc4178 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ ## 性能对比 -| 环境 | 明细 | -| ------ | ------------------------- | -| 数据量 | 事件表500万条数据,90个字段 | -| 节点数 | 3 | -| 硬件 | 16逻辑核,64G内存,4T*2硬盘。给HBase分配了16G内存,读写各占40% | +| 环境 | 明细 | +| ------ | ------------------------------------------------------------ | +| 数据量 | 事件表500万条数据,90个字段 | +| 节点数 | 3 | +| 硬件 | 16逻辑核 64G内存(其中Presto和RegionServer各占16G内存) 4T*2硬盘 | ![analysys-hb-performance.png](https://github.com/analysys/presto-hbase-connector/blob/master/imgs/analysys-hb-performance.png?raw=true) @@ -18,16 +18,16 @@ ## 功能点对比 -| 功能点 | 易观 | 其他 | -| ------------------- | ------------ | ------ | -| 加盐查询 | 支持 | 不支持 | -| 拼接StartKey/EndKey | 支持 | 不支持 | -| 批量Get查询 | 支持 | 不支持 | -| 谓词下推(Filter) | 支持 | 不支持 | -| ClientSideScan | 商业版已实现 | 不支持 | -| Insert | 后续支持 | 支持 | -| Delete | 后续支持 | 支持 | -| 建表语句 | 后续支持 | 支持 | +| 功能点 | 易观 | 其他 | +| ------------------- | -------- | ------ | +| 加盐查询 | 支持 | 不支持 | +| 拼接StartKey/EndKey | 支持 | 不支持 | +| 批量Get查询 | 支持 | 不支持 | +| 谓词下推(Filter) | 支持 | 不支持 | +| ClientSideScan | 支持 | 支持 | +| Insert | 后续支持 | 支持 | +| Delete | 后续支持 | 支持 | +| 建表语句 | 后续支持 | 支持 | @@ -64,41 +64,41 @@ meta-dir=/etc/presto/chbase * connector.name - ​ 该配置固定设置为hbase + 该配置固定设置为hbase * zookeeper-quorum - ​ 相当于HBase API的hbase.zookeeper.quorum参数 + 相当于HBase API的hbase.zookeeper.quorum参数 * zookeeper-client-port - ​ 相当于HBase API的hbase.zookeeper.property.clientPort参数 + 相当于HBase API的hbase.zookeeper.property.clientPort参数 * hbase-cluster-distributed - ​ 相当于HBase API的hbase.cluster.distributed参数 + 相当于HBase API的hbase.cluster.distributed参数 * presto-workers-name - ​ presto worker的hostname,以英文逗号间隔。 - - ​ 如果split-remotely-accessible配置为false,则该参数可以不设置。 + presto worker的hostname,以英文逗号间隔。 + + 如果split-remotely-accessible配置为false,则该参数可以不设置。 * presto-server-port - ​ 与{Presto_Config_Dir}/config.properties配置文件的http-server.http.port参数 + 与{Presto_Config_Dir}/config.properties配置文件的http-server.http.port参数 * random-schedule-redundant-split - ​ Presto默认采用按可用Worker的顺序,依次分配Split的方式调度Split。 - - ​ 这样容易导致多表查询时,第余数个Split集中调度到可用Worker列表开头的几台机器上。 - - ​ 将这个参数设置为true可以改为将第余数个Split随机调度到一个Worker上执行。 + Presto默认采用按可用Worker的顺序,依次分配Split的方式调度Split。 + + 这样容易导致多表查询时,第余数个Split集中调度到可用Worker列表开头的几台机器上。 + + 将这个参数设置为true可以改为将第余数个Split随机调度到一个Worker上执行。 * meta-dir - ​ 存放HBase表元数据信息的目录。 + 存放HBase表元数据信息的目录。 ##### 2.配置namespace @@ -215,7 +215,7 @@ mvn clean package 在组件中使用盐值需要在json文件中设置以下两个属性: * rowKeySaltUpperAndLower - ​ 该属性用来定义盐值的数值范围,如果设置为"0,29",则会从00到29依次生成30对startKey和endKey,每一对startKey和endKey会交给一个split去做数据扫描。如下: + 该属性用来定义盐值的数值范围,如果设置为"0,29",则会从00到29依次生成30对startKey和endKey,每一对startKey和endKey会交给一个split去做数据扫描。如下: ``` (00, 00|) @@ -229,7 +229,7 @@ mvn clean package * rowKeySeparator - ​ RowKey的不同组成部分之间的分隔符,默认是\001 + RowKey的不同组成部分之间的分隔符,默认是\001 ##### 2.根据RowKey的组成拼接StartKey和EndKey @@ -258,11 +258,11 @@ select xwhat, xwho, date, xwhen from t_event_test where xwhat='login' and xwho i * rowKeyFormat - ​ 定义RowKey是由哪些字段有序组成。以刚才的例子来说,该参数应该配置为"xwhat,xwho" + 定义RowKey是由哪些字段有序组成。以刚才的例子来说,该参数应该配置为"xwhat,xwho" * rowKeySeparator - ​ RowKey的不同组成部分之间的分隔符,默认是\001 + RowKey的不同组成部分之间的分隔符,默认是\001 如果想查看sql具体切分出了哪些split,可以将日志级别设置为info,在server.log中查看。 @@ -281,3 +281,135 @@ select * from t_event_test where rk in ('rk1', 'rk2', 'rk3'); 使用这个查询模式,要求必须在表的json文件中通过isRowKey指定RowKey字段。 注意:因为我们定义的RowKey字段是虚拟字段,所以对它做除等值查询之外的其他类型的查询都是没有逻辑意义的。 + +##### 4.ClientSideRegionScanner + +ClientSideRegionScanner是HBase在0.96版本新增的Scanner,他可以在Client端直接扫描HDFS上的数据文件,不需要发送请求给RegionServer,再由RegionServer扫描HDFS上的文件。 +这样减少了RegionServer的负担,并且即使RegionServer处于不可用状态也不影响查询。同时,因为是直接读取HDFS,所以在负载较为均衡的集群中,可以基本实现本地读策略,避免了很多网络负载。 + +下图是ClientSideRegionScanner与普通RegionScanner的性能对比,通过比较可以得出,大部分查询都有了30%以上的提升,尤其是接近全表扫描的查询性能提升更为明显: + +![ClientSide&NormalScanner.png](https://github.com/analysys/presto-hbase-connector/blob/dev_0.1.1/imgs/ClientSide-NormalScanner.png?raw=true) + +详细测试结果参见:https://github.com/analysys/public-docs/blob/master/Attachment6(Presto-HBase-Connector-PerformanceTesting-ClientSide).xlsx + +使用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。 + +## 问题解决 + +##### 1.如何让ClientSideRegionScanner可以查询Snappy压缩格式的HBase表? + +你需要解决以下几个问题: + +###### 1) SnappyCodec找不到的问题 + +这是因为在Presto的classPath中缺少hadoop-common-2.7.3.jar这个jar包。因为我们是基于ambari搭建的presto,所以需要将这个jar包拷贝到/usr/lib/presto/lib目录下。 + +###### 2) SnappyCodec无法转换为CompressionCodec的问题 + +经过定位发现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包即可。 + +###### 3) java.lang.UnsatisfiedLinkError: org.apache.hadoop.util.NativeCodeLoader.buildSupportsSnappy()Z + +这是需要在jvm中增加hadoop的native snappy库。可以在presto的jvm.config中,增加如下配置: + +``` +-Djava.library.path={path to hadoop native lib} +``` + +###### 4) java.io.IOException: java.lang.NoSuchMethodError: com.google.common.base.Objects.toStringHelper(Ljava/lang/Object;)Lcom/google/common/base/Objects$ToStringHelper; + +这是因为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包冲突的问题。 + +###### 5) Stopwatch的构造函数找不到 + +将guava的com.google.common.base.Stopwatch类中的构造函数改为public即可。 + +或者使用shade来解决这类jar包冲突的问题。 + +###### 6)Caused by: org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.AccessControlException): Permission denied: user=presto, access=WRITE, inode="/apps/hbase/data/data/db_moredatatest/multifamily_90cq_7cf_500w_snappy2/dee9b34c5cd8ee34f74ff5fc5446432a/.tmp":hbase:hdfs:drwxr-xr-x +权限不足,因为hbase对自身的数据文件权限都是hbase用户之下,而我们通过presto查询使用的是presto用户,需要给presto用户授予读取权限。 + +##### 2.如何在Idea中debug开发ClientSideRegionScanner查询以Snappy格式进行压缩的HBase表? + +你需要解决以下几个问题: + +###### 1) 找不到类CanUnbuff + +在presto-hbase-connector模块中增加如下dependency: + +``` + + com.facebook.presto.hadoop + hadoop-apache2 + 2.7.4-1 + +``` + +###### 2) 使用hbase-shaded-client和hbase-shaded-server依赖 + +###### 3) 参考“SnappyCodec无法转换为CompressionCodec的问题”部分,修改hbase-common模块的代码,并重新编译更新maven库。其中hbase-shade-client、hbase-shade-server和hbase-common这三个模块必须重新编译。 + +###### 4) 在idea的run->Edit Configuration中配置-Djava.library.path到PrestoServer的VM options中。java.library.path就是hadoop的native snappy库路径。