一 HBase优化
1 预分区
每一个region维护着startRow与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。
(1)手动设定预分区
create 'staff1','info',SPLITS => ['1000','2000','3000','4000']
(2)生成16进制序列预分区
create 'staff2','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
(3) 按照文件中设置的规定预分区
aaaa
bbbb
cccc
dddd
create 'staff3','info',SPLITS_FILE => 'splits.txt'
(4)使用JavaAPI创建预分区
byte[][] splitKeys = 某个散列值函数
HBaseAdmin hAdmin = new HBaseAdmin(HbaseConfiguration.create());
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
hAdmin.createTable(tableDesc, splitKeys);
单独考虑预分区没有任何意义,需要结合下一小节RowKey的设计综合考虑
2 RowKey设计
一条数据的唯一标识就是rowkey,那么这条数据存储于哪个分区,取决于rowkey处于哪个一个预分区的区间内,设计rowkey的主要目的 ,就是让数据均匀的分布于所有的region中,在一定程度上防止数据倾斜。
rowkey设计 + 预分区 的原则: 唯一性 散列性 长度
**场景:**大量的运营商的通话数据,数据格式如下
1388888888(主叫) 13999999999(被叫) 2022-05-14 12:12:12 360 ......
**业务::**查询某个用户某天,某月,某年的通话记录
预分区: 预计规划50个分区 .
-∞| ~ 00| 00| ~ 01| 01| ~ 02 | …
分析: 假如将某个用户某天的数据存到一个分区中。查某天的数据只需要扫描一个分区 假如将某个用户某月的数据存到一个分区中。查某天,某月的数据只需要扫描一个分区. √
rowkey: 01_1388888888_2021-05-14 12:12:12 -> 1388888888_2021-05 % 分区数 = 01(通过月份对分区数取余,此例中分区数不确定) 01_1388888888_2021-05-15 12:12:12 -> 1388888888_2021-05 % 分区数 = 01 01_1388888888_2021-05-16 12:12:12 01_1388888888_2021-05-17 12:12:12
? 03_1377777777_2021-05-16 12:12:12 -> 1377777777_2021-05 % 分区数 = 03
验证:
**例一:**查询 1388888888 用户 2020年08月的通话记录 ① 先计算分区号 1388888888_2020-08 % 50 = 04 ② rowkey 04_1388888888_2020-08-… ③ scan
scan "teldata" ,{STARTROW=> '04_1388888888_2020-08' STOPROW=> '04_1388888888_2020-08|'}
**例二:**查询 1388888888 用户 2020年08月08日的通话记录 ① 先计算分区号 1388888888_2020-08 % 50 = 04 ② rowkey 04_1388888888_2020-08-08… ③ scan
scan "teldata" ,{STARTROW=> '04_1388888888_2020-08-08' STOPROW=> '04_1388888888_2020-08-08|'}
**例三:**查询 1388888888 用户 2020年08月 和 09月的通话记录
① 先计算分区号 1388888888_2020-08 % 50 = 04 1388888888_2020-09 % 50 = 06 ② rowkey 04_1388888888_2020-08-… 06_1388888888_2020-09-… ③ scan
scan "teldata" ,{STARTROW=> '04_1388888888_2020-08' STOPROW=> '04_1388888888_2020-08|'}
scan "teldata" ,{STARTROW=> '06_1388888888_2020-09' STOPROW=> '06_1388888888_2020-09|'}
**例四:**查询 1388888888 用户 2020年08月09日 和 10日的通话记录
① 先计算分区号 1388888888_2020-08 % 50 = 04 ② rowkey 04_1388888888_2020-08-09… 04_1388888888_2020-08-09… 04_1388888888_2020-08-10… ③ scan
scan "teldata" ,{STARTROW=> '04_1388888888_2020-08-09' STOPROW=> '04_1388888888_2020-08-10|'}
3 内存优化
HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16~36G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。
4 基础优化
hbase-site.xml
(1)Zookeeper会话超时时间
属性:zookeeper.session.timeout
解释:默认值为90000毫秒(90s)。当某个RegionServer挂掉,90s之后Master才能察觉到。可适当减小此值,以加快Master响应,可调整至60000毫秒。
(2)设置RPC监听数量
属性:hbase.regionserver.handler.count
解释:默认值为30,用于指定RPC监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。
(3)手动控制Major Compaction
属性:hbase.hregion.majorcompaction
解释:默认值:604800000秒(7天), Major Compaction的周期,若关闭自动Major Compaction,可将其设为0
(4)优化HStore文件大小
属性:hbase.hregion.max.filesize
解释:默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值,因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个Hfile。
(5)优化HBase客户端缓存
属性:hbase.client.write.buffer
解释:默认值2097152bytes(2M)用于指定HBase客户端缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存,反之会消耗更小的内存。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。
(6)指定scan.next扫描HBase所获取的行数
属性:hbase.client.scanner.caching
解释:用于指定scan.next方法获取的默认行数,值越大,消耗内存越大。
(7)BlockCache占用RegionServer堆内存的比例
属性:hfile.block.cache.size
解释:默认0.4,读请求比较多的情况下,可适当调大
(8)MemStore占用RegionServer堆内存的比例
属性:hbase.regionserver.global.memstore.size
解释:默认0.4,写请求较多的情况下,可适当调大
二 整合Phoenix
官方网站:http://phoenix.apache.org/
1 Phoenix简介
(1)定义
Phoenix是HBase的开源SQL皮肤。可以使用标准JDBC API代替HBase客户端API来创建表,插入数据和查询HBase数据。可以将Phoenix理解为客户端,也可以理解为一个数据库。
(2)特点
容易集成:如Spark,Hive,Pig,Flume和Map Reduce;
操作简单:DML命令以及通过DDL命令创建和操作表和版本化增量更改;
支持HBase二级索引创建。
(3)架构
thin client 通过Query Server服务将sql语句转化成Nosql语句,再将结果返回给thin client。
2 快速入门
(1)Phoenix安装
tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /opt/module/
mv apache-phoenix-5.0.0-HBase-2.0-bin phoenix-5.0.0
export PHOENIX_HOME=/opt/module/phoenix-5.0.0
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
cp /opt/module/phoenix/phoenix-5.0.0-HBase-2.0-server.jar /opt/module/hbase/lib/
xsync /opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-server.jar
stop-hbase.sh
start-hbase.sh
/opt/module/phoenix/bin/sqlline.py
!quit
sqlline.py hadoop101,hadoop102,hadoop103:2181
queryserver.py start
sqlline-thin.py hadoop101:8765
(2)Phoenix shell 操作
默认情况下,在phoenix中不能直接创建schema。需要将如下的参数添加到Hbase中conf目录下的hbase-site.xml 和 phoenix中bin目录下的 hbase-site.xml中
<property>
<name>phoenix.schema.isNamespaceMappingEnabled</name>
<value>true</value>
</property>
注意分发hbase的配置文件,重启hbase,queryserver.py
①增删改查创销
sqlline.py
create schema if not exists mydb;
list_namespace
create schema if not exists "mydb3";
drop schema if exists "mydb3";
create table if not exists student(
id varchar primary key,
name varchar,
addr varchar);
!tables
desc 'STUDENT'
upsert into student(id,name,addr) values('1001','zhangsan','beijin');
upsert into student(id,name,addr) values('1002','lisi','shanghai');
upsert into student(id,name,addr) values('1002','lixiaosi','tianjin');
select id,name,addr from student;
scan 'STUDENT'
scan 'STUDENT',{RAW=>true,VERSIONS=>5}
delete from student where id = '1002';
②联合主键
CREATE TABLE IF NOT EXISTS us_population (
State CHAR(2) NOT NULL,
City VARCHAR NOT NULL,
Population BIGINT
CONSTRAINT my_pk PRIMARY KEY (state, city));
upsert into us_population values('NY','New York',8143197) ;
upsert into us_population values('CA','Los Angeles',3844829) ;
③表的映射
create 'emp','info'
put 'emp','1001','info:name','zhangsan'
put 'emp','1001','info:addr','beijing'
create view "emp"(
id varchar primary key,
"info"."name" varchar,
"info"."addr" varchar
);
select * from "emp";
select id,"name","addr" from "emp";
drop view "emp";
create table "emp"(
id varchar primary key,
"info"."name" varchar,
"info"."addr" varchar
);
select * from "emp";
drop table "emp";
create table "emp"(
id varchar primary key,
"info"."name" varchar,
"info"."addr" varchar
)
COLUMN_ENCODED_BYTES=NONE;
④数值问题
# phoenix存, phoenix查。 没有问题
# phoenix存, hbase查。 有问题
# hbase存, hbase查。 没有问题
# hbase存, phoenix查。 有问题
create table test(
id varchar primary key,
name varchar,
salary integer
)
COLUMN_ENCODED_BYTES=NONE;
upsert into test values('1001','zs',123456);
scan 'TEST' #salary列不是数字
scan 'TEST',{COLUMNS => ['0:SALARY:toInt']} #数值出现问题
delete from test where id = '1001';
put 'TEST','1002','0:NAME','ls'
put 'TEST','1002','0:SALARY',Bytes.toBytes(456789) #Long
scan 'TEST',{COLUMNS => ['0:SALARY:toLong']} #没有问题
#使用无符号整数
create table test1 (
id varchar primary key ,
name varchar ,
salary UNSIGNED_INT
)
COLUMN_ENCODED_BYTES = NONE;
upsert into test1 values('1001','zs',123456);
put 'TEST1','1002','0:NAME','ls'
put 'TEST1','1002','0:SALARY',Bytes.toBytes(456789) // Long
(3)Phoenix JDBC操作
①Thin Client
启动客户端
queryserver.py startqueryserver.py start
创建项目,导入依赖
<dependencies>
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-queryserver-client</artifactId>
<version>5.0.0-HBase-2.0</version>
</dependency>
</dependencies>
public class PhoenixTest {
public static void main(String[] args) throws SQLException {
String connectionUrl = ThinClientUtil.getConnectionUrl("hadoop101", 8765);
Connection connection = DriverManager.getConnection(connectionUrl);
PreparedStatement preparedStatement = connection.prepareStatement("select * from student");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getString("id")
+ ":" + resultSet.getString("name"));
+ ":" + resultSet.getString("addr"));
}
connection.close();
}
}
②Thick Client
<dependencies>
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-core</artifactId>
<version>5.0.0-HBase-2.0</version>
<exclusions>
<exclusion>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b06</version>
</dependency>
</dependencies>
public class TestThick {
public static void main(String[] args) throws SQLException {
String url = "jdbc:phoenix:hadoop101,hadoop102,hadoop103:2181";
Properties props = new Properties();
props.put("phoenix.schema.isNamespaceMappingEnabled","true");
Connection connection = DriverManager.getConnection(url,props);
PreparedStatement ps = connection.prepareStatement("select * from \"test\"");
ResultSet rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getString(1)+":" +rs.getString(2));
}
}
}
3 Phoenix二级索引
Phoenix底层是hbase,hbase的一级索引是rowkey。
(1)配置文件
<!-- phoenix regionserver 配置参数-->
<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 uses separate 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 RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
cd /opt/module/hbase-2.0.5/conf/
xsync hbase-site.xml
(1)全局二级索引
所谓的全局二级索引,意味着建索引会创建一张索引表。
在索引表中, 将索引列与原表中的rowkey组合起来作为索引表的rowkey。
explain select id from student ;
explain select id from student where id = '1002' ;
explain select id from student where name = 'lixiaosi' ;
给name字段建索引
create index idx_student_name on student(name);
explain select id from student where name = 'lixiaosi' ;
explain select id ,name from student where id ='1001' ;
explain select id ,name from student where name ='lixiaosi' ;
explain select id ,name ,addr from student where name ='lixiaosi' ;
给name addr 建复合索引
drop index idx_student_name on student;
create index idx_student_name on student(name,addr);
explain select id ,name ,addr from student where name ='lixiaosi' ;
explain select id ,name ,addr from student where name ='lixiaosi' and addr = 'beijing';
explain select id ,name ,addr from student where addr = 'beijing';
explain select id ,name ,addr from student where addr = 'beijing' and name ='lixiaosi' ;
给name列建索引包含addr列,以后不使用addr进行过滤,只需要查找addr
drop index idx_student_name on student;
create index idx_student_name on student(name) include(addr);
explain select id ,name ,addr from student where name ='lixiaosi' ;
(2)本地二级索引
hbase中没有索引表,在原表上进行一些修改。
drop index idx_student_name on student;
create local index idx_student_name on student(name);
explain select id ,name ,addr from student where name ='lixiaosi' ;
三 hive与HBase集成
在hive-site.xml中添加zookeeper的属性,如下:
<property>
<name>hive.zookeeper.quorum</name>
<value>hadoop101,hadoop102,hadoop103</value>
</property>
<property>
<name>hive.zookeeper.client.port</name>
<value>2181</value>
</property>
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");
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';
load data local inpath '/opt/module/hive/datas/emp.txt' into table emp;
insert into hive_hbase_emp_table select * from emp;
2 Hbase中已经有表, 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");
|