- Phoenix
- Phoenix介绍
- 什么是Phoenix
- Phoenix底层原理
- 安装部署
- 下载安装
- 配置环境变量
- 重启hbase集群
- 验证是否成功
- Phoenix使用
- 批处理方式
- 命令行方式
- 1. 表的映射
- JDBC
- Phoenix构建二级索引
- 为什么需要用二级索引?
- 全局索引和本地索引
- 1. Global Indexing
- 2. Local Indexing
- 不可变索引和可变索引
- 1. immutable index
- 2. mutable index
- 配置Hbase支持Phoenix二级索引
- 实战
- 1. 在 phoenix 中创建表
- 2. 导入测试数据
- 3. Global Indexing的二级索引测试
- 4. Local Indexing的二级索引测试
- 5. 如何确保query查询使用索引
- 创建覆盖索引
- 在查询中提示其使用索引
- 使用本地索引(创建Local Indexing 索引)
- 6. 索引重建
- 7. 删除索引
- 索引性能调优
- Phoenix是一个Hbase的开源SQL引擎,你可以使用标准的 JDBC API 代替 Hbase 客户端 API 来创建表、插入数据、查询数据。
- Phoenix框架将命令行上键入的 sql 语句翻译成 hbase 指令,然后 hbase 用翻译好的指令去 *** 作集群,执行完之后给客户端反馈结果。
- 需要先安装好hbase集群,phoenix只是一个工具,只需要在一台机器上安装就可以了,这里我们选择 node02 服务器来进行安装即可
- 这里我们使用的是 phoenix-hbase-2.2-5.1.1-bin.tar.gz
# 下载 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:2181Phoenix使用 批处理方式
- 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 # 查看表 !tables1. 表的映射
- 建立 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
- 工程 hbase-demo 中在添加依赖:
org.apache.phoenix phoenix-core5.0.0-Hbase-2.0
- 代码开发
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%-15sn", "CITY", "POPULATION", "STATE"); while (rs.next()) { System.out.printf("%-15s%-15s%-15sn", rs.getString("city"), rs.getString("POPULATION"), rs.getString("STATE")); } System.out.println("---------------------------------------------"); } } }Phoenix构建二级索引 为什么需要用二级索引?
- 对于Hbase而言,如果想精确地定位到某行记录,唯一的办法是通过 rowkey 来查询。如果不通过rowkey来查找数据,就必须逐行地比较每一列的值,即全表扫瞄。
- 对于较大的表,全表扫描的代价是不可接受的。但是,很多情况下,需要从多个角度查询数据。
- 例如,在定位某个人的时候,可以通过姓名、身份z号、学籍号等不同的角度来查询
- 要想把这么多角度的数据都放到rowkey中几乎不可能(业务的灵活性不允许,对rowkey长度的要求也不允许)。
- 所以需要secondary index(二级索引)来完成这件事。secondary index的原理很简单,但是如果自己维护的话则会麻烦一些。
- 现在,Phoenix已经提供了对Hbase secondary index的支持。
- Global indexing:全局索引,适用于读多写少的业务场景。
- 使用 Global indexing 在写数据的时候开销很大,因为所有对数据表的更新 *** 作(DELETe, UPSERT VALUES and UPSERT SELECT),都会引起索引表的更新,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。
- 在读数据的时候 Phoenix 会选择索引表来降低查询消耗的时间。
- 在默认情况下如果想查询的字段不是索引字段的话索引表不会被使用,也就是说不会带来查询速度的提升。
- Local indexing:本地索引,适用于写 *** 作频繁以及空间受限制的场景。
- 与Global indexing一样,Phoenix会自动判定在进行查询的时候是否使用索引。
- 使用Local indexing时,索引数据和数据表的数据存放在相同的服务器中,这样避免了在写 *** 作的时候往不同服务器的索引表中写索引带来的额外开销。
- 使用Local indexing的时候即使查询的字段不是索引字段索引表也会被使用,这会带来查询速度的提升,这点跟Global indexing不同。对于Local Indexing,一个数据表的所有索引数据都存储在一个单一的独立的可共享的表中。
- 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。
- mutable index:可变索引,适用于数据有增删改的场景。
- Phoenix默认情况创建的索引都是可变索引,除非在create table的时候显式地指定IMMUTABLE_ROWS = true。
- 可变索引同样分为Global mutable index和Local mutable index两种。
- 修改配置文件 hbase-site.xml
hbase.regionserver.wal.codec org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec hbase.region.server.rpc.scheduler.factory.class org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory hbase.rpc.controllerfactory.class org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory
- 完成上述修改后重启 hbase 集群使配置生效。
- 进入 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.csv3. 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
- 要想让一个查询使用索引,有三种方式实现。
- 如果在某次查询中,查询项或者查询条件中包含除被索引列之外的列(主键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之间加上,通过这种方式强制使用索引。
select "age" from user where "cookie_id"='99738fd1-2084-44e9';
- 如果age是索引字段,那么就会直接从索引表中查询;如果age不是索引字段,那么将会进行全表扫描,所以当用户明确知道表中数据较少且符合检索条件时才适用,此时的性能才是最佳的。
- 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 文件中设置,针对所有的服务器- 。
更新索引表可以用的最大线程数,也就是同时可以更新多少张索引表,数量最好和索引表的数量一致。10
增加此值,可以在写索引表时不用每次都去重复的创建htable,这个值越大,内存消耗越多。10
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)