大数据高级开发工程师——HBase学习笔记(3)

Phoenix

Phoenix介绍

什么是Phoenix

  • Phoenix是一个HBase的开源SQL引擎,你可以使用标准的 JDBC API 代替 HBase 客户端 API 来创建表、插入数据、查询数据。

Phoenix底层原理

  • Phoenix框架将命令行上键入的 sql 语句翻译成 hbase 指令,然后 hbase 用翻译好的指令去操作集群,执行完之后给客户端反馈结果。

安装部署

  • 需要先安装好hbase集群,phoenix只是一个工具,只需要在一台机器上安装就可以了,这里我们选择 node02 服务器来进行安装即可

下载安装

# 下载
wget https://dlcdn.apache.org/phoenix/phoenix-5.1.1/phoenix-hbase-2.2-5.1.1-bin.tar.gz
# 解压
tar -zxvf phoenix-hbase-2.2-5.1.1-bin.tar.gz -C /bigdata/install/
cd /bigdata/install/phoenix-hbase-2.2-5.1.1-bin

# 每个节点都需要拷贝这个jar包,否则会报错
cp -a phoenix-server-hbase-2.2-5.1.1.jar ../hbase-2.2.6/lib/
mv bin/hbase-site.xml bin/hbase-site.xml.init

cp $HBASE_HOME/conf/hbase-site.xml ./bin
cp $HADOOP_HOME/etc/hadoop/hdfs-site.xml ./bin

配置环境变量

sudo vim /etc/profile
# 配置Phoenix环境变量
export PHOENIX_HOME=/bigdata/install/phoenix-hbase-2.2-5.1.1-bin
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin

source /etc/profile

重启hbase集群

  • 记得要先启动hadoop集群、zookeeper集群
  • node01 执行以下命令来重启hbase的集群
cd /bigdata/install/hbase-2.2.6

bin/stop-hbase.sh 
bin/start-hbase.sh

验证是否成功

  • 在 node03 执行以下命令,能进入 phoenix 客户端即可
sqlline.py node03:2181

在这里插入图片描述

Phoenix使用

批处理方式

  • node03 执行以下命令创建 user_phoenix.sql 文件
-- vim /bigdata/install/hbasedatas/user_phoenix.sql
create table if not exists user_phoenix (state varchar(10) NOT NULL,  city varchar(20) NOT NULL, population BIGINT  CONSTRAINT my_pk PRIMARY KEY (state, city));
  • node03 执行以下命令,创建 user_phoenix.csv 数据文件
# vim /bigdata/install/hbasedatas/user_phoenix.csv
NY,New York,8143197
CA,Los Angeles,3844829
IL,Chicago,2842518
TX,Houston,2016582
PA,Philadelphia,1463281
AZ,Phoenix,1461575
TX,San Antonio,1256509
CA,San Diego,1255540
TX,Dallas,1213825
CA,San Jose,912332
  • 创建 user_phoenix_query.sql 文件
-- vim /bigdata/install/hbasedatas/user_phoenix_query.sql 
select state as "userState",count(city) as "City Count",sum(population) as "Population Sum" FROM user_phoenix GROUP BY state; 
  • 执行sql语句
cd /bigdata/install/hbasedatas

psql.py node01:2181 user_phoenix.sql user_phoenix.csv user_phoenix_query.sql

在这里插入图片描述

命令行方式

# 进入 phoenix 客户端执行命令
sqlline.py node03:2181

# 退出命令行方式
> !quit
# 或
> !q

# 查看 phoenix的帮助文档,显示所有命令
> !help

# 查看表
!tables

在这里插入图片描述

1. 表的映射
  • 建立 employee 的映射表:node01 进入hbase客户端,创建一个普通表employee,并且有两个列族 company 和 family
# node01
# hbase shell
> create 'employee','company','family'
  • 添加数据
put 'employee','row1','company:name','ted'
put 'employee','row1','company:position','worker'
put 'employee','row1','family:tel','13600912345'
put 'employee','row1','family:age','18'
put 'employee','row2','company:name','michael'
put 'employee','row2','company:position','manager'
put 'employee','row2','family:tel','1894225698'
put 'employee','row2','family:age','20'

在这里插入图片描述

  • 建立hbase到phoenix的映射表:node03 进入到 phoenix 的客户端,然后创建映射表
-- sqlline.py node03:2181
> CREATE TABLE IF NOT EXISTS "employee" ("no" VARCHAR(10) NOT NULL PRIMARY KEY, "company"."name" VARCHAR(30),"company"."position" VARCHAR(20), "family"."tel" VARCHAR(20), "family"."age" VARCHAR(20)) column_encoded_bytes=0;
-- 说明:在建立映射表之前要说明的是,Phoenix是==大小写敏感==的,并且所有命令都是大写。如果你建的表名没有用双引号括起来,那么无论你输入的是大写还是小写,建立出来的表名都是大写的;如果你需要建立出同时包含大写和小写的表名和字段名,请把表名或者字段名用==双引号括起来==。 
  • 查询映射表数据
> select * from "employee";
> select * from "employee" where "tel" = '13600912345';

在这里插入图片描述

JDBC

<dependency>
    <groupId>org.apache.phoenix</groupId>
    <artifactId>phoenix-core</artifactId>
    <version>5.0.0-HBase-2.0</version>
</dependency>
  • 代码开发
public class PhoenixSearch {
    private static final String URL = "jdbc:phoenix:node03:2181";
    private static final String SQL = "select * from USER_PHOENIX";
    public static void main(String[] args) throws Exception {
        try (Connection connection = DriverManager.getConnection(URL);
             Statement statement = connection.createStatement();
             ResultSet rs = statement.executeQuery(SQL)) {
            System.out.printf("%-15s%-15s%-15s\n", "CITY", "POPULATION", "STATE");
            while (rs.next()) {
                System.out.printf("%-15s%-15s%-15s\n", rs.getString("city"),
                        rs.getString("POPULATION"), rs.getString("STATE"));
            }
            System.out.println("---------------------------------------------");
        }
    }
}

在这里插入图片描述

Phoenix构建二级索引

为什么需要用二级索引?

  • 对于HBase而言,如果想精确地定位到某行记录,唯一的办法是通过 rowkey 来查询。如果不通过rowkey来查找数据,就必须逐行地比较每一列的值,即全表扫瞄。
  • 对于较大的表,全表扫描的代价是不可接受的。但是,很多情况下,需要从多个角度查询数据。
    • 例如,在定位某个人的时候,可以通过姓名、身份证号、学籍号等不同的角度来查询
    • 要想把这么多角度的数据都放到rowkey中几乎不可能(业务的灵活性不允许,对rowkey长度的要求也不允许)。
    • 所以需要secondary index(二级索引)来完成这件事。secondary index的原理很简单,但是如果自己维护的话则会麻烦一些。
    • 现在,Phoenix已经提供了对HBase secondary index的支持。

在这里插入图片描述

全局索引和本地索引

1. Global Indexing
  • Global indexing:全局索引,适用于读多写少的业务场景。
  • 使用 Global indexing 在写数据的时候开销很大,因为所有对数据表的更新操作(DELETE, UPSERT VALUES and UPSERT SELECT),都会引起索引表的更新,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。
  • 在读数据的时候 Phoenix 会选择索引表来降低查询消耗的时间。
  • 在默认情况下如果想查询的字段不是索引字段的话索引表不会被使用,也就是说不会带来查询速度的提升。
2. Local Indexing
  • Local indexing:本地索引,适用于写操作频繁以及空间受限制的场景。
  • 与Global indexing一样,Phoenix会自动判定在进行查询的时候是否使用索引。
  • 使用Local indexing时,索引数据和数据表的数据存放在相同的服务器中,这样避免了在写操作的时候往不同服务器的索引表中写索引带来的额外开销。
  • 使用Local indexing的时候即使查询的字段不是索引字段索引表也会被使用,这会带来查询速度的提升,这点跟Global indexing不同。对于Local Indexing,一个数据表的所有索引数据都存储在一个单一的独立的可共享的表中。

不可变索引和可变索引

1. immutable index
  • immutable index:不可变索引,适用于数据只增加不更新并且按照时间先后顺序存储(time-series data)的场景,如保存日志数据或者事件数据等。
  • 不可变索引的存储方式是write one,append only。
  • 当在Phoenix使用create table语句时指定IMMUTABLE_ROWS = true表示该表上创建的索引将被设置为不可变索引。
  • 不可变索引分为Global immutable index和Local immutable index两种。
  • Phoenix默认情况下如果在create table时不指定IMMUTABLE_ROW = true时,表示该表为mutable。
2. mutable index
  • mutable index:可变索引,适用于数据有增删改的场景。
  • Phoenix默认情况创建的索引都是可变索引,除非在create table的时候显式地指定IMMUTABLE_ROWS = true。
  • 可变索引同样分为Global mutable index和Local mutable index两种。

配置HBase支持Phoenix二级索引

  • 修改配置文件 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>
</property>
<property>
	<name>hbase.rpc.controllerfactory.class</name>
	<value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory</value>
</property>
  • 完成上述修改后重启 hbase 集群使配置生效。

实战

1. 在 phoenix 中创建表
  • 进入 node03 的 phoenix 客户端
sqlline.py node03:2181
  • 创建表
create table user (
"session_id" varchar(100) not null primary key, 
"f"."cookie_id" varchar(100), 
"f"."visit_time" varchar(100), 
"f"."user_id" varchar(100), 
"f"."age" varchar(100), 
"f"."sex" varchar(100), 
"f"."visit_url" varchar(100), 
"f"."visit_os" varchar(100), 
"f"."browser_name" varchar(100),
"f"."visit_ip" varchar(100), 
"f"."province" varchar(100),
"f"."city" varchar(100),
"f"."page_id" varchar(100), 
"f"."goods_id" varchar(100),
"f"."shop_id" varchar(100)) column_encoded_bytes=0;
2. 导入测试数据
  • 先上数据文件 user50w.csv 上传到 node03 的 /bigdata/install/hbasedatas 路径下,然后导入50W的测试数据
psql.py -t USER node01:2181 /bigdata/install/hbasedatas/user50w.csv
3. Global Indexing的二级索引测试
  • 测试正常查询一条数据所需的时间:在为表USER创建secondary index之前,先看看查询一条数据所需的时间。在 node03 服务器,进入 phoenix 的客户端,然后执行以下sql语句,查询数据,查看耗费时间
select * from user where "cookie_id" = '99738fd1-2084-44e9';
  • 可以看到,对名为cookie_id的列进行按值查询需要 13 秒左右。
  • 我们可以通过explain来查看执行计划:
explain select * from user where "cookie_id" = '99738fd1-2084-44e9';

在这里插入图片描述

  • 由此看出先进行了全表扫描再通过过滤器来筛选出目标数据,显示这种查询方式效率是很低的。
  • 接下来给表 USER 创建基于 Global Indexing 的二级索引:在cookie_id列上面创建二级索引
create index USER_COOKIE_ID_INDEX on USER ("f"."cookie_id"); 

-- 查看当前所有表会发现多一张 USER_COOKIE_ID_INDEX 索引表,查询该表数据。
select * from USER_COOKIE_ID_INDEX limit 5;

在这里插入图片描述

  • 再次执行查询 “cookie_id”=‘99738fd1-2084-44e9’ 的数据记录
select "cookie_id" from user where "cookie_id" = '99738fd1-2084-44e9';

在这里插入图片描述

  • 此时,查询速度由10秒左右减少到了毫秒级别。注意:select所带的字段必须包含在覆盖索引内。
  • 查看 EXPLAIN(语句的执行逻辑及计划),可以看到使用到了创建的索引USER_COOKIE_ID_INDEX。
explain select "cookie_id" from user where "cookie_id"='99738fd1-2084-44e9';
  • 以下查询不会用到索引表
-- 虽然cookie_id是索引字段,但age不是索引字段,所以不会使用到索引
select "cookie_id","age" from user where "cookie_id"='99738fd1-2084-44e9';
-- 也可以通过EXPLAIN查询语句的执行逻辑及计划
explain select "cookie_id","age" from user where "cookie_id"='99738fd1-2084-44e9';
-- 同理要查询的字段不是索引字段,也不会使用到索引表。
select "sex" from user where "cookie_id"='99738fd1-2084-44e9';

在这里插入图片描述

4. Local Indexing的二级索引测试
  • 测试正常查询一条数据所需的时间:在为表USER创建secondary index之前,先看看查询一条数据所需的时间
select * from user where "user_id"='371e963d-c-487065';
-- 查看执行计划
explain select * from user where "user_id"='371e963d-c-487065';
  • 可以看到,对名为user_id的列进行按值查询需要 4 秒左右。由此知道先进行了全表扫描再通过过滤器来筛选出目标数据,显示这种查询方式效率是很低的。

在这里插入图片描述

  • 然后给表 USER 创建基于Local Indexing的二级索引:
-- 在user_id列上面创建二级索引
create local index USER_USER_ID_INDEX on USER ("f"."user_id");
  • 查看当前所有表会发现多一张USER_USER_ID_INDEX索引表,查询该表数据。

在这里插入图片描述

  • 再次执行查询 “user_id”=‘371e963d-c-487065’ 的数据记录
select * from user where "user_id"='371e963d-c-487065';
-- 查看执行计划
explain select * from user where "user_id"='371e963d-c-487065';
  • 可以看到,对名为user_id的列进行按值查询需要0.1秒左右。查看执行计划,没有执行全表扫描,效率更高了。查询速度由 4 秒左右减少到了毫秒级别。

在这里插入图片描述

  • 那如果查询的字段不包含在索引表中,有如何呢?
select "user_id","age","sex" from user where "user_id"='371e963d-c-487065';
-- 查看执行计划
explain select "user_id","age","sex" from user where "user_id"='371e963d-c-487065';
  • 可以看到使用到了创建的索引USER_USER_ID_INDEX

在这里插入图片描述

5. 如何确保query查询使用索引
  • 要想让一个查询使用索引,有三种方式实现。
创建覆盖索引
  • 如果在某次查询中,查询项或者查询条件中包含除被索引列之外的列(主键MY_PK除外)。
  • 默认情况下,该查询会触发full table scan(全表扫描),但是使用covered index则可以避免全表扫描。创建包含某个字段的覆盖索引,创建方式如下:
create index USER_COOKIE_ID_AGE_INDEX on USER ("f"."cookie_id") include("f"."age");
  • 查看当前所有表会发现多一张USER_COOKIE_ID_AGE_INDEX索引表,查询该表数据。
select * from USER_COOKIE_ID_AGE_INDEX limit 5;
-- 查询数据
select "age" from user where "cookie_id"='99738fd1-2084-44e9';
select "age","sex" from user where "cookie_id"='99738fd1-2084-44e9';

在这里插入图片描述

在查询中提示其使用索引
  • 在select和column_name之间加上/*+ Index(<表名> <index表名>)*/,通过这种方式强制使用索引。
select /*+ index(user,USER_COOKIE_ID_AGE_INDEX) */ "age" from user where "cookie_id"='99738fd1-2084-44e9';
  • 如果age是索引字段,那么就会直接从索引表中查询;如果age不是索引字段,那么将会进行全表扫描,所以当用户明确知道表中数据较少且符合检索条件时才适用,此时的性能才是最佳的。
使用本地索引(创建Local Indexing 索引)
6. 索引重建
  • Phoenix的索引重建是把索引表清空后重新装配数据。
alter index USER_COOKIE_ID_INDEX on user rebuild;
7. 删除索引
  • 删除某个表的某张索引:drop index 索引名称 on 表名
drop  index USER_COOKIE_ID_INDEX on user;
  • 如果表中的一个索引列被删除,则索引也将被自动删除
  • 如果删除的是覆盖索引上的列,则此列将从覆盖索引中被自动删除。

索引性能调优

  • 一般来说,索引已经很快了,不需要特别的优化。
  • 这里也提供了一些方法,让你在面对特定的环境和负载的时候可以进行一些调优。下面的这些需要在 hbase-site.xml 文件中设置,针对所有的服务器- 。
配置说明默认值
index.builder.threads.max创建索引时,使用的最大线程数10
index.builder.threads.keepalivetime创建索引的创建线程池中线程的存活时间,单位:秒60
index.writer.threads.max写索引表数据的写线程池的最大线程数。
更新索引表可以用的最大线程数,也就是同时可以更新多少张索引表,数量最好和索引表的数量一致。
10
index.writer.threads.keepalivetime索引写线程池中,线程的存活时间,单位:秒60
hbase.htable.threads.max每一张索引表可用于写的线程数2,147,483,647
hbase.htable.threads.keepalivetime索引表线程池中线程的存活时间,单位:秒60
index.tablefactory.cache.size允许缓存的索引表的数量。
增加此值,可以在写索引表时不用每次都去重复的创建htable,这个值越大,内存消耗越多。
10
org.apache.phoenix.regionserver.index.handler.count处理全局索引写请求时,可以使用的线程数。30
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讲文明的喜羊羊拒绝pua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值