• Apache Phoenix基本操作-2


    1. 如何映射一个Phoenix的表到一个Hbase的表?

      你可以通过Create table/create view DDL语句在一个已经存在的hbase表上创建一个Phoenix表或者视图。对于Createtable来说,我们将创建任何元数据(表,列族),这些之前都是不存在的。我们也将对于每行记录添加一个空的key值,以便查询时按照我们的方式(不需要在scan过程中对所有的列进行投影)。

       另外需要注意的是,这些序列化的字节的方式必须匹配在Phoenix中序列化字节的方式。对于Varchar,Char和Unsigned_*类型,我们使用HBase字节的方法。Char字节仅仅为单个字节,Unsigned类型的值大于或等于0。

      如果创建一个HBase的表,如下:

        create 't1', {NAME => 'f1', VERSIONS=> 5}

        你有一个HBase的表,表名为t1,列族为f1。请记住,在HBase里面,你不能模仿这KeyValues 或者行键的结构,你在Phoenix中指定的信息是建立在表和列族之上的。

       因此在Phoenix中,你可以创建一个对应的视图,如下:

        CREATE VIEW "t1" ( pk VARCHAR PRIMARY KEY, "f1".val VARCHAR );

        pk列定义了你的行键的类型为VARCHAR类型,"f1".val列定义了你的HBase表包含KeyValues ,包含一个列族和列修饰符"f1".val,并且他们的值是Varchar类型的。

       如果你创建HBase表时,都使用大写名称,如下:

        create 'T1', {NAME => 'F1', VERSIONS=> 5}

      你可以创建对应的Phoenix视图,可以不需要双引符号:

        CREATE VIEW t1 ( pk VARCHAR PRIMARY KEY,f1.val VARCHAR )

      或者你创建一个新的HBase表,仅仅让Phoenix为你如下的一切事情(根本不需要使用HBase shell):

        CREATE TABLE t1 ( pk VARCHAR PRIMARY KEY,val VARCHAR )

      这里补充一点Family Column:

        如果在Phoenix SQL中创建table时为qualifier指定了family,如下:

          CREATE TABLE "example" (my_pk bigint notnull, m1.col varchar(50), m2.col varchar(50), m3.col varchar(50) constraint pk primary key (my_pk)) ;

      Phoenix primary key 与HBase rowkey 的关系:

      在创建phoenix table时,必须指定一个primary key,但是这个主键我们不一定用到。如果我们想在用Phoenix创建table时,让Phoniex的主键自动地与HBase的rowkey对应起来,则可以用以下方式来创建:

      create table"{空间名:表名}"  ("pk"varchar primary key, "{列1}" varchar, "{列2}" varchar, "{列3}" varchar)  salt_buckets=10;

      这样,Phoniex的主键(名为pk)就自动地与HBase的rowkey对应起来了。

      注:

      (1)自己定义的HBase中的 HTableName,ColumnFamily,以及Column,需要和Phoenix中保持一致。(最好都用大写)

      (2)Phoenix操作HBase,我们有两种方式,创建表和创建视图。

      这两种方式的区别如下:

        创建表的话,可读可写,就可以对HBase进行插入,查询,删除操作。

        创建视图的话,是只读的,一般就只可以进行查询操作

        删除Phoenix中表时,会将HBase对应的表删掉。但是删除Phoenix视图操作,却不会影响HBase原始表的结构。

        因为使用Phoenix,创建表后,会自动和HBase建立关联映射。当你使用Phoenix删除和HBase之间的关系时,就会将HBase中的表也删掉了。所以用视图,会对原始的HBase表影响小一些。

    2. 优化Phoenix有哪些提示?

      2.1   使用Salting方式提升读写性能

        Salting能够通过预分区(pre-splitting)数据到多个region中来显著提升读写性能。

        Salting 翻译成中文是加盐的意思,本质是在hbase中,rowkey的byte数组的第一个字节位置设定一个系统生成的byte值,这个byte值是由主键生成rowkey的byte数组做一个哈希算法,计算得来的。Salting之后可以把数据分布到不同的region上,这样有利于phoenix并发的读写操作。

        CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, DESCRIPTION  VARCHAR) SALT_BUCKETS=16;

       注:

      (1)理性情况下,对于拥有四核CPUs的16个RegionServer的HBase集群来说,选择salt buckets在32-64个以获取较好的性能。Salt bucket的个数必须在1-256之间。

      (2)这里说明一下Salted Tables

        为了避免读写HBase表数据时产生hot-spot问题,我们使用Phoenix来创建表时可以采用salted table。

        salted table可以自动在每一个rowkey前面加上一个字节,这样对于一段连续的rowkeys,它们在表中实际存储时,就被自动地分布到不同的region中去了。当指定要读写该段区间内的数据时,也就避免了读写操作都集中在同一个region上。

        简而言之,如果我们用Phoenix创建了一个saltedtable,那么向该表中写入数据时,原始的rowkey的前面会被自动地加上一个byte(不同的rowkey会被分配不同的byte),使得连续的rowkeys也能被均匀地分布到多个regions。

      创建salted table:

        CREATE TABLE "test:salted" (pk VARCHAR PRIMARY KEY, c1 VARCHAR, c2 VARCHAR, c3 VARCHAR) SALT_BUCKETS=5;

      上述语句创建了一个名为"test:salted"的table(HBase中事先要创建名为test的namespace),SALT_BUCKETS=5 说明该salted table由5个bucket组成(必须在1~256之间)。

      上面在创建table时,没有指定family,只指定了qualifier(c1, c2, c3),因此在HBase shell向表salted写入数据时,column name 要写成'0:[qualifier]'(如'0:c1'),否则HBase会报错。

       另外,创建salted table需要注意两点:

        A.   创建salted table后,应该使用Phoenix SQL来读写数据,而不要混合使用Phoenix SQL和HBase API

        B.   如果通过Phoenix创建了一个salted table,那么只有通过Phoenix SQL插入数据才能使得被插入的原始rowkey前面被自动加上一个byte,通过HBase shell插入数据无法prefix原始的rowkey

      2.2 Pre-split

        Salting能够自动的设置表预分区,但是你得去控制表是如何分区的,所以在建phoenix表时,可以精确的指定要根据什么值来做预分区,比如:

        CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, DESCRIPTION VARCHAR) SPLIT ON ('CS','EU','NA');

       2.3 使用多列族

        列族包含相关的数据都在独立的文件中,在Phoenix设置多个列族可以提高查询性能。例如:

        CREATE TABLE TEST (MYKEY VARCHAR NOT NULL PRIMARY KEY, A.COL1 VARCHAR,A.COL2 VARCHAR, B.COL3 VARCHAR);

      2.4 使用压缩

        在数据量大的表上使用压缩算法来提高性能。

        例如:

        CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, DESCRIPTION VARCHAR) COMPRESSION='GZ';

      2.5 创建二级索引

        下面将会介绍。

      2.6 优化HBase集群参数

        请参考HBase官方详细介绍:http://hbase.apache.org/book.html#performance

      2.7 优化Phoenix参数

        请参考官网介绍:http://phoenix.apache.org/tuning.html

    3. 如何在表上创建二级索引?

      从Phoenix 2.1版本开始,Phoenix支持可变和不可变(数据插入后不再更新)数据建立二级索引。Phoenix 2.0版本仅支持在不可变数据建立二级索引。

      示例:

      3.1   创建表

        不可变表:create table test (mykey varchar primary key, col1 varchar,col2 varchar) IMMUTABLE_ROWS=true;

         可变表:create table test (mykey varchar primary key, col1 varchar,col2 varchar);

      3.2   在col2创建索引

        create index idx on test (col2);

      3.3   在col1和一个覆盖列col2上创建索引

        create index idx on test (col1) include (col2);

       在test表中upsert数据,Phoenix查询优化器将选择使用合适的索引。你可以使用explain plan进行查看(http://phoenix.apache.org/language/index.html#explain)。当然你也可以使用hint(http://phoenix.apache.org/language/index.html#hint)方式指定一个需要使用的索引。

    4. 为什么我的二级索引没有被使用?

      除非所有查询使用的列被索引或者覆盖列,否则二级索引不会被使用。所有列组成数据表的主键将被包含在索引中。

      例子:

      创建表:

        create table usertable (id varchar primary key,firstname varchar, lastname varchar);

        create index idx_name on usertable (firstname);

      这里创建二级索引时报错了如下:

                  Java.sql.SQLException: ERROR 1029 (42Y88):Mutable secondary indexes must have the hbase.regionserver.wal.codec propertyset to org.apache.Hadoop.hbase.regionserver.wal.IndexedWALEditCodec in thehbase-sites.xml of every region server. tableName=IDX

      根据错误日志,需要在RegionServer每个节点的hbase-site.xml中配置支持可变的二级索引的配置项:

      <property>

        <name>hbase.regionserver.wal.codec</name>

        <value> org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>

      </property>

      <property>

        <name>hbase.region.server.rpc.scheduler.factory.class</name> 

        <value>org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory</value>

        <description>Factory to create the Phoenix RPC Scheduler that usesseparate queues for index and metadata updates</description>

      </property>

      <property>

        <name>hbase.rpc.controllerfactory.class</name>

        <value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory</value>

        <description>Factory to create the Phoenix RPCScheduler that uses separate queues for index and metadataupdates</description>

      </property>

       <property>

        <name>hbase.coprocessor.regionserver.classes</name> 

        <value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value>

      </property>

       在每一个Master的hbase-site.xml中加入如下的属性:

      <property>

          <name>hbase.master.loadbalancer.class</name>

        <value>org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer</value>

      </property>

      <property>

        <name>hbase.coprocessor.master.classes</name>

         <value>org.apache.phoenix.hbase.index.master.IndexMasterObserver</value>

      </property>

      查询:

        select id, firstname, lastname from usertablewhere firstname = 'foo';

      请注意这个查询不会用到索引,因为lastname不是被索引或者覆盖列的部分。我们可以查询执行计划进行验证,比如:

      0:jdbc:phoenix:SZB-L0023780:2181:/hbase114> explain select id, firstname,lastname from usertable where firstname = 'foo';

      +------------------------------------------------------------------------------------------------------------------+

      |                                PLAN                                 |

      +------------------------------------------------------------------------------------------------------------------+

      | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBINFULL SCAN OVER USERTABLE     |

      |    SERVER FILTER BY FIRSTNAME = 'foo'                                   |

      +------------------------------------------------------------------------------------------------------------------+

      为了修改这个创建索引的问题,要么对lastname创建索引,要么创建覆盖索引,如下:

        drop index idx_name on usertable;

        create index idx_name on usertable (firstname) include(lastname);

      我们再次验证一下

        0: jdbc:phoenix:SZB-L0023780:2181:/hbase114>explain select id, firstname, lastname from usertable where firstname = 'foo';

        +-------------------------------------------------------------------------------------------------------------------------+

        |                                PLAN                                     |

        +-------------------------------------------------------------------------------------------------------------------------+

        | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBINRANGE SCAN OVER IDX_NAME ['foo']  |

        +------------------------------------------------------------------------------------------------------------------------+

        根据执行计划的结果,可以看出使用了IDX_NAME索引。

    5. Phoenix有多快,为什么如此之快?

      全表扫描100M的数据量通过在20s完成(中等大小集群的窄表)。如果查询时增加一些索引列过滤的话,时间会减低到毫秒级别。

      为什么Phoenix全表扫描还那么快?

      (1)Phoenix使用region边界拆分查询,并且客户端使用配置的线程数进行并行查询。

      (2)聚合操作将在服务端的协处理器中完成

    6. 为什么我的查询不走Range scan?

      首先看一下创建表的语句:

        CREATE TABLE TEST (pk1 char(1) not null, pk2char(1) not null, pk3 char(1) not null, non_pk varchar CONSTRAINT PK PRIMARY KEY(pk1, pk2, pk3));

       Range Scan意味着仅会扫描表的一部分子集的行记录。如果你使用主键约束中的首个列开头的话,就会使用Range Scan方式。

      比如下面的查询将会使用Full Scan方式:

        select * from test where pk2='x' and pk3='y';

       而下面的查询将会使用Range Scan方式:

        select * from test where pk1='x' and pk2='y';

      DEGENERATE SCAN意味着一个查询不可能返回任何行记录。

      FULL SCAN意味着将扫描表的所有行记录。

      SKIP SCAN意味着要么表的部分子集,要么扫描表的所有记录,然而它将根据过滤条件跳过大量的记录。在一些情况下,当你的leading primary key columns的基数比较低时,它往往比FULL SCAN更有效率。

    7. 我应该缓存Phoenix JDBC连接吗?

      No,没有必要缓存Phoenix JDBC连接。

      Phoenix的连接对象不同于绝大部分的JDBC连接,因为其是底层是HBase连接的。如果Phoenix连续被重用,有可能之前使用的用户留下的HBase连接状态并不是良好的,所以最好还是新创建一个Phoenix连接,可以避免潜在的问题。

    8. 为什么执行upsert操作时,Phoenix添加一个empty/dummy的KeyValue?

      我们先来看一下示例:

        create table HADOOP (rk Integer primarykey,info.name varchar ,info.score Integer );

        upsert into HADOOP values(1,'Hadoop',90);

      查看HBase的表内容:

        hbase(main):037:0> scan 'HADOOP'

        ROW                 COLUMN+CELL                                                                                                                                  

         x80x00x00x01   column=INFO:NAME, timestamp=1472634053972,value=Hadoop

         x80x00x00x01   column=INFO:SCORE, timestamp=1472634053972,value=x80x00x00Z

         x80x00x00x01   column=INFO:_0, timestamp=1472634053972,value=x

      我们再通过Phoenix插入一行记录:

        upsert into HADOOP values(2,'Spark',95);

       再次查看HBase的表内容:

        hbase(main):037:0> scan 'HADOOP'

        ROW                 COLUMN+CELL                                                                                                                                  

         x80x00x00x01   column=INFO:NAME, timestamp=1472634053972, value=Hadoop

         x80x00x00x01   column=INFO:SCORE, timestamp=1472634053972,value=x80x00x00Z

         x80x00x00x01   column=INFO:_0, timestamp=1472634053972,value=x

         x80x00x00x02   column=INFO:NAME, timestamp=1472634140841,value=Spark

         x80x00x00x02   column=INFO:SCORE, timestamp=1472634140841,value=x80x00x00_

         x80x00x00x02   column=INFO:_0, timestamp=1472634140841,value=x

       可以发现针对每一行,都会增加一列_0

        empty/dummy的KeyValue(列修饰符_0)是需要的,为了确保给定的列能够被所有行访问,这是从提升查询性能方面考虑的。

      据你所知,数据是以KeyValues形式存储在HBase中的,意味着全部的行键存储着每个列值。这也意味着除非至少有一个列存储,否则行键根本不存储数据。

      这部分内容,后续我们有时间从源码层面给大家说明一下。

    9. 如何将带有Schema的表移到namespace中?

      前提条件:需要4.8以及以上版本去映射表到namespace。具体相关配置请查看官方文档:http://phoenix.apache.org/tuning.html

       对于基于Kerberos的环境来说,运行的用户需要有admin权限创建namespace。

      表将仅仅映射到名称为schema_name的namespace中,目前不支持迁移已经存在的表到不同的schema或者namespace。  

      我们来看一下测试过程:

        [kylin@SZB-L0023780apache-phoenix-4.8.0-HBase-1.1-bin]$ bin/psql.py SZB-L0023780:2181:hbase114 -mmyworld.HADOOP

        Starting upgrading table:myworld.HADOOP...please don't kill it in between!!

        java.lang.IllegalArgumentException:phoenix.schema.isNamespaceMappingEnabled is not enabled!!

                 atorg.apache.phoenix.util.UpgradeUtil.upgradeTable(UpgradeUtil.java:1717)

                 atorg.apache.phoenix.util.PhoenixRuntime.main(PhoenixRuntime.java:221) 

      可以看到需要设置参数允许namespace mapping。

      <property>

        <name>phoenix.schema.isNamespaceMappingEnabled</name>

        <value>true</value>

      </property>

      这里一定要注意:如果设置为true,创建的带有schema的表将映射到一个namespace,这个需要客户端和服务端同时设置。一旦设置为true,就不能回滚了。旧的客户端将无法再正常工作。所以建议大家都查看官方文档,确定后再进行设置。

      示例:将表“table_name”移动到“schema_name”的namespace中。

      bin/psql.py <zookeeper> -m<schema_name>.<table_name>

  • 相关阅读:
    CMS之图片管理(4)
    CMS之图片管理(2)
    CMS:文章管理之控制器
    CMS:文章管理之视图(1)
    CMS:文章管理之视图(3)
    CMS:文章管理之视图(2)
    影响Incremental checkpoint position的条件
    oracle中一个数据文件的最大值(非lob)
    X$KCCLE
    图一时之快:手动释放Linux服务器内存
  • 原文地址:https://www.cnblogs.com/sh425/p/7274150.html
Copyright © 2020-2023  润新知