首页 > 其他分享 >HBase学习的第五天--HBase的·拓展-Phenix

HBase学习的第五天--HBase的·拓展-Phenix

时间:2024-08-18 22:48:47浏览次数:12  
标签:name -- Phenix score user rowkey HBase id

六、Phoenix

3、phoenix表映射

默认情况下,直接在hbase中创建的表,通过phoenix是查看不到的

如果需要在phoenix中操作直接在hbase中创建的表,则需要在phoenix中进行表的映射。映射方式有两种:视图映射和表映射

3.1、视图映射

Phoenix创建的视图是只读的,所以只能用来做查询,无法通过视图对源数据进行修改等操作

# hbase shell 进入hbase命令行
hbase shell 

# 创建hbase表
create 'test2','name','company' 

# 插入数据
put 'test2','001','name:firstname','zhangsan1'
put 'test2','001','name:lastname','zhangsan2'
put 'test2','001','company:name','zhangsan3'
put 'test2','001','company:address','zhangsan4'


upsert into TEST values('002','xiaohu','xiaoxiao','zhangsan3','hefei');


# 在phoenix创建视图, primary key 对应到hbase中的rowkey
-- 创建视图之后,查询数据,主键是不需要加上双引号的
-- 其他字段在查询时需要加上双引号,包括表名
# 一开始在原表中取出非主键列时需要加上双引号
select empid from "test2";

create view "test2"(
empid varchar primary key,
"name"."firstname" varchar,
"name"."lastname"  varchar,
"company"."name"  varchar,
"company"."address" varchar
);

CREATE view "students" (
 id VARCHAR NOT NULL PRIMARY KEY, 
 "info"."name" VARCHAR,
 "info"."age" VARCHAR, 
 "info"."gender" VARCHAR,
 "info"."clazz" VARCHAR
);

# 在phoenix查询数据,表名通过双引号引起来
select * from "test";

# 删除视图
drop view "test";

'1500101004','张三',22,'男','理科一班'
put 'students','1500101001','info:name','xiaohu'
put 'students','1500101001','info:age','29'
put 'students','1500101001','info:gender','男'
put 'students','1500101001','info:clazz',"理科一班"

3.2、表映射

使用Apache Phoenix创建对HBase的表映射,有两类:

1) 当HBase中已经存在表时,可以以类似创建视图的方式创建关联表,只需要将create view改为create table即可。

2)当HBase中不存在表时,可以直接使用create table指令创建需要的表,并且在创建指令中可以根据需要对HBase表结构进行显示的说明。

第1)种情况下,如在之前的基础上已经存在了test表,则表映射的语句如下:

create table "test2" (
empid varchar primary key,
"name"."firstname" varchar,
"name"."lastname"varchar,
"company"."name"  varchar,
"company"."address" varchar
)column_encoded_bytes=0;
#表映射在建表时,映射过来的列是已经确定的,不会再改变,
#即使hbase中发生改变,phoenix也不会改变

upsert into "students" values('150011000100','张三','24','男','理科三班');

upsert into  "test"  values('1001','xiaohu','李四','阿里','杭州');

CREATE table "students" (
 id VARCHAR NOT NULL PRIMARY KEY, 
 "info"."name" VARCHAR,
 "info"."age" VARCHAR, 
 "info"."gender" VARCHAR,
 "info"."clazz" VARCHAR
) column_encoded_bytes=0;

# 增加和删除表的字段值是使用单引号
upsert into "students" values('150011000100','zhangsan','24','男','理科三班');

CREATE table  "scores" (
 id VARCHAR NOT NULL PRIMARY KEY, 
 "info"."score" VARCHAR
) column_encoded_bytes=0;

1、过滤出文科一班的学生
select ID as id,"name" as name,"clazz" as clazz from "students" where "clazz"='文科一班';


2、将成绩表做切分转换
select regexp_split(ID,'-')[1] as student_id,regexp_split(ID,'-')[2] as subject_id,"score" as score from "scores";
|         ID         | score |
+--------------------+-------+
| 1500100001-1000001 | 98    |
| 1500100001-1000002 | 5     |
| 1500100001-1000003 | 137   |
| 1500100001-1000004 | 29    |
| 1500100001-1000005 | 85    |
| 1500100001-1000006 | 52    |
| 1500100002-1000001 | 139   |
| 1500100002-1000002 | 102   |
| 1500100002-1000003 | 44    |
| 1500100002-1000004 | 18    |

3、在第二步的基础之上求每个学生的总分
select 
    t1.sid as score_id,
    sum(to_number(t1."score")) as sum_score
from(
    select
    	regexp_split(id,'-')[1] as sid
    	,regexp_split(id,'-')[2] as subject_id
    	,"score" 
    from "scores") t1
group by t1.sid;

4、在第3步的基础上将学生总分成绩降序排序,取前10名
 select 
    t1.sid as score_id,
    sum(to_number(t1."score")) as sum_score
    from(select
    regexp_split(id,'-')[1] as sid
    ,regexp_split(id,'-')[2] as subject_id
    ,"score" 
from "scores") t1
group by t1.sid order by sum_score desc limit 10;

5、在第四步的基础上关联学生信息表获取总分前十名学生的基本信息
select tt1.id
    ,tt1."name"
    ,tt1."age"
    ,tt1."gender"
    ,tt1."clazz"
    ,to_char(tt2.sum_score) as sum_sc
from "students" tt1
join
 (select 
    t1.sid as score_id,
    sum(to_number(t1."score")) as sum_score
    from(select
    regexp_split(id,'-')[1] as sid
    ,regexp_split(id,'-')[2] as subject_id
    ,"score" 
from "scores") t1
group by t1.sid order by sum_score desc limit 10) tt2
on tt1.id=tt2.score_id
order by sum_sc desc;

6、与步骤1的文科学生进行关联
select b1.id as student_id,b1.name as name,b1.clazz as clazz,to_char(b2.sum_score) as sum_score from (select ID as id,"name" as name,"clazz" as clazz from "students" where "clazz"='文科一班') b1 join (select t1.student_id as student_id,sum(to_number(t1.score)) as sum_score from (select regexp_split(ID,'-')[1] as student_id,regexp_split(ID,'-')[2] as subject_id,"score" as score from "scores") t1 group by t1.student_id) b2 on (b1.id=b2.student_id) order by b2.sum_score desc limit 10;

通过这个例子遇到的注意点:
1、切分字符串的函数不是split,而是regexp_split
2、Phoenix中,数组的索引是从1开始的
3、给字段起别名之后的嵌套查询,就不需要再加双引号了,主键本身就可以不用加
4、sum函数中的数据类型必须是数值类型,如果是10的整数倍,会以科学计数法进行标识 580->5.8E+2(5.8*10^2)
5、to_number()转数值  to_char()转字符串

"jdbc:phoenix:master,node2,node3:2181

使用create table创建的关联表,如果对表进行了修改,源数据也会改变,同时如果关联表被删除,源表也会被删除。但是视图就不会,如果删除视图,源数据不会发生改变。

视图映射和表映射的区别***

1、视图映射只能对映射过来的数据做查询操作,不能做增删改操作,而表映射是可以对映射过来的数据做增删改操作的。

2、在删除表时:对于视图映射删除映射表时,原HBase里的表不会被删除,而表映射删除映射表时,HBase中的原表和数据也都同样会被删除。

七、bulkLoad实现批量导入

优点:

  1. 如果我们一次性入库hbase巨量数据,处理速度慢不说,还特别占用Region资源, 一个比较高效便捷的方法就是使用 “Bulk Loading”方法,即HBase提供的HFileOutputFormat类

  2. 它是利用hbase的数据信息按照特定格式存储在hdfs内这一原理,直接生成这种hdfs内存储的数据格式文件,然后上传至合适位置,即完成巨量数据快速入库的办法。配合mapreduce完成,高效便捷,而且不占用region资源,增添负载。(只进行了数据的读操作,所以只需要map任务)

限制:

  1. 仅适合初次数据导入,即表内数据为空,或者每次入库表内都无数据的情况。
  2. HBase集群与Hadoop集群为同一集群,即HBase所基于的HDFS为生成HFile的MR的集群

代码编写:

提前在Hbase中创建好表

生成Hfile基本流程:

  1. 设置Mapper的输出KV类型:

    K: ImmutableBytesWritable(代表行键)

    V: KeyValue (代表cell)

​ 2. 开发Mapper

​ 读取你的原始数据,按你的需求做处理

​ 输出rowkey作为K,输出一些KeyValue(Put)作为V

​ 3. 配置job参数

​ a. Zookeeper的连接地址

​ b. 配置输出的OutputFormat为HFileOutputFormat2,并为其设置参数

​ 4. 提交job

​ 导入HFile到RegionServer的流程

​ 构建一个表描述对象

​ 构建一个region定位工具

​ 然后用LoadIncrementalHFiles来doBulkload操作

pom文件:

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-hdfs</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.phoenix</groupId>
            <artifactId>phoenix-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.lmax</groupId>
            <artifactId>disruptor</artifactId>
        </dependency>


    </dependencies>

    <build>
        <plugins>
            <!-- compiler插件, 设定JDK版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.8</source>
                    <target>1.8</target>
                    <showWarnings>true</showWarnings>
                </configuration>
            </plugin>


            <!-- 带依赖jar 插件-->
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

    </build>

八、HBase中rowkey的设计(重点!!)

HBase的RowKey设计

HBase是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这个三个维度可以对HBase中的数据进行快速定位。

HBase中rowkey可以唯一标识一行记录,在HBase查询的时候,有两种方式:

通过get方式,指定rowkey获取唯一一条记录

通过scan方式,设置startRow和stopRow参数进行范围匹配

全表扫描,即直接扫描整张表中所有行记录

rowkey长度原则

rowkey是一个二进制码流,可以是任意字符串,最大长度 64kb ,实际应用中一般为10-100bytes,以 byte[] 形式保存,一般设计成定长。

建议越短越好,不要超过16个字节,原因如下:

数据的持久化文件HFile中是按照KeyValue存储的,如果rowkey过长,比如超过100字节,1000w行数据,光rowkey就要占用100*1000w=10亿个字节,将近1G数据,这样会极大影响HFile的存储效率;

MemStore将缓存部分数据到内存,如果rowkey字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。

目前操作系统都是64位系统,内存8字节对齐,控制在16个字节,8字节的整数倍利用了操作系统的最佳特性。

rowkey散列原则

如果rowkey按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位作为散列字段,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个RegionServer,以实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。

rowkey唯一原则

必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的,因此,设计rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

什么是热点

HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。然而糟糕的rowkey设计是热点的源头。 热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。 设计良好的数据访问模式以使集群被充分,均衡的利用。

为了避免写热点,设计rowkey使得不同行在同一个region,但是在更多数据情况下,数据应该被写入集群的多个region,而不是一个。

下面是一些常见的避免热点的方法以及它们的优缺点:

加盐

这里所说的加盐不是密码学中的加盐,而是在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。

哈希

哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据

反转

第三种防止热点的方法时反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性

反转rowkey的例子以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题

时间戳反转

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到key的末尾,例如 [key]reverse_timestamp , [key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。

比如需要保存一个用户的操作记录,按照操作时间倒序排序,在设计rowkey的时候,可以这样设计

[userId反转]Long.Max_Value - timestamp,在查询用户的所有操作记录数据的时候,直接指定反转后的userId,startRow是[userId反转]000000000000,stopRow是[userId反转]Long.Max_Value - timestamp

如果需要查询某段时间的操作记录,startRow是[user反转]Long.Max_Value - 起始时间,stopRow是[userId反转]Long.Max_Value - 结束时间

其他一些建议

尽量减少行和列的大小在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。如果你的rowkey和列名很大,甚至可以和具体的值相比较,那么你将会遇到一些有趣的问题。HBase storefiles中的索引(有助于随机访问)最终占据了HBase分配的大量内存,因为具体的值和它的key很大。可以增加block大小使得storefiles索引再更大的时间间隔增加,或者修改表的模式以减小rowkey和列名的大小。压缩也有助于更大的索引。

列族尽可能越短越好,最好是一个字符

冗长的属性名虽然可读性好,但是更短的属性名存储在HBase中会更好

# 原数据:以时间戳_user_id作为rowkey
# 时间戳高位变化不大,太连续,最终可能会导致热点问题
1638584124_user_id
1638584135_user_id
1638584146_user_id
1638584157_user_id
1638584168_user_id
1638584179_user_id

# 解决方案:加盐、反转、哈希

# 加盐
# 加上随即前缀,随机的打散
# 该过程无法预测 前缀时随机的
00_1638584124_user_id
05_1638584135_user_id
03_1638584146_user_id
04_1638584157_user_id
02_1638584168_user_id
06_1638584179_user_id

# 反转
# 适用于高位变化不大,低位变化大的rowkey
4214858361_user_id
5314858361_user_id
6414858361_user_id
7514858361_user_id
8614858361_user_id
9714858361_user_id

# 散列 md5、sha1、sha256......
25531D7065AE158AAB6FA53379523979_user_id
60F9A0072C0BD06C92D768DACF2DFDC3_user_id
D2EFD883A6C0198DA3AF4FD8F82DEB57_user_id
A9A4C265D61E0801D163927DE1299C79_user_id
3F41251355E092D7D8A50130441B58A5_user_id
5E6043C773DA4CF991B389D200B77379_user_id

# 时间戳"反转"
# rowkey:时间戳_user_id
# rowkey是字典升序的,那么越新的记录会被排在最后面,不容易被获取到
# 需求:让最新的记录排在最前面

# 大数:9999999999
# 大数-小数

1638584124_user_id => 8361415875_user_id
1638584135_user_id => 8361415864_user_id
1638584146_user_id => 8361415853_user_id
1638584157_user_id => 8361415842_user_id
1638584168_user_id => 8361415831_user_id
1638584179_user_id => 8361415820_user_id

1638586193_user_id => 8361413806_user_id


标签:name,--,Phenix,score,user,rowkey,HBase,id
From: https://www.cnblogs.com/shmil/p/18366176

相关文章

  • 基础数据结构回顾记录
    数组初始化两种方式声明和初始化数组——使用new关键字和使用大括号。refer:https://www.freecodecamp.org/chinese/news/java-array-declaration-how-to-initialize-an-array-in-java-with-example-code/先声明,后赋值//数组声明dateType[]nameOfArray=newdataType......
  • 029、Vue3+TypeScript基础,路由组件和一般组件的存放位置,以及页面生命周期
    01、main.js代码如下://引入createApp用于创建Vue实例import{createApp}from'vue'//引入App.vue根组件importAppfrom'./App.vue'//引入路由importrouterfrom'./router'constapp=createApp(App);//使用路由app.use(router);//App.vue的根元素id为ap......
  • 关于c++使用toml plusplus(俗称toml++)的使用
    链接toml++-githubtoml++-帮助文档使用要求:c++17及以上版本toml语法-英文toml语法-中文toml读取参见官方给出的范例toml写入一个范例,一个开胃菜toml文件待生成的目标文件内容为[NET_INTERFACE]bool=falseinteger=1234567890string='thisis......
  • 截屏工具:PixPin
    省流:使用PixPin截图,设置格式为JPG。‍一、介绍PixPin‍一个好用的截图工具。官网:https://pixpinapp.com/​​‍支持功能:截图。支持界面元素识别,支持Ctrl+C​复制到剪贴板,Ctrl+C​粘贴到笔记,十分方便。贴图:支持将截图固定在屏幕上,拖动,放大,缩小。标注:支持划线,添......
  • Kubernetes 的架构和核心概念
    Kurbernetes是Google旗下的容器跨主机编排工具。Kurbernetes可以自动化应用容器的部署、扩展和操作,提供以容器为中心的基础架构。一、Kurbernetes集群架构与组件Kubernetes采用主从分布式架构,节点在角色上分为Maste和Node。KubernetesMaster是控制节点,负责k8s集群的调......
  • 阿里云ACP的三种报名与对应题库获取方式的详细说明(按费用排序)
    文章目录前言方式一、官方途径(较为昂贵)考试资格获取官方视频教程获取方式总结方式二、报名机构(价格适中,考取速度快)推荐机构大概费用机构报名方式机构报名后所携带的内容或者说对于其他方法有什么优势总结方式三、闲鱼(最便宜,但题库有风险)考试资格获取题库获取总......
  • VS常用拓展以及快捷键
    VS常用拓展以及快捷键扩展1:SelectNextOccurrence该拓展可以当前目标、下一个目标、上一个目标,类似于Alt+鼠标拖动,但是可以在没对齐的情况下使用安装设置4个常用的快捷键工具->选项->键盘->c#2005选择下一个快捷键:Ctrl+D选择上一个快捷键:Ctrl+E撤销......
  • simple sprintf wrapper
    version0#include<memory>#include<string>#include<stdexcept>template<typename...Args>std::stringstring_format(conststd::string&format,Args...args){intsize_s=std::snprintf(nullptr,0,format.c_str(),......
  • 单调栈
    单调证需要一直保证栈中元素是按序排列的。插入元素时首先检查,循环检查栈顶元素是否符合条件,不符合则弹出。不需要再将弹出元素插入回去。如果插入回去的话,其实整套程序逻辑实现就会多此一举,不如直接插入之后sort()即可。classMyMonoStack{public://constructorMyMo......
  • 叠Buff!经典麻雀优化算法+多重双向深度学习!SSA-BiTCN-BiGRU-Attention多输入单输出回
    叠Buff!经典麻雀优化算法+多重双向深度学习!SSA-BiTCN-BiGRU-Attention多输入单输出回归预测目录叠Buff!经典麻雀优化算法+多重双向深度学习!SSA-BiTCN-BiGRU-Attention多输入单输出回归预测效果一览基本介绍程序设计参考资料效果一览基本介绍1.Matlab实现SS......