文章目录
文章目录
1、Phoenix搭建(Phoenix 4.15 HBase 1.4.6 hadoop 2.7.6)
1)、关闭hbase集群,在master中执行
stop-hbase.sh
2)、上传解压配置环境变量
解压
tar -xvf apache-phoenix-4.15.0-HBase-1.4-bin.tar.gz
改名
mv apache-phoenix-4.15.0-HBase-1.4-bin phoenix-4.15.0
配置环境变量
#phoenix
export PHOENIX_HOME=/usr/local/soft/phoenix-4.15.0
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
3)、将phoenix-4.15.0-HBase-1.4-server.jar复制到所有节点的hbase lib目录下
scp /usr/local/soft/phoenix-4.15.0/phoenix-4.15.0-HBase-1.4-server.jar master:/usr/local/soft/hbase-1.4.6/lib/
scp /usr/local/soft/phoenix-4.15.0/phoenix-4.15.0-HBase-1.4-server.jar node1:/usr/local/soft/hbase-1.4.6/lib/
scp /usr/local/soft/phoenix-4.15.0/phoenix-4.15.0-HBase-1.4-server.jar node2:/usr/local/soft/hbase-1.4.6/lib/
4)、启动hbase , 在master中执行
start-hbase.sh
5)、连接sqlline
sqlline.py master,node1,node2
# 出现,成功!
163/163 (100%) Done
Done
sqlline version 1.5.0
0: jdbc:phoenix:master,node1,node2>
2、Phoenix Shell基本操作
1)、基本操作
--1、创建表
CREATE TABLE IF NOT EXISTS STUDENT (
id VARCHAR NOT NULL PRIMARY KEY,
name VARCHAR,
age BIGINT,
gender VARCHAR ,
clazz VARCHAR
);
--2、显示所有表
!table
--3、插入数据
upsert into STUDENT values('1500100004','葛德曜',24,'男','理科三班');
upsert into STUDENT values('1500100005','宣谷芹',24,'男','理科六班');
upsert into STUDENT values('1500100006','羿彦昌',24,'女','理科三班');
--4、查询数据
select * from STUDENT ;
select * from STUDENT where age=24;
--5、删除数据
delete from STUDENT where id='1500100004';
--6、删除表
drop table STUDENT;
--7、退出命令行
!quit
2)、表的映射
①、视图映射:
在hbase shell创建表并存储数据
--建表
create 'fruit','info'
--插入数据
put 'fruit','1001','info:name','apple'
put 'fruit','1002','info:name','pinapple'
put 'fruit','1001','info:color','red'
put 'fruit','1002','info:color','yellow'
--扫描该表
scan 'fruit'
--结果展示
ROW COLUMN+CELL
1001 column=info:color, timestamp=1611238657236, value=red
1001 column=info:name, timestamp=1611238624978, value=apple
1002 column=info:color, timestamp=1611238673993, value=yellow
1002 column=info:name, timestamp=1611238638719, value=pinapple
在Phoenix Shell建立视图映射
--创建视图表
create view "fruit"
(id varchar primary key,
"info"."name" varchar,
"info"."color" varchar);
--查询该表
select * from "fruit";
--结果展示
+-------+-----------+---------+
| ID | name | color |
+-------+-----------+---------+
| 1001 | apple | red |
| 1002 | pinapple | yellow |
+-------+-----------+---------+
补充:Table is read only(创建的视图是只读的,所以只能用来做查询)
②、表映射:
在Phoenix Shell建立表映射
create table "fruit"
(id varchar primary key,
"info"."name" varchar,
"info"."color" varchar)
column_encoded_bytes=0;
补充:创建视图映射是为了保护表中的数据,视图映射表删除hbase上表格还在,表映射删除表格,hbase上的表也就对应的删除了。
3、Phoenix JDBC操作
1)、导入maven依赖
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.4.6</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.4.6</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-client -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-core</artifactId>
<version>4.15.0-HBase-1.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.6</version>
</dependency>
</dependencies>
2)、jdbc代码
public class phoenix_jdbc {
public static void main(String[] args) throws SQLException {
Connection connection = DriverManager.getConnection("jdbc:phoenix:master,node1,node2:2181");
PreparedStatement ps = connection.prepareStatement("select mdn,x,y,county from dianxin where x=? and y =?");
ps.setDouble(1, 117.288);
ps.setDouble(2, 31.822);
ResultSet resultSet = ps.executeQuery();
while (resultSet.next()) {
String mdn = resultSet.getString("mdn");
double x = resultSet.getDouble("x");
double y = resultSet.getDouble("y");
String county = resultSet.getString("county");
System.out.println(mdn + "\t" + x + "\t" + y + "\t" + county);
}
connection.close();
}
}
4、Phoenix二级索引
对于Hbase,如果想精确定位到某行记录,唯一的办法就是通过rowkey查询。如果不通过rowkey查找数据,就必须逐行比较每一行的值,对于较大的表,全表扫描的代价是不可接受的。
1)、开启索引支持
# 关闭hbase集群
stop-hbase.sh
# 在hbase-site.xml中增加如下配置
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
# 同步到所有节点
scp hbase-site.xml node1:`pwd`
scp hbase-site.xml node2:`pwd`
# 启动hbase
start-hbase.sh
2)、创建索引
①、全局索引
全局索引适合读多写少的场景。如果使用全局索引,读数据基本不损耗性能,所有的性能损耗都来源于写数据。数据表的添加、删除和修改都会更新相关的索引表(数据删除了,索引表中的数据也会删除;数据增加了,索引表的数据也会增加)
注意: 对于全局索引在默认情况下,在查询语句中检索的列如果不在索引表中,Phoenix不会使用索引表将,除非使用hint。
# 创建DIANXIN.sql
CREATE TABLE IF NOT EXISTS DIANXIN (
mdn VARCHAR ,
start_date VARCHAR ,
end_date VARCHAR ,
county VARCHAR,
x DOUBLE ,
y DOUBLE,
bsid VARCHAR,
grid_id VARCHAR,
biz_type VARCHAR,
event_type VARCHAR ,
data_source VARCHAR ,
CONSTRAINT PK PRIMARY KEY (mdn,start_date)
);
# 上传数据DIANXIN.csv
# 导入数据
psql.py master,node1,node2 DIANXIN.sql DIANXIN.csv
# 创建全局索引
CREATE INDEX DIANXIN_INDEX ON DIANXIN ( end_date );
# 查询数据 ( 索引未生效)
select * from DIANXIN where end_date = '20180503154014';
# 强制使用索引 (索引生效) hint
select /*+ INDEX(DIANXIN DIANXIN_INDEX) */ * from DIANXIN where end_date = '20180503154014' and start_date = '20180503154614';
# 取索引列,(索引生效)
select end_date from DIANXIN where end_date = '20180503154014';
select and,end_date from DIANXIN where end_date = '20180503154014';
# 创建多列索引
CREATE INDEX DIANXIN_INDEX1 ON DIANXIN ( end_date,start_data );
# 多条件查询 (索引生效)
select end_date from DIANXIN where end_date = '20180503154014' and start_date = '20180503154614';
# 查询所有列 ( 索引未生效)
select * from DIANXIN where end_date = '20180503154014' and start_date = '20180503154614';
# 查询所有列 (索引生效)
select /*+ INDEX(DIANXIN DIANXIN_INDEX) */ * from DIANXIN where end_date = '20180503154014' and start_date = '20180503154614';
# 单条件 ( 索引未生效)
select end_date from DIANXIN where start_date = '20180503154614';
注意:全局索引的代价,占空间是肯定的,插入数据也会变慢
②、本地索引
本地索引适合写多读少的场景,或者存储空间有限的场景。和全局索引一样,Phoenix也会在查询的时候自动选择是否使用本地索引。本地索引因为索引数据和原数据存储在同一台机器上,避免网络数据传输的开销,所以更适合写多的场景。由于无法提前确定数据在哪个Region上,所以在读数据的时候,需要检查每个Region上的数据从而带来一些性能损耗。
注意:对于本地索引,查询中无论是否指定hint或者是查询的列是否都在索引表中,都会使用索引表。
@ 创建本地索引
CREATE LOCAL INDEX DIANXIN_LOCAL_IDEX ON DIANXIN(grid_id);
# 索引生效
select grid_id from dianxin where grid_id='117285031820040'
# 索引生效
select * from dianxin where grid_id='117285031820040'
注意:本地索引是在hbase表里插入格外的一批值,这批值只有主键没有value,查找原理:通过过滤的信息查找到表的主键再通过主键查找这一行的信息,因此查找 * 是可以的。
③、覆盖索引
覆盖索引是把原数据存储在索引数据表中,这样在查询时不需要再去HBase的原表获取数据就,直接返回查询结果。
注意:查询是 select 的列和 where 的列都需要在索引中出现。
# 创建覆盖索引
CREATE INDEX DIANXIN_INDEX_COVER ON DIANXIN ( x,y ) INCLUDE ( county_id );
# 查询所有列 (索引未生效)
select * from dianxin where x=117.288 and y =31.822;
# 强制使用索引 (索引生效)
select /*+ INDEX(DIANXIN DIANXIN_INDEX_COVER) */ * from dianxin where x=117.288 and y =31.822;
# 查询所索引中的列 (索引生效)
select x,y,county_id from dianxin where x=117.288 and y =31.822;
查询条件必须放在索引中 select 中的列可以放在INCLUDE (将数据保存在索引中)
3)、对比
查找:全局索引和本地索引在单纯查找这一方面肯定是全局查找快!因为全局是一次查找出结果,而本地是二次查找.
空间代价:全局代价比本地高
插入,修改:本地插入、修改更快
因而本地适合读少写多,全局适合读多写少
5、Hbase与Hive的集成
1)、二者对比
一.Hive
(1) 数据仓库
Hive的本质其实就相当于将HDFS中已经存储的文件在Mysql中做了一个双射关系,以方便使用HQL去管理查询。
(2) 用于数据分析、清洗
Hive适用于离线的数据分析和清洗,延迟较高。
(3) 基于HDFS、MapReduce
Hive存储的数据依旧在DataNode上,编写的HQL语句终将是转换为MapReduce代码执行。
二.HBase
(1) 数据库
是一种面向列族存储的非关系型数据库。
(2) 用于存储结构化和非结构化的数据
适用于单表非关系型数据的存储,不适合做关联查询,类似JOIN等操作。
(3) 基于HDFS
数据持久化存储的体现形式是HFile,存放于DataNode中,被ResionServer以region的形式进行管理。
(4) 延迟较低,接入在线业务使用
面对大量的企业数据,HBase可以直线单表大量数据的存储,同时提供了高效的数据访问速度。
★2)、HBase与Hive集成使用
HBase与Hive的集成在最新的两个版本中无法兼容。所以,我们只能重新编译:hive-hbase-handler-1.2.2.jar!
编译过程附上网上大佬的流程网址https://www.jianshu.com/p/425df8dbca11
/*案例1:*/
--1、在Hive中创建表同时关联HBase
CREATE TABLE hive_hbase_emp_table(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
--2、在hive里面创建中间表,做数据分析
CREATE TABLE emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
row format delimited fields terminated by '\t';
--3、导入数据分析的结果,例如:通过insert命令将中间表中的数据导入到Hive关联Hbase的那张表中
insert into table hive_hbase_emp_table select * from emp where sal>2000;
--4、phoenix编写视图最后结果显示(供其他团队使用)
create view "hbase_emp_table"(
empno varchar primary key,
"info"."ename" varchar,
"info"."job" varchar,
"info"."mgr" varchar,
"info"."hiredate" varchar,
"info"."sal" varchar,
"info"."comm" varchar,
"info"."deptno" varchar);
/*案例2:*/
--1、在Hive中创建外部表,可以用来做数据分析
CREATE EXTERNAL TABLE relevance_hbase_emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
STORED BY
'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" =
":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
--2、hive外部表修改hbase表也会修改
insert into table relevance_hbase_emp select * from emp;
注意:这样在hive建表,hdfs上是没有数据的
3)、附录带重编译过程:
1、下载Hive源码地址
http://archive.apache.org/dist/hive/hive-1.2.1/
2、解压Hive源码
3、进入Hive源码,把hbase-handler模块引入Eclipse项目中
4、创建Java Project
5、创建lib文件夹
6、导入Hive源码的hbase-handler模块
7、把Hive和HBase 安装包lib目录下的所有jar包,复制到java project的lib目录下
8、把lib包下的所有jar包Build Path,添加到类路径
9、打包项目,开始重新编译
10、替换hive/lib目录下的hive-hbase-handler-1.2.1.jar
[victor@node1 ~]$ cp hive-hbase-handler-1.2.1.jar $HIVE_HOME/lib/hive-hbase-handler-1.2.1.jar
6、Hbase的优化
1)、预分区
如果面对大量的数据,并且我们提前知道该数据可能要分区,为了防止在以后的使用过程中频繁分区,因而做一个提前的预分区。
1.手动设定预分区
Hbase> create 'staff1','info','partition1',SPLITS => ['1000','2000','3000','4000']
2.生成16进制序列预分区
create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
3.按照文件中设置的规则预分区
创建splits.txt文件内容如下:
aaaa
bbbb
cccc
dddd
然后执行:
create 'staff3','partition3',SPLITS_FILE => 'splits.txt'
4.使用JavaAPI创建预分区
//自定义算法,产生一系列hash散列值存储在二维数组中
byte[][] splitKeys = 某个散列值函数
//创建HbaseAdmin实例
HBaseAdmin hAdmin = new HBaseAdmin(HbaseConfiguration.create());
//创建HTableDescriptor实例
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
//通过HTableDescriptor实例和散列值二维数组创建带有预分区的Hbase表
hAdmin.createTable(tableDesc, splitKeys);
2)、RowKey设计
①、生成随机数、hash、散列值
比如:原本rowKey为1001的,SHA1后变成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7原本rowKey为3001的,SHA1后变成:49042c54de64a1e9bf0b33e00245660ef92dc7bd原本rowKey为5001的,SHA1后变成:7b61dec07e02c188790670af43e717f0f46e8913在做此操作之前,一般我们会选择从数据集中抽取样本,来决定什么样的rowKey来Hash后作为每个分区的临界值。
②、字符串反转
20170524000001转成10000042507102
20170524000002转成20000042507102
这样也可以在一定程度上散列逐步put进来的数据。
③、字符串拼接
20170524000001_a12e
20170524000001_93i7
3)、内存优化
HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,一般会分配整个可用内存的70%给HBase的Java堆。但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16~48G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。
4)、基础优化
//1.允许在HDFS的文件中追加内容
hdfs-site.xml、hbase-site.xml
属性:dfs.support.append
解释:开启HDFS追加同步,可以优秀的配合HBase的数据同步和持久化。默认值为true。
//2.优化DataNode允许的最大文件打开数
hdfs-site.xml
属性:dfs.datanode.max.transfer.threads
解释:HBase一般都会同一时间操作大量的文件,根据集群的数量和规模以及数据动作,设置为4096或者更高。默认值:4096
//3.优化延迟高的数据操作的等待时间
hdfs-site.xml
属性:dfs.image.transfer.timeout
解释:如果对于某一次数据操作来讲,延迟非常高,socket需要等待更长的时间,建议把该值设置为更大的值(默认60000毫秒),以确保socket不会被timeout掉。
//4.优化数据的写入效率
mapred-site.xml
属性:
mapreduce.map.output.compress
mapreduce.map.output.compress.codec
解释:开启这两个数据可以大大提高文件的写入效率,减少写入时间。第一个属性值修改为true,第二个属性值修改为:org.apache.hadoop.io.compress.GzipCodec或者其他压缩方式。
//5.设置RPC监听数量
hbase-site.xml
属性:Hbase.regionserver.handler.count
解释:默认值为30,用于指定RPC监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。
//6.优化HStore文件大小
hbase-site.xml
属性:hbase.hregion.max.filesize
解释:默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值,因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个Hfile。
//7.优化HBase客户端缓存
hbase-site.xml
属性:hbase.client.write.buffer
解释:用于指定Hbase客户端缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。
//8.指定scan.next扫描HBase所获取的行数
hbase-site.xml
属性:hbase.client.scanner.caching
解释:用于指定scan.next方法获取的默认行数,值越大,消耗内存越大。
//9.flush、compact、split机制
当MemStore达到阈值,将Memstore中的数据Flush进Storefile;compact机制则是把flush出来的小文件合并成大的Storefile文件。split则是当Region达到阈值,会把过大的Region一分为二。
涉及属性:
即:128M就是Memstore的默认阈值
hbase.hregion.memstore.flush.size:134217728
即:这个参数的作用是当单个HRegion内所有的Memstore大小总和超过指定值时,flush该HRegion的所有memstore。RegionServer的flush是通过将请求添加一个队列,模拟生产消费模型来异步处理的。那这里就有一个问题,当队列来不及消费,产生大量积压请求时,可能会导致内存陡增,最坏的情况是触发OOM。
hbase.regionserver.global.memstore.upperLimit:0.4
hbase.regionserver.global.memstore.lowerLimit:0.38
即:当MemStore使用内存总量达到hbase.regionserver.global.memstore.upperLimit指定值时,将会有多个MemStores flush到文件中,MemStore flush 顺序是按照大小降序执行的,直到刷新到MemStore使用内存略小于lowerLimit