phoenix 使用说明

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cheungmine/article/details/80832903

phoenix 使用说明

2018-06-14~2018-06-19

Apache Phoenix是构建在HBase之上的关系型数据库层,作为内嵌的客户端JDBC驱动用以对HBase中的数据进行低延迟访问。Apache Phoenix会将用户编写的sql查询编译为一系列的scan操作,最终产生通用的JDBC结果集返回给客户端。数据表的元数据存储在HBase的表中被会标记版本号,所以进行查询的时候会自动选择正确的schema。直接使用HBase的API,结合协处理器(coprocessor)和自定义的过滤器的话,小范围的查询在毫秒级响应,千万数据的话响应速度为秒级。


1. 为 CDH 安装 phoenix

Phoenix Downloads

下载链接:

4.14.0-cdh5.14.2

解压到每个节点并执行:

# cp apache-phoenix-4.14.0-cdh5.14.2-bin/phoenix-4.14.0-cdh5.14.2-server.jar /opt/cloudera/parcels/CDH/jars/

# cd /opt/cloudera/parcels/CDH/lib/hbase/lib
# ln -s ../../../jars/phoenix-4.14.0-cdh5.14.2-server.jar phoenix-4.14.0-cdh5.14.2-server.jar

Phoenix支持两种索引:可变索引跟不可变索引。在可变表上建的索引是可变索引,在不可变表上建的索引是不可变索引。可变索引是指插入或删除数据的时候会同时更新索引;不可变索引适用于只写入一次不再更改的表,索引只建立一次,再插入数据不会更新索引。需要在 hbase-site.xml 中进行相关配置使其支持可变索引(不可变索引无需另外配置,默认支持)。

java.sql.SQLException: ERROR 1029 (42Y88): Mutable secondary indexes must have the hbase.regionserver.wal.codec property set to org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec in the hbase-sites.xml of every region server.

集群所有 RegionServer 的 hbase-site.xml 配置文件里面增加如下配置:

<property>
    <name>hbase.regionserver.wal.codec</name>
    <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
    <description>支持可变索引</description>
</property>

<property>
    <name>hbase.regionserver.executor.openregion.threads</name>
    <value>100</value>
    <description>Phoenix导致HBase集群region offline故障</description>
</property>

<property>
    <name>hbase.rpc.timeout</name>
    <value>6000000</value>
</property>

<property>
    <name>hbase.client.operation.timeout</name>
    <value>6000000</value>
</property>

<property>
    <name>hbase.client.scanner.timeout.period</name>
    <value>6000000</value>
</property>

<property>
    <name>hbase.client.ipc.pool.type</name>
    <value>RoundRobinPool</value>
</property>

<property>
    <name>hbase.client.ipc.pool.size</name>
    <value>10</value>
</property>

<property>
    <name>hbase.regionserver.lease.period</name>
    <value>6000000</value>
</property>

<property>
    <name>phoenix.query.timeoutMs</name>
    <value>6000000</value>
</property>

<property>
    <name>phoenix.query.keepAliveMs</name>
    <value>6000000</value>
</property>

同时 apache-phoenix-4.14.0-cdh5.14.2-bin/bin/hbase-site.xml 加上以上内容。

2. 基准测试

2.1. 准备 csv 文件

使用 galaxy_loggers 准备测试文件如下(/tmp/stash/):

weblogger_2018061510.csv
weblogger-1_2018061510.csv
weblogger-1_2018061510.csv.1
weblogger-1_2018061510.csv.2
weblogger-1_2018061510.csv.3
weblogger-2_2018061510.csv
weblogger-2_2018061510.csv.1
weblogger-2_2018061510.csv.2
weblogger-2_2018061510.csv.3
...

文件是模拟生成的网站访问日志,每行 1 KB,文件 1GB=1000万行,1TB=100亿行:

rowid,timestr,timeint,dest.id,sour.ip,sour.port,dest.ip,dest.port,dest.url,proxy.ip,proxy.port,proxy.type,keywdid

上传单个文件 weblogger_2018061510.csv 到 hdfs 中(/phoenix/stash/ 目录下):

# sudo -u hdfs hdfs dfs -put /tmp/stash/weblogger_2018061510.csv /phoenix/stash/

或上传整个文件夹(将本地文件夹 /tmp/stash/ 下的全部文件复制到 hdfs://phoenix/stash/):

# sudo -u hdfs hdfs dfs -put /tmp/stash/ /phoenix/

2.2 phoenix 创建 hbase 表

进入 phoenix 命令行 ( 提示符 > ):

# bin/sqlline.py ha06.ztgame.com,ha07.ztgame.com,ha08.ztgame.com:2181

创建表 weblogger.testcsv:

> CREATE TABLE IF NOT EXISTS weblogger.testcsv (
        rowid BIGINT NOT NULL,
        timestr DATE,
        timeint BIGINT,
        dest.id BIGINT,
        sour.ip VARCHAR(30),
        sour.port VARCHAR(10),
        dest.ip VARCHAR(30),
        dest.port VARCHAR(10),
        dest.url VARCHAR(255),
        proxy.ip VARCHAR(30),
        proxy.port VARCHAR(10),
        proxy.type VARCHAR(10),
        keywdid BIGINT,
        CONSTRAINT PK PRIMARY KEY (rowid)
    );

删除索引:

> drop index idx_testcsv_time on weblogger.testcsv;

创建第二索引(secondary index):

> create index idx_testcsv_time on weblogger.testcsv(timestr);
> create index idx_testcsv_ip on weblogger.testcsv(sour.ip, dest.ip);
> create index idx_testcsv_url on weblogger.testcsv(dest.url, keywdid);

因为是空表,所以立即创建索引。使用下面的命令查看,可以看到表和索引(也是表)已经创建好了:

> !tables

TABLE_SCHEM    TABLE_NAME        INDEX_STATE
-------------------------------------------------
WEBLOGGER      IDX_TESTCSV_IP      ACTIVE
WEBLOGGER      IDX_TESTCSV_TIME    ACTIVE
WEBLOGGER      IDX_TESTCSV_URL     ACTIVE
WEBLOGGER      TESTCSV

注:phoenix 的所有表名字都必须大写!

2.3 导入 hdfs 数据文件到表

先看看有哪些 hdfs 文件:

# sudo -u hdfs hdfs dfs -ls /phoenix/stash/

导入其中一个 hdfs:

# sudo -u hbase HADOOP_CLASSPATH=/opt/cloudera/parcels/CDH/jars/hbase-protocol-1.2.0-cdh5.14.2.jar \
    hadoop jar ./phoenix-4.14.0-cdh5.14.2-client.jar \
    org.apache.phoenix.mapreduce.CsvBulkLoadTool \
    --zookeeper=ha06.ztgame.com,ha07.ztgame.com,ha08.ztgame.com:2181 \
    --table=weblogger.testcsv \
    --input=/phoenix/stash/weblogger_2018061510.csv

2.4 创建异步索引

参考:Phoenix系列:二级索引

如果数据表中已经导入了大量数据,此时创建第二索引会很慢,因此提供了异步索引(ASYNC)。

> create index idx_testcsv_keyid on weblogger.testcsv(keywdid, dest.id) async;

执行了上句会立即返回,但其实并没有做任何事情,还需要退出 phoenix 命令回到系统提示符,调用 IndexTool 命令:

# hbase org.apache.phoenix.mapreduce.index.IndexTool --schema=WEBLOGGER --data-table=TESTCSV --index-table=IDX_TESTCSV_KEYID --output-path=/phoenix/ASYNC_INDEX_RES

这个任务不会因为客户端给关闭而结束,是在后台运行。你可以在指定的 hdfs 文件目录下(–output-path=ASYNC_INDEX_RES) 中找到最终的结果。执行结束显示:

    ... ...
    18/06/15 18:25:08 INFO index.IndexTool: Loading HFiles from /phoenix/ASYNC_INDEX_RES/WEBLOGGER.IDX_TESTCSV_KEYID
    18/06/15 18:25:08 WARN mapreduce.LoadIncrementalHFiles: Skipping non-directory hdfs://ha01.ztgame.com:8020/phoenix/ASYNC_INDEX_RES/WEBLOGGER.IDX_TESTCSV_KEYID/_SUCCESS
    18/06/15 18:25:08 INFO hfile.CacheConfig: CacheConfig:disabled
    18/06/15 18:25:08 INFO mapreduce.LoadIncrementalHFiles: Trying to load hfile=hdfs://ha01.ztgame.com:8020/phoenix/ASYNC_INDEX_RES/WEBLOGGER.IDX_TESTCSV_KEYID/0/19e68383bd2f444a81ad85ae459582e0 first=\xC1\x02\x00\xC3\x0B\x01&\x00\x80\x00\x00\xE8\xE3\xD2\xDF\x98 last=\xC2\x0A`\x00\xC3\x1205\x00\x80\x00\x00\xE8\xE3\xD6\x8Dg
    18/06/15 18:25:08 INFO index.IndexToolUtil:  Updated the status of the index IDX_TESTCSV_KEYID to ACTIVE

如果执行上面的任务发生异常(NoSuchMethodError),复制 phoenix-4.14.0-cdh5.14.2-client.jar 到:

  /opt/cloudera/parcels/CDH/jars/

然后进入:

  # cd /opt/cloudera/parcels/CDH/lib/hbase/lib/

创建链接:

  # ln -s ../../../jars/phoenix-4.14.0-cdh5.14.2-client.jar phoenix-4.14.0-cdh5.14.2-client.jar

再执行 IndexTool 命令。

3. 压力测试

将 hdfs://phoenix/stash/ 下的所有文件一并导入到 hbase 表中。总 hdfs 文件 300GB。

3.1. 数据批量导入到 hbase 表

新建一个 weblogger.stashcsv 表:

# CREATE TABLE IF NOT EXISTS weblogger.stashcsv (
    rowid BIGINT NOT NULL,
    timestr DATE,
    timeint BIGINT,
    dest.id BIGINT,
    sour.ip VARCHAR(30),
    sour.port VARCHAR(10),
    dest.ip VARCHAR(30),
    dest.port VARCHAR(10),
    dest.url VARCHAR(255),
    proxy.ip VARCHAR(30),
    proxy.port VARCHAR(10),
    proxy.type VARCHAR(10),
    keywdid BIGINT,
    CONSTRAINT PK PRIMARY KEY (rowid)
);

然后将 hdfs://phoenix/stash/ 下的所有文件一并导入到表中 Loading via MapReduce

# sudo -u hbase HADOOP_CLASSPATH=$(hbase mapredcp):/path/to/hbase/conf \
    hadoop jar ./phoenix-4.14.0-cdh5.14.2-client.jar \
    org.apache.phoenix.mapreduce.CsvBulkLoadTool \
    --zookeeper=ha06.ztgame.com,ha07.ztgame.com,ha08.ztgame.com:2181 \
    --table=weblogger.stashcsv --input=/phoenix/stash/*

或者

# sudo -u hbase HADOOP_CLASSPATH=/path/to/hbase-protocol.jar:/path/to/hbase/conf \
    hadoop jar ./phoenix-4.14.0-cdh5.14.2-client.jar \
    org.apache.phoenix.mapreduce.CsvBulkLoadTool \
    --zookeeper=ha06.ztgame.com,ha07.ztgame.com,ha08.ztgame.com:2181 \
    --table=weblogger.stashcsv --input=/phoenix/stash/*

实际操作命令:

# sudo -u hbase HADOOP_CLASSPATH=$(hbase mapredcp) hadoop jar ./phoenix-4.14.0-cdh5.14.2-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool --zookeeper=ha06.ztgame.com,ha07.ztgame.com,ha08.ztgame.com:2181 --table=weblogger.stashcsv --input=/phoenix/stash/*

本例 300 GB 文件运行 2.5 个小时显示失败异常:

Exception in thread “main” java.io.IOException: Trying to load more than 32 hfiles to one family of one region

更改每个节点的配置文件 hbase-site.xml (cdh manager 可以直接修改配置项) 如下:

<property>
    <name>hbase.hstore.blockingStoreFiles</name>
    <value>200</value>
    <description>当某一个region的storefile个数达到该值则block写入,等待compact. 7 default</description>
</property>

<property>
    <name>hbase.hregion.max.filesize</name>
    <value>107374182400</value>
    <description>单个ColumnFamily的region大小,若按照ConstantSizeRegionSplitPolicy策略,超过设置的该值则自动split. 10 GB default</description>
</property>

再次运行:

# sudo -u hbase HADOOP_CLASSPATH=$(hbase mapredcp) hadoop jar ./phoenix-4.14.0-cdh5.14.2-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool -Dhbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily=500 --zookeeper=ha06.ztgame.com,ha07.ztgame.com,ha08.ztgame.com:2181 --table=weblogger.stashcsv --input=/phoenix/stash/*

其中 hbase.mapreduce.bulkload.max.hfiles.perRegion.perFamily 表示:

在bulkload过程中,每个region列族的HFile数的上限. 默认是 32.

导入成功。300GB hdfs 文件。耗时约 6 hours。

3.2. 创建异步索引

# bin/sqlline.py ha06.ztgame.com,ha07.ztgame.com,ha08.ztgame.com:2181
Done
sqlline version 1.2.0

0: jdbc:phoenix:ha06.ztgame.com,ha07.ztgame.c>

> drop index IDX_STASHCSV_ALL on WEBLOGGER.STASHCSV;
> create index idx_stashcsv_all on weblogger.stashcsv(timestr,sour.ip,dest.ip,dest.url) async;

执行了上句会立即返回,但其实并没有做任何事情,还需要退出 phoenix 命令回到系统提示符,调用 IndexTool 命令:

# hbase org.apache.phoenix.mapreduce.index.IndexTool \
    --schema=WEBLOGGER \
    --data-table=STASHCSV \
    --index-table=IDX_STASHCSV_ALL \
    --output-path=/phoenix/ASYNC_INDEX_RES

索引创建成功,耗时很长,可能和 hbase 配置有关(待查)。

> UPDATE STATISTICS weblogger.stashcsv;
No rows affected (3.705 seconds)

> select count(1) from weblogger.stashcsv;

+-------------+
|  COUNT(1)   |
+-------------+
| 2315915000  |
+-------------+
1 row selected (148.85 seconds)


> explain select rowid,timestr,sour.ip from weblogger.stashcsv where timestr=to_date('2012-01-01 00:00:24') and sour.ip='1.108.167.37';
ROUND ROBIN RANGE SCAN OVER WEBLOGGER.IDX_STASHCSV_ALL

RANGE SCAN 说明使用了索引。

> select rowid,timestr,sour.ip from weblogger.stashcsv where timestr >= to_date('2012-01-01 00:00:00') and timestr < to_date('2013-01-01 00:00:00') and sour.ip='1.108.167.37';

大量的查询返回慢是因为结果集庞大。查询之前首先 explain select … 以获得是否使用索引的信息,否则会全部扫描。

是否每个字段单独建索引而不是联合索引?需要测试。

4. 深度测试(TODO)

猜你喜欢

转载自blog.csdn.net/cheungmine/article/details/80832903