hbase的sql解决方案——phoenix初尝试,从入门到放弃

原创
2020/12/02 14:46
阅读数 256

    最近的需求是希望能用mysql、sqlserver这样的sql来进行hbase的数据查询,这样不但查询比起hbase自带的查询语言更符合使用习惯,也能够整合mybatis、hibernate的框架,写起来不用考虑这么多。我上网一搜:“hbase  sql查询”,phoenix有最多的教程贴。被这么多人使用,肯定有它的独到之处,并且劣势不会那么难以接受,那么我先用phoenix试试效果,看看感受如何。

    首先phoenix的安装并不困难,简单来说下载对应hbase版本的phoenix,解压之后把对应包放入每个hbase RegionServer的lib下面,重启hbase就可以了。我的hadoop是cdh 6.3.0版本,在安装的时候没有遇到什么困难。之后进入phoenix的安装位置的bin目录,执行sqlline.py,就能在shell中进入phoenix的使用界面了。

安装成功,先试试phoenix的一些基础操作,比如!tables,create table等等。都没有问题,使用习惯也和mysql差不多。比较大的不同之处在于插入和更新都用的是upsert,这和hbase也是比较类似的。

    在linux上使用也没有问题,那么接下来就是使用javaApi进行连接。连接方式也并不困难,和其它组件差不多,配置连接信息然后就能连接了。具体怎么配网上教程很多,我就不做搬运工了。随便找个链接【十二】Phoenix Java API操作        同质化内容那么多,总要说点自己的东西。值得一提的是phoenix是用jdbc连接的,配置看上去就和mysql的连接一样,显然这是十分友好的。在按照上面教程操作过后,我感觉十分欣喜,不愧是众多教程所推荐的中间件,使用起来太舒爽了(除了手动加两个phoenix的包,也不知道能不能在pom文件里面配置自动加载这样不值一提的小问题),就连我这样的小白都能轻松使用。不过教程上面的表都是直接建立在默认命名空间的,假如我想建立在指定命名空间,那应该怎么办呢?

  按照之前的习惯,我直接将表名改为 命名空间.表名,但是报了错,表示没有找到namespace.table这张表。是因为我用的.出错了吗?我将.改为:,但还是报错,报错信息只是将namespace.table改为namespace:table,说明错误类型都是一样的,错误不在这里。

  上网搜索“phoenix  指定命名空间”,大量的帖子告诉了我一件事:phoenix想要使用命名空间映射,需要额外的配置。这里要说一下,很多教程写的不完全一样,有的多一些有的少一些,按照经验来说,多配置一些更不容易出问题。给出一个配置比较全的帖子:HBase - Phoenix的安装使用教程3(SCHEMA的启用、操作、关闭)  。在我看各种关于如何开启phoenix命名空间映射帖子的时候,有些帖子提到这个功能不但无法正常使用,反而还把原本正常的功能弄出问题了。关于如何关闭在上面这个帖子里面也有,我在使用过程中没有遇到,给大家看看以防万一。

    我当时看的帖子是cdh版的,帖子现在找不到了,配置内容是在web页面完成的,配置内容更多一些,我把完成后的结果给大家看看,步骤基本都差不多,方法不同而已。

分别在客户端和服务端配置信息,并将这两个配置写入phoenix的bin目录的hbase.site.xml文件内。

    之后重启hbase,在shell中进入phoenix,就能够使用create schema 来创建命名空间了。

    再试试用java api来连接,先将之前下载的hbase.site.xml换成现在的,然后再次连接,报错配置信息错误,和我之前新加的phoenix配置有关。感觉像是我哪里配置没配好,找半天也找不到。本着先能用的方式在创建phoenix连接的时候手动加入配置:

Properties pros = new Properties();
pros.setProperty("phoenix.schema.isNamespaceMappingEnabled", "true");
pros.setProperty("phoenix.schema.mapSystemTablesToNamespace", Boolean.toString(true));

之后再创建的连接就没有问题了。能够直接查询多个命名空间下面的表,相当于跨库查询。要注意的一点是:当你使用shell进入phoenix创建命名空间下面的表时,需要先use schema,然后才能创建表。但是在你使用java api进行创建的时候就不需要这么麻烦,直接 create table schema.table即可,不需要像shell中一样那么麻烦。假如像shell中一样先use schema 再create table反而会报错。关于这一点我倒是没看到有教程提到,明明这才是比较有价值的东西。

举个栗子:

public static void selectTable() throws SQLException{
    	String sql = "select * from ENTERPRISE_INFORMATION.TEST_PHOENIX_API a left join ENTERPRISE_INFORMATION.TEST_PHOENIX_API2 b on a.mykey = b.mykey ";
    	rs = stat.executeQuery(sql);
    	JSONArray array = resultSetToJosnArray(rs);
    	System.out.println(array.toJSONString());
    }
    
    public static void testCreateTable() throws SQLException {
        String sql="create table ENTERPRISE_INFORMATION.test_phoenix_api2(mykey integer not null primary key ,mycolumn varchar )";
        stat.executeUpdate(sql);
        conn.commit();
    }
 
    public static void upsert() throws SQLException {
        String sql1="upsert into ENTERPRISE_INFORMATION.test_phoenix_api2 values(1,'test4')";
        String sql2="upsert into ENTERPRISE_INFORMATION.test_phoenix_api2 values(2,'test5')";
        String sql3="upsert into ENTERPRISE_INFORMATION.test_phoenix_api2 values(3,'test6')";
        stat.executeUpdate(sql1);
        stat.executeUpdate(sql2);
        stat.executeUpdate(sql3);
        conn.commit();
    }
 
    public static void delete() throws SQLException {
        String sql1="delete from ENTERPRISE_INFORMATION.test_phoenix_api where mykey = 1";
        stat.executeUpdate(sql1);
        conn.commit();
    }
 
    public static void deleteTable() throws SQLException {
    	String sql = "drop table ENTERPRISE_INFORMATION.test_phoenix_api";
    	stat.executeUpdate(sql);
    	conn.commit();
    	

    创建连接之后,执行的表名直接使用schema.table即可,不加双引号的情况下,无论大小写都会被转换成大写。在执行select的时候,要使用rs = stat.executeQuery(sql);在增删改的时候,使用stat.executeUpdate(sql)。假如在增删改的时候使用executeQuery,可能会报错。另外还要注意一点,phoenix在识别string类型数据的时候要使用单引号而不是双引号,双引号会报错无法识别,这一点无论是在shell还是java api执行的时候都是一样的,比如我上面的sql:

"upsert into ENTERPRISE_INFORMATION.test_phoenix_api2 values(1,'test4')";

test4是用单引号包起来的,这一点一定要注意。

    我之前使用的是squirrel-3.7.1来进行phoenix的图形化界面操作。配置比较简单,需要把phoenix的驱动加入,之后写下phoenix的jdbc连接就可以了。使用还算,并且能够连接多种数据库。但是在我进行phoenix命名空间映射后,就无法连接上了。报错和我之前通过java api连接报错一样,缺少关于phoenix.schema的配置。这一点没法解决,我真找不到哪里的配置有问题。网上大把的教程没能解决我这个问题,最后我只有放弃squirrel,而是使用shell来操作phoenix。

    

    以上都是小问题,还在我的接受范围之内。那么接下来就是要将phoenix和hbase已有的表进行映射。按照我对phoenix的理解,phoenix在使用上是和mysql一样的,schema映射为命名空间,table假如没有就在hbase中新建一个表并且将它们映射在一起,我操作phoenix中的表就能将修改转换成hbase的语句;假如table和hbase已有表名一样那么就新建映射,操作还是一样的。那么最好hbase中的表名本身就是大写,我就不用特地用双引号修饰表名,写起来方便很多。

    事实上我的想法没有错,只要映射的表名相同,那么就能够通过phoenix来修改hbase的数据。但是当我通过phoenix修改已有数据的时候,我发现了一件奇怪的事——phoenix不能看见hbase已有数据,但是假如把phoenix表删掉的话,hbase表也会一起被删掉,这说明映射是成功的。但是为什么在phoenix中会看不到数据呢?在hbase中是能够看到的数据的,当我尝试用phoenix往映射表中加数据,再在hbase中查看数据时,发现了一件奇怪的事:

hbase创表(通过sqoop将mysql表导入hbase):

sqoop import \
--connect jdbc:mysql://192.168.49.201:3306/envir \
--username root \
--password Hskj123456! \
--table hs_apply_detailinfo_r  \
--hbase-table ENVIR:HS_APPLY_DETAILINFO_R \
--m 1 \
--column-family 0  \
--hbase-create-table \
--hbase-row-key ID

phoenix创表:

CREATE TABLE envir.hs_apply_detailinfo_r (
  ID varchar primary key,
  APPLY_TYPE varchar ,
  APPLY_ID varchar ,
  MAT_CD varchar ,
  MAT_NUM double ,
  PRODUCE_TIME date ,
  OUTDATE_TIME date,
  SUP_CD varchar
)

phoenix修改数据:

upsert envir.hs_apply_detailinfo_r (ID,APPLY_ID) values ('10','2');

    在phoenix中查看envir.hs_apply_detailinfo_r的数据,能看到新加的数据,并且只有这一条,hbase导入的表的数据是没有的。再到hbase中scan一下,发现了表中新加了数据,而不是修改原来row为10 ,列为0:APPLY_ID的单元格,而是新加了一个单元格,列限定符为十六进制码,而不是我在phoenix中输入的APPLY_ID;并且值也不是我所输入的字符串2,而是一串十六进制码。在hue中,我也无法查看由phoenix映射的表的数据。

    这个问题比较大,直接影响到我是否会继续使用phoenix,但是我又不是很明白哪里出了问题,在网上一直搜索,大概知道了问题出在phoenix在保存字符的时候处理方式和hbase是不一样的,假如phoenix表字段要映射到hbase表字段,要加上column_encoded_bytes=0;并且字段属性要修改,能用unsigned_的尽量用unsigned_。

Phoenix 映射已存在 HBase 表,查询不到数据

phoenix映射hbase中数据类型问题

所以phoenix表的创建应该改为:

CREATE TABLE envir.hs_apply_detailinfo_r(
  "ID" varchar primary key,
  "info"."APPLY_TYPE" VARBINARY,
  "info"."APPLY_ID" VARBINARY,
  "info"."MAT_CD" varchar,
  "info"."MAT_NUM" UNSIGNED_DOUBLE,
  "info"."PRODUCE_TIME" UNSIGNED_DATE,
  "info"."OUTDATE_TIME" UNSIGNED_DATE,
  "info"."SUP_CD" varchar)column_encoded_bytes=0;

    我将列族改为了info,所以上面用sqoop导入的时候也应该将--column-family改为info。0是在phoenix不指定列族的时候默认列族。

    修改之后,表字段映射的问题解决了,我能修改row=10,列为info:APPLY_TYPE单元格的值了。但是数值的问题依然没有解决,我在phoenix输入的值为‘2’,在hbase查看的结果依然是十六进制码。这个问题有点无解,太底层了,我看了phoenix官网给出的关于数据类型转换的介绍(http://phoenix.apache.org/language/datatypes.html) ,里面没有解决办法。其它网友也遇到了类似的问题,给出的解决办法是:当用hbase存储的时候,先用Bytes.toBytes()压缩成phoenix的形式,这样就能用phoenix来获得hbase所插入的数据了。

    此外,phoenix存储hbase的int时会有问题,会报错字段长度不够。还是以上的hbase表和phoenix表,假如我先创建phoenix表,再通过sqoop将数据导入hbase,就会报错字段长度不够(hbase-presto-phoenix遇到的坑)。那么假如要使用phoenix,我就需要放弃很多东西,数据的导入不能用sqoop导入,而要手写map/reduce;数据的查询修改都要使用phoenix,不然无法正确识别。从mysql向HBase+Phoenix迁移数据的心得总结   中写了一些解决方案,但是我看不懂,对我来说太难了,而且太麻烦了。与其继续坚持下去,不如换个合适的中间件来满足需求。

    我同事用了antdb(antdb官网),我大概看了它的使用过程,和phoenix感觉差不多,创建一个虚拟的mysql来映射hbase表,让用户可以像修改mysql一样修改hbase数据。但是不能设置主键,一些像phoenix的特性不知道有没有,并且在我使用过程中RegionServer直接被停了,不知道什么原因。感觉太不成熟,也没有什么教程贴来教教我如何用,还是当做备用方案,先去看看simplehbase如何使用吧。

展开阅读全文
打赏
0
0 收藏
分享
加载中
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部