day01
一、大纲形式
1. 操作系统
2. 开源
3. 免费
4. 多用户
5. 多进程
6. 多线程
7. 性能稳定
8. 安全
(权限的管理
)
解决了大数据集如何存储的问题
解决了分布式系统上的大数据集如何快速,高效的分析与计算的问题,是一个运行在hdfs上的并发的计算与分析框架
是一个资源
(内存,cpu,磁盘
)管理框架
是一个为分布式应用程序提供协调服务的分布式框架。
是一款数据仓库的工具,可以将HDFS上的具有结构化的文件映射
(描述信息,也叫元数据,存储在MYSQL
)成一张表。可以使用类sql语言进行管理。其实底层就是MR、SPARK、TEZ.
1、Linux
1. 概述
2. 常用指令
3. VI/VIM编辑工具
4. 网络配置(静态IP的设置)(重点)
5. 用户和权限管理(重点)
6. 软件包的安装
7. 虚拟机的克隆(学习期间 一定要会)
8. scp命令
9. 免密登录认证(原理,重点)
10. 时间同步(重点)
11. shell脚本(重点)
2、HDFS
1. 概述
--4V(重点)
--google的三篇论文:《GFS》《MapReduce》《Bigtable》
2. 安装:
-- 单节点:
--1)本地模式
--2) 伪分布式:使用分布式文件系统,守护进程都是独立的(每一个进程都有自己的jvm)
-- 多节点(重点)
-- 完全分布式:使用分布式文件系统,守护进程是分布到各个节点的。也都是独立的进程。
3. 块的概念(重点)
-- 合理的块的设计,都是为了解决稀缺的网络带宽和负载均衡问题
-- 块是hdfs的存储单元
-- 块的大小是相同的(除了最后一个块)
-- 块的大小选择(参考的是寻址时间与传输时间的比例,认为在1:100的这个比例是最优)
4. hdfs的体系结构(重点)
5. hdfs的工作机制
-- 开机启动过程
-- 安全模式
-- 检查点机制 (重点)
-- 心跳反馈(10分30秒)(重点)
-- 网络拓扑
-- 机架感知(2.8.2是一个分水岭, 版本之前是 前两个副本一个机架, 版本之后是后两个副本在同一个机架)(重点)
-- 动态的上下线
6. 读写流程(重中之重)
7. API
-- 关心1: 此方法是静态的还是非静态的
-- 关心2: 此方法的形参类型
-- 关心3: 此方法的返回值类型
3、Mapreduce
1. 概述
-- 框架的思想(重点)
移动计算而非移动数据,分而治之,然后进行汇总处理。
数据的扭转: fetch
原始数据--><K1,V1>-->map函数--<K2,V2>---><K2,List<V2>>--->reduce函数---><K3,V3>
2. 入门案例
-- 分片机制(重点)
inputSplit--->FileSplit(path,start,length,hosts)
-- 分片的特点
3. 序列化机制
4. MapTask的流程(重点)
5. ReduceTask的流程(重点)
6. shuffle流程(重中之重)
7. yarn的job提交流程(重中之重)
8. 经典案例:
--topN案例
--共同好友案例
--自定义输入格式案例
--Map-join
--Reduce-join
4、Zookeeper
1. 简介与特点
--本身是一个分布式集群框架,一个leader,多个follower
--适合安装在奇数台机器上,能正常服务的条件是,(n+1)/2台机器正常运行
允许(n-1)/2台机器宕机
-- 每个服务节点的数据一致(重点理解)
2. 安装:
集群模式(重点)
3. 数据模型:(重点)
类似Linux/Unix的文件系统的多层次的数据结构。每个节点称之为znode. znode可以记录子节点的名字和个数(相当于文件系统的目录),还可以存储1M以内的数据(相当于文件系统的文件).znode通过路径进行唯一标识。
每一个服务节点上都有这个数据模型,因此客户端连接上任务一个服务节点,数据都是一致的。
4. 选举制度
权重: epoch > zxid >serverid
5. 监听和通知机制(重点理解)
6. 应用场景(重点):
-- 集群的管理,比如HA
-- 配置文件的管理
-- 服务器的动态上下线感知
-- 分布式锁
-- 分布式队列
5、HIVE
1. 概念和体系结构,工作流程
-- 体系结构(重点)
-- 工作流程(重点)
2. hive的安装
-- 内嵌模式: derby,只支持单session
-- 本地模式: hive客户端指令会内置开启metastore服务项,自己连接自己,与mysql在哪一台机器上无关。
-- 远程模式: hive的服务项(hiveserver2或metastore)是单独开启的,然后供客户端指令去连接。
重点理解本地模式和远程模式, 实际生产环境中本地和远程用的一样多。
3. hive的库和表操作
-- 本地上,库和表都是hdfs上的一个目录
4. hive的表类型
-- 内部表
-- 外部表
5. hive的基本查询子句
-- left semi join(与exists的原理相同)
-- map-side-join
6. 函数
-- 日期函数
-- 字符串函数
-- 数学函数
-- 开窗函数(重点)
--1) 排名函数
--2) 聚合函数
--3) 序列函数
-- 自定义函数
7. serde
8. 分区与分桶(重点)
二、Hbase的讲解
1、Hbase的简介
1. hadoop base 缩写成Hbase
-- 开源的,基于hdfs的分布式的,可扩展的,面向列式存储,非关系型(nosql->not only sql)的数据库
-- 数据可以有多个版本(历史版本)
-- 提供了高可靠性
-- 本身是基于内存的(高性能,近似实时的访问)
-- 起源于google的《bigtable》论文
2. 与hive的区别
-- hive是用于OLAP,提供类sql语言的分析与计算的框架,底层就是MR。
-- hbase是用于存储的,设计目录是想存储数十亿行X数百万列的大数据集
2、Hbase的表模型
2.1 关系型数据库的表模型(扩展)
关系型数据库的表模型:面向行式存储。在定义表结构时,需要提前定义好列名,列类型,列数目
缺点:
1. 一旦数据表中存储数据后,修改表结构变得特别困难。
2. 如果我们想扩展字段时,会对表结构产生影响。
3. 即使某一行中的某个字段没有赋值,也要使用null填充
4. 一旦涉及到多张表,因为数据表存在着复杂的关系,管理非常不方便。
5. 一旦面对海量数据的处理时,读写性能特别差,尤其在高并发这一块。
2.2 Hbase的表模型
1)Cell
hbase是面向列式存储的表模型,列指的是KV对,这个key就是column,v就是column对应的值。KV对被称之单元格,也就是cell。单元格有属于自己的版本号,其实就是一个时间戳。
2)rowkey
为了表示某些单元格是同一个事物的,所以引入了rowkey的概念。因此rowkey是不可以重复的,否则会出现覆盖情况。
3)column family(列族)
为了更好的方便管理单元格,以及有相同意义的单元格尽可能的汇聚到一起,所以引入了列族的概念。
4)region
region是hbase的物理存储模型,是整张表(数据量比较小的时候),或者是表的一部分(数据量比较大时,有多个region)
5)排序机制
由于hbase是优先基于内存存储的,因此内存中的数据进行排序,排序规则是字典排序(ascii升序)
排序方式:
-- 先按照rowkey进行排序
-- 然后按照列族进行排序
-- 再按照key进行排序
-- 如果是多个版本,会按照时间戳进行降序排序
6)数据类型
hbase是不支持其他类型的维护的,底层就是byte[]类型
3、Hbase的体系结构
4、Hbase的安装
4.1 单机模式
步骤1)上传并解压,更名
[root@qianfeng01 ~
]
[root@qianfeng01 ~
]
[root@qianfeng01 local
]
步骤2)配置环境变量并重新引导
[root@qianfeng01 local
]
.........省略
........
export HBASE_HOME
=/usr/local/hbase
export PATH
=$HBASE_HOME/bin:
$PATH
[root@qianfeng01 local
]
步骤3)修改hbase的环境脚本(hbase-env.sh)
[hadoop@qianfeng01 local
]$ vim
$HBASE_HOME/conf/hbase-env.sh
export JAVA_HOME
=/usr/local/jdk
export HBASE_MANAGES_ZK
=true
步骤4)修改hbase的site.xml(自定义配置文件)
<configuration>
<property>
<name>hbase.rootdir
</name>
<value>file:///usr/local/hbase/data
</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir
</name>
<value>/usr/local/hbase/zkdata
</value>
</property>
</configuration>
步骤5)可以开心的玩了
start-hbase.sh
hbase shell
stop-hbase.sh
4.2 伪分布式模式
4.2.1 情况说明
hbase的伪分布式,其实就是指相关的守护进程都有,并且运行在同一个机器上,是独立的进程(每一个守护进程都有自己的JVM)
-- hbase的伪分布式的数据的存储位置,可以是hdfs,也可以是本地文件系统。
-- hbase的伪分布式如果选择hdfs,针对于zookeeper来说,可以选择内置的,也可以我们自行安装的。
下面的演示是自行安装的zookeeper
步骤1)上传并解压,更名
[root@qianfeng01 ~
]
[root@qianfeng01 ~
]
[root@qianfeng01 local
]
步骤2)配置环境变量并重新引导
[root@qianfeng01 local
]
.........省略
........
export HBASE_HOME
=/usr/local/hbase
export PATH
=$HBASE_HOME/bin:
$PATH
[root@qianfeng01 local
]
步骤3)修改hbase的环境脚本(hbase-env.sh)
[hadoop@qianfeng01 local
]$ vim
$HBASE_HOME/conf/hbase-env.sh
export JAVA_HOME
=/usr/local/jdk
export HBASE_MANAGES_ZK
=false
步骤4)修改hbase的site.xml(自定义配置文件)
<configuration>
<property>
<name>hbase.rootdir
</name>
<value>hdfs://qianfeng01/hbase
</value>
</property>
<property>
<name>hbase.cluster.distributed
</name>
<value>true
</value>
</property>
<property>
<name>hbase.zookeeper.quorum
</name>
<value>qianfeng01:2181,qianfeng02:2181,qianfeng03:2181
</value>
</property>
<property>
<name>hbase.unsafe.stream.capability.enforce
</name>
<value>true
</value>
</property>
</configuration>
步骤5)可以开心的玩了
由于使用自行安装的zookeeper,所以再启动hbase服务进程时,应该提前开启zookeeper集群
zkServer.sh start <---三台一起开
start-hbase.sh
hbase shell
stop-hbase.sh
4.3 完全分布式模式
4.3.1 情况说明和布局安排
4.3.2 安装步骤
步骤1)上传并解压,更名
[root@qianfeng01 ~
]
[root@qianfeng01 ~
]
[root@qianfeng01 local
]
步骤2)配置环境变量并重新引导
[root@qianfeng01 local
]
.........省略
........
export HBASE_HOME
=/usr/local/hbase
export PATH
=$HBASE_HOME/bin:
$PATH
[root@qianfeng01 local
]
步骤3)修改hbase的环境脚本(hbase-env.sh)
[hadoop@qianfeng01 local
]$ vim
$HBASE_HOME/conf/hbase-env.sh
export JAVA_HOME
=/usr/local/jdk
export HBASE_MANAGES_ZK
=false
步骤4)修改hbase的site.xml(自定义配置文件)
<configuration>
<property>
<name>hbase.rootdir
</name>
<value>hdfs://qianfeng01/hbase
</value>
</property>
<property>
<name>hbase.cluster.distributed
</name>
<value>true
</value>
</property>
<property>
<name>hbase.zookeeper.quorum
</name>
<value>qianfeng01:2181,qianfeng02:2181,qianfeng03:2181
</value>
</property>
<property>
<name>hbase.unsafe.stream.capability.enforce
</name>
<value>true
</value>
</property>
</configuration>
步骤5)三台机器的免密登录认证要做好
步骤6)三台机器的时间一定要同步,不能超过30秒
步骤7)配置regionserver的布局
[root@qianfeng01 hbase
]
qianfeng01
qianfeng02
qianfeng03
步骤8) 配置备份的hmaster
[root@qianfeng01 hbase
]
步骤9)将hadoop的core-site.xml和hdfs-site.xml拷贝到hbase的conf目录下
[root@qianfeng01 hbase
]
步骤10)分发到其他两台机器上
[root@qianfeng01 local
]
[root@qianfeng01 local
]
[root@qianfeng01 local
]
[root@qianfeng01 local
]
最好去另外两台机器上,重新加载一下环境变量的文件
步骤11)启动hbase
hdfs和zookeeper一定要先启动,
然后再启动hbase的服务进程
启动后,要去webui界面查看一下,ip:16010
如果启动失败,应该去相关进程所在的机器上的hbase的家里的logs目录下查看响应的日志文件
day02
一 Regionserver的动态上下线(了解)
1.1 动态上线
1.1.1 原理
当我们启动一个新的regionserver机器时,会主动向zookeeper的某一个znode(/hbase/rs/)下创建一个临时节点,名字是代表自己的唯一标识。zookeeper会通知master,master收到信息后会进行维护新的regionserver,比如分region等等。
1.1.2 环境准备
准备1)搭建一台新机器:包括jdk,hadoop的环境,hdfs的动态上线。
hdfs的动态上线的步骤:
<property
>
<name
>dfs.hosts
</name
>
<value
>/usr/local/hadoop/etc/hadoop/include
</value
>
</property
>
hdfs dfsadmin -refreshNodes
[root@qianfeng04 local
]
准备2)准备hbase的环境
可以scp, 并且将/etc/profile也拷贝过去, 保证新机器上的hbase的配置要与hmaster上的一致。
1.1.3 上线步骤
[root@qianfeng04 local
]
1.2 动态下线
1.2.1 原理
regionserver与zookeeper维护一个会话,在/hbase/rs下有一个代表自己的唯一标识znode。如果regionserver动态下线,那么这个会话会断开,zookeeper会删除此唯一标识znode。master也会与zookeeper时刻保证一个会话,也就是监听/hbase/rs下的znode的增删变量,当有动态下线的机器时,重新分配下线的regionserver上的region,
最后删除regionserver,停止服务。
1.2.2 下线步骤
[root@qianfeng01 local
]
二、hbase shell操作(熟悉)
1.namespace的DDL
##1. list_namespace
##2. create_namespace
##3. describe_namespace
##4. alter_namespace: 可以add/modify 也可以delete属性
##5. list_namespace_tables
##6. drop_namespace: 不能删除非空的namespace
2.table的DDL
##1. create
##2. desc|describe
##3. alter: 可以增删列族,还可以修改列族的属性
##4. list: 不能列出系统表
##5. disable
##6. drop
##7. disable_all,enable_all,drop_all
3.table的CRUD(DML+DQL)
## 插入数据使用put
## 修改数据使用put
## 查询表数据scan: 浏览整个表,或者rowkey范围的所有行记录
## 查询表数据get: 指定行进行查询
## delete :删除单元格
## deleteall: 删除指定行
## count
## truncate
## exists
三、Hbase的API操作(重点)
0.准备工作
1. 创建maven项目:sz2002_hbase
2. 导入jar包(坐标)
<dependencies>
<dependency>
<groupId>org.apache.hbase
</groupId>
<artifactId>hbase-client
</artifactId>
<version>1.2.1
</version>
</dependency>
</dependencies>
3. 导入log4j.properties
注意事项: 因为要访问zookeeper,必须使用域名(ip无效),因此要在windows里的hosts文件里配置映射关系
C:/windows/system32/drivers/etc/hosts
1.namespace的DDL
createNamespace()
describeNamespace()
alterNamespaceAddProperties()
alterNamespaceDropProperties()
listAllNamespace()
listTablesOfSpecifiedNamespace()
listAllTable()
listTablesOfALLNamespace()
deleteNamespace()
package com
.qf
.hbase
.api
;
import com
.qf
.hbase
.util
.HbaseUtil
;
import org
.apache
.hadoop
.hbase
.NamespaceDescriptor
;
import org
.apache
.hadoop
.hbase
.TableName
;
import org
.apache
.hadoop
.hbase
.client
.Admin
;
import org
.junit
.After
;
import org
.junit
.Before
;
import org
.junit
.Test
;
import java
.io
.IOException
;
import java
.util
.Map
;
import java
.util
.Set
;
public class NamespaceDDL {
private Admin admin
;
@Before
public void getAdmin() throws IOException
{
admin
= HbaseUtil
.getAdmin();
}
@After
public void closeAdmin(){
HbaseUtil
.closeAdmin(admin
);
}
@Test
public void createNamespace() throws IOException
{
NamespaceDescriptor
.Builder builder
= NamespaceDescriptor
.create("myns2");
NamespaceDescriptor namespace
= builder
.build();
admin
.createNamespace(namespace
);
}
@Test
public void describeNamespace() throws IOException
{
NamespaceDescriptor myns2
= admin
.getNamespaceDescriptor("myns2");
Map
<String, String> map
= myns2
.getConfiguration();
Set
<String> keys
= map
.keySet();
for (String key
: keys
) {
System
.out
.println(key
+"="+map
.get(key
));
}
}
@Test
public void alterNamespaceAddProperties() throws IOException
{
NamespaceDescriptor myns2
= admin
.getNamespaceDescriptor("myns2");
myns2
.setConfiguration("author","michael");
myns2
.setConfiguration("time","2020-09-01");
myns2
.setConfiguration("company","qf");
admin
.modifyNamespace(myns2
);
}
@Test
public void alterNamespaceDropProperties() throws IOException
{
NamespaceDescriptor myns2
= admin
.getNamespaceDescriptor("myns2");
myns2
.removeConfiguration("time");
admin
.modifyNamespace(myns2
);
}
@Test
public void listAllNamespace() throws IOException
{
NamespaceDescriptor
[] namespaceDescriptors
= admin
.listNamespaceDescriptors();
for (NamespaceDescriptor namespaceDescriptor
: namespaceDescriptors
) {
System
.out
.println(namespaceDescriptor
.getName());
}
}
@Test
public void listTablesOfSpecifiedNamespace() throws IOException
{
TableName
[] tables
= admin
.listTableNamesByNamespace("myns1");
for (TableName tableName
: tables
) {
System
.out
.println(tableName
);
}
}
@Test
public void listAllTable() throws IOException
{
TableName
[] tables
= admin
.listTableNames();
for (TableName tableName
: tables
) {
System
.out
.println(tableName
);
}
}
@Test
public void listTablesOfALLNamespace() throws IOException
{
NamespaceDescriptor
[] namespaceDescriptors
= admin
.listNamespaceDescriptors();
for (NamespaceDescriptor namespaceDescriptor
: namespaceDescriptors
) {
TableName
[] tableNames
= admin
.listTableNamesByNamespace(namespaceDescriptor
.getName());
for (TableName tableName
: tableNames
) {
System
.out
.println(tableName
.getNameAsString());
}
}
}
@Test
public void deleteNamespace() throws IOException
{
admin
.deleteNamespace("ns11");
}
}
2.table的DDL
createTable()
describeTable()
alterTableProperties()
alterTableAddFamily()
alterTableDropFamily()
dropTable()
package com
.qf
.hbase
.api
;
import com
.qf
.hbase
.util
.HbaseUtil
;
import org
.apache
.hadoop
.hbase
.HColumnDescriptor
;
import org
.apache
.hadoop
.hbase
.HTableDescriptor
;
import org
.apache
.hadoop
.hbase
.TableName
;
import org
.apache
.hadoop
.hbase
.client
.Admin
;
import org
.apache
.hadoop
.hbase
.regionserver
.BloomType
;
import org
.apache
.hadoop
.hbase
.util
.Bytes
;
import org
.junit
.After
;
import org
.junit
.Before
;
import org
.junit
.Test
;
import java
.io
.IOException
;
import java
.util
.Map
;
public class TableDDL {
private Admin admin
;
@Before
public void getAdmin() throws IOException
{
admin
= HbaseUtil
.getAdmin();
}
@After
public void closeAdmin(){
HbaseUtil
.closeAdmin(admin
);
}
@Test
public void createTable() throws IOException
{
TableName tableName
= TableName
.valueOf("myns2:student");
HTableDescriptor hTableDescriptor
= new HTableDescriptor(tableName
);
HColumnDescriptor hColumnDescriptor
= new HColumnDescriptor("base_info".getBytes());
hColumnDescriptor
.setBlocksize(1024*64*2);
hColumnDescriptor
.setVersions(1,3);
hColumnDescriptor
.setTimeToLive(3600*24*7);
hTableDescriptor
.addFamily(hColumnDescriptor
);
admin
.createTable(hTableDescriptor
);
}
@Test
public void describeTable() throws IOException
{
HTableDescriptor tableDescriptor
= admin
.getTableDescriptor(TableName
.valueOf("myns2:student"));
HColumnDescriptor
[] columnFamilies
= tableDescriptor
.getColumnFamilies();
for (HColumnDescriptor columnFamily
: columnFamilies
) {
System
.out
.print("列族名:"+columnFamily
.getNameAsString()+"\t");
System
.out
.print("块大小:"+columnFamily
.getBlocksize()+"\t");
System
.out
.print("布隆过滤器:"+columnFamily
.getBloomFilterType()+"\t");
System
.out
.print("版本数:"+columnFamily
.getMaxVersions()+"\t");
System
.out
.println("存活时间:"+columnFamily
.getTimeToLive());
}
}
@Test
public void alterTableProperties() throws IOException
{
HTableDescriptor tableDescriptor
= admin
.getTableDescriptor(TableName
.valueOf("myns2:student"));
HColumnDescriptor base_info
= tableDescriptor
.getFamily(Bytes
.toBytes("base_info"));
base_info
.setTimeToLive(3600*24*5);
base_info
.setVersions(1,5);
admin
.modifyColumn(TableName
.valueOf("myns2:student"),base_info
);
}
@Test
public void alterTableAddFamily() throws IOException
{
TableName tableName
= TableName
.valueOf("myns2:student");
HColumnDescriptor hColumnDescriptor1
= new HColumnDescriptor("f1");
hColumnDescriptor1
.setVersions(1,3);
HColumnDescriptor hColumnDescriptor2
= new HColumnDescriptor("f2");
hColumnDescriptor2
.setTimeToLive(3600*3);
HColumnDescriptor hColumnDescriptor3
= new HColumnDescriptor("f3");
hColumnDescriptor3
.setBloomFilterType(BloomType
.ROWCOL
);
admin
.addColumn(tableName
,hColumnDescriptor1
);
admin
.addColumn(tableName
,hColumnDescriptor2
);
admin
.addColumn(tableName
,hColumnDescriptor3
);
}
@Test
public void alterTableDropFamily() throws IOException
{
TableName tableName
= TableName
.valueOf("myns2:student");
byte[] columnName
= "f3".getBytes();
admin
.deleteColumn(tableName
,columnName
);
}
@Test
public void dropTable() throws IOException
{
TableName tableName
= TableName
.valueOf("myns2:student");
if(admin
.tableExists(tableName
)){
if(!admin
.isTableDisabled(tableName
)){
admin
.disableTable(tableName
);
}
admin
.deleteTable(tableName
);
}
}
}
3.table的CRUD(DML+DQL)
putOneRowData()
putBatchData()
getOneRowData()
-------------------------------------
getMultiRowData()
scanMultiRowData()
deletOneRowData()
deleteMultiRowData()
deleteOneCell()
package com
.qf
.hbase
.api
;
import com
.qf
.hbase
.util
.HbaseUtil
;
import org
.apache
.hadoop
.conf
.Configuration
;
import org
.apache
.hadoop
.hbase
.Cell
;
import org
.apache
.hadoop
.hbase
.CellUtil
;
import org
.apache
.hadoop
.hbase
.client
.*
;
import org
.apache
.hadoop
.hbase
.util
.Bytes
;
import org
.junit
.After
;
import org
.junit
.Before
;
import org
.junit
.Test
;
import java
.io
.IOException
;
import java
.util
.ArrayList
;
import java
.util
.Iterator
;
import java
.util
.List
;
public class TableCRUD {
private Table table
;
@Before
public void getTable() throws IOException
{
table
= HbaseUtil
.getTable("myns2:student");
}
@After
public void closeTable() {
HbaseUtil
.closeTable(table
);
}
@Test
public void putOneRowData() throws IOException
{
Put put
= new Put("rk02004".getBytes());
put
.addColumn("base_info".getBytes(), Bytes
.toBytes("name"), "hanxin".getBytes());
put
.addColumn("f1".getBytes(), Bytes
.toBytes("province"), "广东".getBytes());
table
.put(put
);
}
@Test
public void putBatchData() throws IOException
{
List
<Put> puts
= new ArrayList<Put>();
for (int i
= 2; i
< 1010; i
++) {
String rowkey
= "";
if (i
< 10) {
rowkey
= "rk0000" + i
;
} else if (i
< 100) {
rowkey
= "rk000" + i
;
} else if (i
< 1000) {
rowkey
= "rk00" + i
;
} else {
rowkey
= "rk0" + i
;
}
Put put
= new Put(rowkey
.getBytes());
put
.addColumn("base_info".getBytes(), "name".getBytes(), Bytes
.toBytes("zhaoyun" + i
));
put
.addColumn("base_info".getBytes(), "age".getBytes(), Bytes
.toBytes((int) (Math
.random() * 90) + 10 + ""));
int num
= (int) (Math
.random() * 2);
String gender
= num
== 0 ? "f" : "m";
put
.addColumn("base_info".getBytes(), "gender".getBytes(), Bytes
.toBytes(gender
));
puts
.add(put
);
if (i
% 300 == 0) {
table
.put(puts
);
puts
.clear();
}
}
table
.put(puts
);
}
@Test
public void getOneRowData() throws IOException
{
Get get
= new Get("rk00009".getBytes());
Result result
= table
.get(get
);
while(result
.advance()){
Cell cell
= result
.current();
System
.out
.print(new String(CellUtil
.cloneRow(cell
))+"\t");
System
.out
.print(new String(CellUtil
.cloneFamily(cell
))+"\t");
System
.out
.print(new String(CellUtil
.cloneQualifier(cell
))+"\t");
System
.out
.println(new String(CellUtil
.cloneValue(cell
)));
}
}
@Test
public void getMultiRowData() throws IOException
{
List
<Get> list
= new ArrayList<Get>();
Get g1
= new Get(Bytes
.toBytes("rk00001"));
Get g2
= new Get(Bytes
.toBytes("rk00002"));
Get g3
= new Get(Bytes
.toBytes("rk00003"));
Get g4
= new Get(Bytes
.toBytes("rk00004"));
list
.add(g1
);
list
.add(g2
);
list
.add(g3
);
list
.add(g4
);
Result
[] results
= table
.get(list
);
for (Result result
: results
) {
HbaseUtil
.printResult(result
);
}
}
@Test
public void scanMultiRowData() throws IOException
{
Scan scan
= new Scan("rk00001".getBytes(),("rk00020"+"\000").getBytes());
ResultScanner scanner
= table
.getScanner(scan
);
Iterator
<Result> iterator
= scanner
.iterator();
while(iterator
.hasNext()){
Result result
= iterator
.next();
HbaseUtil
.printResult(result
);
}
}
@Test
public void deletOneRowData() throws IOException
{
Delete d1
= new Delete(Bytes
.toBytes("rk0001010"));
table
.delete(d1
);
}
@Test
public void deleteMultiRowData() throws IOException
{
List
<Delete> list
= new ArrayList<Delete>();
Delete d1
= new Delete(Bytes
.toBytes("rk00010"));
Delete d2
= new Delete(Bytes
.toBytes("rk00011"));
Delete d3
= new Delete(Bytes
.toBytes("rk00012"));
Delete d4
= new Delete(Bytes
.toBytes("rk00013"));
list
.add(d1
);
list
.add(d2
);
list
.add(d3
);
list
.add(d4
);
table
.delete(list
);
}
@Test
public void deleteOneCell() throws IOException
{
Delete d1
= new Delete(Bytes
.toBytes("rk00009"));
d1
.addColumn("base_info".getBytes(),"gender".getBytes());
table
.delete(d1
);
}
}
day03
一、API(续)
1.table的CRUD(DML+DQL)(续)
putOneRowData()
putBatchData()
getOneRowData()
-------------------------------------续讲
getMultiRowData()
scanMultiRowData()
deletOneRowData()
deleteMultiRowData()
deleteOneCell()
2.hbase的过滤器(重点)
2.1 为什么要学习过滤器
因为之前学习的查询有局限性,只能查询指定行号的或者行范围的,或者是查询column叫什么名字的这些情况
不能查询单元格的值是什么,比如name=zhangsan or age = 23的这种需求。
过滤器是可以帮助我们实现上述需求。
2.2过滤器的分类
1. 列值(ColumnValue)过滤器
-- SingleColumnValueFilter : select *
2. 结构(Structural)过滤器
-- FilterList: 可以绑定多个过滤器(条件)
3. keyvalue元数据(metadata)过滤器 : 查询的都是符合指定条件的单元格数据,不是select *
-- FamilyFilter
-- QualifierFilter
-- ColumnPrefixFilter
-- MultipleColumnPrefixFilter
-- ColumnRangeFilter
4. 行键(RowKey)过滤器
-- RowFilter : 查询效果是select *
5. 实用(utility)过滤器
-- FirstKeyOnlyFilter : 查询效果是每一行中的第一个单元格
6. 分页过滤器
-- PageFilter
package com
.qf
.hbase
.api
;
import com
.qf
.hbase
.util
.HbaseUtil
;
import org
.apache
.hadoop
.hbase
.client
.Result
;
import org
.apache
.hadoop
.hbase
.client
.ResultScanner
;
import org
.apache
.hadoop
.hbase
.client
.Scan
;
import org
.apache
.hadoop
.hbase
.client
.Table
;
import org
.apache
.hadoop
.hbase
.filter
.*
;
import org
.apache
.hadoop
.hbase
.util
.Bytes
;
import org
.apache
.hadoop
.yarn
.webapp
.view
.HtmlPage
;
import org
.junit
.After
;
import org
.junit
.Before
;
import org
.junit
.Test
;
import java
.io
.IOException
;
import java
.util
.Iterator
;
public class FilterDemo {
private Table table
;
@Before
public void getTable() throws IOException
{
table
= HbaseUtil
.getTable("myns2:student");
}
@After
public void closeTable() {
HbaseUtil
.closeTable(table
);
}
@Test
public void testSingleColumnValueFilter() throws IOException
{
SingleColumnValueFilter singleColumnValueFilter
= new SingleColumnValueFilter(
"base_info".getBytes(),"age".getBytes(), CompareFilter
.CompareOp
.EQUAL
,"23".getBytes());
singleColumnValueFilter
.setFilterIfMissing(true);
Scan scan
= new Scan();
scan
.setFilter(singleColumnValueFilter
);
ResultScanner scanner
= table
.getScanner(scan
);
Iterator
<Result> iterator
= scanner
.iterator();
while (iterator
.hasNext()){
Result next
= iterator
.next();
HbaseUtil
.printResult(next
);
}
}
@Test
public void testFilterList() throws IOException
{
SingleColumnValueFilter ageFilter
= new SingleColumnValueFilter(
"base_info".getBytes(), Bytes
.toBytes("age"), CompareFilter
.CompareOp
.GREATER
,"89".getBytes()
);
SingleColumnValueFilter genderFilter
= new SingleColumnValueFilter(
"base_info".getBytes(), Bytes
.toBytes("gender"), CompareFilter
.CompareOp
.EQUAL
,"m".getBytes()
);
ageFilter
.setFilterIfMissing(true);
genderFilter
.setFilterIfMissing(true);
FilterList filterList
= new FilterList(FilterList
.Operator
.MUST_PASS_ALL
);
filterList
.addFilter(ageFilter
);
filterList
.addFilter(genderFilter
);
HbaseUtil
.printScan(filterList
,table
);
}
@Test
public void testComparator() throws IOException
{
BinaryPrefixComparator comparator
= new BinaryPrefixComparator("zhen".getBytes());
SingleColumnValueFilter nameFilter
= new SingleColumnValueFilter(
"base_info".getBytes(),"name".getBytes(), CompareFilter
.CompareOp
.EQUAL
,comparator
);
nameFilter
.setFilterIfMissing(true);
HbaseUtil
.printScan(nameFilter
,table
);
}
@Test
public void testFamilyFilter() throws IOException
{
RegexStringComparator comparator
= new RegexStringComparator("^f1");
FamilyFilter filter
= new FamilyFilter(CompareFilter
.CompareOp
.EQUAL
,comparator
);
HbaseUtil
.printScan(filter
,table
);
}
@Test
public void testQualifierFilter() throws IOException
{
RegexStringComparator comparator
= new RegexStringComparator("ender");
QualifierFilter filter
= new QualifierFilter(CompareFilter
.CompareOp
.EQUAL
,comparator
);
HbaseUtil
.printScan(filter
,table
);
}
@Test
public void testColumnPrefixFilter() throws IOException
{
ColumnPrefixFilter filter
= new ColumnPrefixFilter("gen".getBytes());
HbaseUtil
.printScan(filter
,table
);
}
@Test
public void testMultipleColumnPrefixFilter() throws IOException
{
byte[][] columns
= new byte[][]{"gen".getBytes(),"name".getBytes()};
MultipleColumnPrefixFilter filter
= new MultipleColumnPrefixFilter(columns
);
HbaseUtil
.printScan(filter
,table
);
}
@Test
public void testColumnRangeFilter() throws IOException
{
ColumnRangeFilter filter
= new ColumnRangeFilter("age".getBytes(),true,"name".getBytes(),false);
HbaseUtil
.printScan(filter
,table
);
}
@Test
public void testRowFilter() throws IOException
{
BinaryPrefixComparator comparator
= new BinaryPrefixComparator("rk0001".getBytes());
RowFilter filter
= new RowFilter(CompareFilter
.CompareOp
.EQUAL
,comparator
);
HbaseUtil
.printScan(filter
,table
);
}
@Test
public void testFirstKeyOnlyFilter() throws IOException
{
FirstKeyOnlyFilter filter
= new FirstKeyOnlyFilter();
HbaseUtil
.printScan(filter
,table
);
}
@Test
public void testPageFilter() throws IOException
{
PageFilter filter
= new PageFilter(10);
Scan scan
= new Scan();
scan
.setFilter(filter
);
String maxRow
= "";
int currentPage
= 0;
while(true){
int count
= 0;
ResultScanner scanner
= table
.getScanner(scan
);
Iterator
<Result> iterator
= scanner
.iterator();
System
.out
.println("--------------currentPage"+ (++currentPage
) +"-----------------");
while(iterator
.hasNext()){
Result result
= iterator
.next();
HbaseUtil
.printResult(result
);
maxRow
=new String(result
.getRow());
count
++;
}
System
.out
.println("");
if(count
<10){
break;
}
scan
.setStartRow((maxRow
+"\000").getBytes());
}
}
}
3.hbase的比较器
--1. RegexStringComparator: 正则字符串比较器
--2. SubstringComparator: 子串比较器
--3. BinaryComparator : 二进制比较器
--4. BinaryPrefixComparator: 二进制前缀比较器
二、工作机制
2.1 寻址过程
客户端会发送访问请求,可能是一个查询操作,也可能是插入操作
查询操作:get 'myns2:student','rk01000';
插入操作:put 't1','25','base_info:name','wangzhaojun'
从上述的操作中,可以看出需要找到具体表的具体的某一个region,如果是插入,就向对应的store的memstore里插入,如果是查询,就从需要查询的store里查询数据(memstore-写缓存,storefile,blockCache-读缓存)
访问过程分3步:
第1步:Client请求ZooKeeper获取hbase:meta表所在的RegionServer的地址。
第2步:Client 请求hbase:meta表所在的RegionServer获取访问的具体的表的region的RegionServer地址,Client会将hbase:meta表的相关信息cache下来,以便下一次快速访问。
第3步:Client请求数据所在的 RegionServer,获取所需要的数据。
2.2 存储机制
2.2.1 存储模型region
2.2.2 flush、compact、split
参考文档
day04
一、复习
一、API(重点)
1. table的crud
--. get对象: 只能指定单行查询,但是可以使用List<Get>查询多行
查询效果: select * 的形式
--. scan对象:
默认情况下是全表查询: select * 的形式
也可以指定行范围进行查询: select * 的形式
也可以指定过滤器进行查询: 可能是select *的形式
也可能是select colName1,colName2.....where 的形式
2. 过滤器
--. 单列值过滤器: select *
--. 结构过滤器: 可以绑定多个其他过滤器进行查询
--. keyvalue元数据过滤器: 返回指定要查询的单元格
--. 行键过滤器: 查询指定行的所有单元格
--. 实用过滤器(FirstKeyOnlyFilter): 查询的每行的第一个单元格
--. 分页过滤器(PageFilter): 可以进行一页固定多少条记录进行查询
3. 比较器
--. RegexStringComparator: 可以指定一个正则表达式的字符串进行匹配想要的数据
--. SubStringComparator: 可以指定一个字符串,查询包含此字符串的数据
--. BinaryComparator: 可以指定一个字符串的字节数组,查询必须是此字符串的数据
--. BinaryPrefixComparator: 可以指定一个字符串的字节数组,查询必须是以此字符串开头的数据
二、工作机制
1. 寻址流程
--. 客户端连接zookeeper,获取hbase:meta表的region的位置(regionserver的ip地址)
--. 紧接着,客户端请求该位置,查询hbase:meta表的数据,获取要操作的表的region及其地址。
--. 然后,客户端就会访问该region的regionserver,然后处理(存取)数据。
2. 存储机制(region)
--. region是hbase中的表或者是表的一部分,也就是基本的存储结构(存储模型,是内存中的java对象)
--. region管理的数据除了在内存中有一部分外,剩下的全都是以文件(storefile)的形式存储在hdfs上。
--. region所要管理的数据可能由于单元格过多,或者是意义不同,分为列族进行管理,一个列族对应一个store(也是内存中的对象)
--. 一个store就管理着相同意义的单元格,这些单元格在对应的memstore中存储,但是由于阈值,最终会flush成storefile。
--. storefile的个数越来越多,store(对象)管理着这些文件的索引信息。
--. storefile的个数太多的话,也不好管理,因此会合并机制,合并成一个文件
--. 如果一个storefile过大,region和regionserver的负载不均衡,因此会有切分机制,细节上是切分文件(从某一个rowkey开始切分,文件大小尽可能均分,也可能造成其他的文件的切分),宏观上是region的切分,因为region有自己的rowkey范围。
3. flush、compact、split、merge_region
-- flush: 指的是memstore存储单元格时,达到自己的阈值(比如是128m,或者是1个小时,或者是整个regionserver的内存的40%),会进行flush,刷成storefile
-- compact: 指的是storefile的数量达到阈值(3)时,会进行合并成一个storefile
-- split: 宏观上指的就是region的切分
二、工作机制
2.1 region
为了更方便的管理表的region以及负载均衡,避免热点问题,所以region一般情况下会预切分。
就是在建表期间提前划分好每一个region的rowkey的范围
create 'ns1:student','base_info',SPLITS=>['RK250000','RK500000','RK750000']
上述就是一种预切分的手段,需要预先估计要存储的真实数据的情况。
2.2 写流程(重点)
(1) Client通过Zookeeper的调度,向RegionServer发出写数据请求,在Region中写数据。
(2) 数据被写入Region的MemStore,直到MemStore达到预设阈值。
(3) MemStore中的数据被Flush成一个StoreFile。
(4) 随着StoreFile文件的不断增多,当其数量增长到一定阈值后,触发Compact合并操作,将多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除。
(5) StoreFiles通过不断的Compact合并操作,逐步形成越来越大的StoreFile。
(6) 单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个新的Region。父Region会下线,新Split出的2个子Region会被HMaster分配到相应的RegionServer上,使得原先1个Region的压力得以分流到2个Region上。
2.3 读流程(重点)
(1) Client 通过寻址流程,找到具体要查询的region。
(2) 由于Regionserver的内存分为MemStore(写存储)和BlockCache(读缓存)两部分。所以,读操作会先读取BlockCache里的数据,然后再读取MemStore里的数据,最后再通过索引找到可能有数据的storefile进行读取,从storefile中读到的数据会暂时缓冲到BlockCache中,为了下次的快速读取操作。
三、Hbase与hive的整合(了解)
3.1 说明
hbase是hadoop数据库,用于存储数据,虽然能提供近似实时的读写操作,但是依然不能从事OLTP的工作,OLAP也不合适。
HIVE底层是MR(MR是一个离线的分析计算框架),也决定了HIVE适合OLAP的工作。
因此Hbase与HIVE整合到一起,就可以满足存储和分析的工作了。
整合的目的:hbase中的表数据在hive中能看到,hive中的表数据在hbase中也能看到
3.2 Hive-to-Hbase
在hive中创建的表,一定要让hbase可以看到
create table if not exists student
(
uid
int,
uname string
,
age
int,
sex string
,
province string
,
city string
)
stored
by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
with serdeproperties
(
"hbase.columns.mapping"=":key,base_info:name,base_info:age,base_info:gender,address_info:province,address_info:city"
)
tblproperties
(
"hbase.table.name"="student1"
);
向hive的student表中插入数据
insert into student values (1001,'zs',23,'f','广东','广州');
insert into student (uid,uname,city) values (1002,'lisi','杭州');
向hbase的student1表中插入数据
put 'student1','1003','base_info:name','wangwu'
put 'student1','1003','address_info:city','changchun'
然后在hive中查询,在hbase中查询
3.3 Hbase-to-Hive
先在hbase中维护一张表
hbase
> create_namespace
'ns1'
hbase
> create 'ns1:t1','f1','f2'
put
'ns1:t1','rk00001','f1:name','zhaoyun'
put
'ns1:t1','rk00001','f1:age',23
put
'ns1:t1','rk00001','f1:gender','m'
put
'ns1:t1','rk00001','f2:math','100'
put
'ns1:t1','rk00001','f2:english','10'
put
'ns1:t1','rk00001','f2:chinese','90'
put
'ns1:t1','rk00002','f1:name','zhenji'
put
'ns1:t1','rk00002','f1:age',24
put
'ns1:t1','rk00002','f1:gender','f'
put
'ns1:t1','rk00003','f1:name','貂蝉'
put
'ns1:t1','rk00003','f1:age',24
put
'ns1:t1','rk00003','f1:gender','f'
2)在hive中创建一张表与之进行映射
create external
table if not exists score_info
(
uid string
,
uname string
,
age
int,
sex string
,
math string
,
chinese string
,
english string
)
stored
by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
with serdeproperties
(
"hbase.columns.mapping"="f1:name,f1:age,f1:gender,f2:math,f2:chinese,f2:english"
)
tblproperties
(
"hbase.table.name"="ns1:t1"
);
在hive中查询一下数据
3.4 注意事项
1. hive和hbase在字段的映射关系上,是按照建表的字段顺序进行映射的,不是根据名称来的。
2. :key 是要映射成hbase的rowkey的。可以写出来,也可以不写,不写的话是默认提供的。
3. hbase先有表数据时,然后映射到hive中,那么hive的表必须是外部表(external)
四、布隆过滤器的原理(熟悉)
4.1 布隆过滤器的由来
1970年由Howard Bloom提出的一个二进制向量存储结构。用于判断一个元素是否在集合中,如果是“否”,那么一定不在集合中,如果是“是”,那么可能在集合中,牺牲了正确率来节省存储空间
4.2 布隆过滤器的应用场景
在爬虫爬取网页数据时,是根据网页链接来计算这个链接是否爬取过,使用布隆过滤器的存储结构,来判断这个网页链接是否没爬过,如果存储结构中经过计算,发现没有这个链接,那么这个链接一定没有爬过,那就爬。
如果经过计算,这个链接可能在存储结构中,那就意味着这个链接可能爬过,也可能没爬过,那么就不爬(大不了不要此网页的数据了)。
4.3 原理
布隆过滤器内部维护了一个64k大小的位数组,以及n个hashcode函数(每个hashcode函数的逻辑不同)
在初始化时,位数组里全都是0,假设有三个hashcode函数
hashCode1() int值-----是位数组的一个下标-----下标对应的元素存为1.
hashCode2() int值-----是位数组的另一个下标-----下标对应的元素存为1.
hashCode3() int值-----是位数组的另一个下标-----下标对应的元素存为1.
判断一个元素是否在一个集合中,
针对于这个元素分别调用三个函数,来计算出三个下标,如果三个下标上的位数组里的元素只要有一个是0,那么这个元素一定不存在集合中,如果三个下标的位数组的上的元素都是1,那么要判断的元素不一定在集合中。
4.4 在Hbase中的应用
当我们随机读get数据时,如果采用hbase的块索引机制,hbase会加载很多块文件。如果采用布隆过滤器后,它能够准确判断该HFile的所有数据块中,是否含有我们查询的数据,从而大大减少不必要的块加载,从而增加hbase集群的吞吐率。这里有几点细节:
1. 布隆过滤器的存储在哪?
对于hbase而言,当我们选择采用布隆过滤器之后,HBase会在生成StoreFile(HFile)时包含一份布隆过滤器结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销。但是在大多数情况下,这些负担相对于布隆过滤器带来的好处是可以接受的。
2. 采用布隆过滤器后,hbase如何get数据?
在读取数据时,hbase会首先在布隆过滤器中查询,根据布隆过滤器的结果,再在MemStore中查询,最后再在对应的HFile中查询。
3. 采用ROW还是ROWCOL布隆过滤器?
这取决于用户的使用模式。
如果用户只做行扫描,使用更加细粒度的行加列布隆过滤器不会有任何的帮助,这种场景就应该使用行级布隆过滤器。
当用户不能批量更新特定的一行,并且最后的使用存储文件都含有该行的一部分时,行加列级的布隆过滤器更加有用。
例如:ROW 使用场景假设有2个Hfile文件hf1和hf2, hf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v) hf2包含kv3(r3 cf:q1 v)、kv4(r4 cf:q1 v) 如果设置了CF属性中的bloomfilter(布隆过滤器)为ROW,那么get(r1)时就会过滤hf2,get(r3)就会过滤hf1 。
ROWCOL使用场景假设有2个Hfile文件hf1和hf2, hf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v) hf2包含kv3(r1 cf:q2 v)、kv4(r2 cf:q2 v) 如果设置了CF属性中的bloomfilter为ROW,无论get(r1,q1)还是get(r1,q2),都会读取hf1+hf2;而如果设置了CF属性中的bloomfilter为ROWCOL,那么get(r1,q1)就会过滤hf2,get(r1,q2)就会过滤hf1。
tip:
ROW和ROWCOL只是名字上有联系,但是ROWCOL并不是ROW的扩展,也不能取代ROW
五、Rowkey的设计原则及其案例(重点)
5.1 热点问题
当大量的客户端要访问的数据集中在一个regionserver或者region里,那么这个regionserver或者这个region一定会不堪重负,造成性能上的影响,也可能影响到其他的region的访问。这种情况就是热点问题。因此在存储数据时,应该尽量散列存储行数据。
5.2 rowkey的重要性
因为hbase的存储机制,以及查询方式(通常是按照rowkey来查询,这样的性能非常高,而不是全表遍历),所以rowkey的设计显得格外重要。
比如在建表之处,我们就要考虑好,我们未来可能发生的查询操作,比如按照部门号查询,按照时间、地域性查询。那么就可以将这些信息设计到rowkey中,来达到更高的查询性能。
5.3 设计原则
--1. 唯一性原则
rowkey一定要遵守唯一性原则,如果不唯一,某一些单元格可能会被不小心覆盖掉
--2. 统一长度原则
rowkey的长度如果不统一,那么在查询时,返回的结果可能与想要的结果不一致。
如果rowkey的长度不统一,假如现在有5个Rowkey,分别是:"012", "0", "123", "234", "3"。因此底层是字典排序,所以排序的结果是:
"0"
"012"
"123"
"234"
"3"
而我们想要的结果是如下的:
"0"
"3"
"012"
"123"
"234"
由于是字典排序,做不到这样的效果。如果想要这样的效果,只能长度统一
"000"
"003"
"012"
"123"
"234"
建议在10个字节到100个字节以内,最好是16个字节,也就是8的倍数个字节。原则是能短绝对不要长。
--3. 散列原则
目的:为了尽量避开热点问题,应该使数据散列到不同的region中。
方式1: rowkey的reverse反转
130xxxx6666
130xxxx6789
131xxxx1234
131xxxx2234
159xxxx3234
188xxxx1111
手机号反转:
1234xxxx031
9876xxxx031
4321xxxx131
4322xxxx131
4323xxxx951
1111xxxx881
方式2:salting(加盐)
abc001
abc002
abc003
abc004
.....
abc019
.....
在设计存储时,随机a-g中的任意一个字符,作为前缀拼接到rowkey上
d-abc001 [,b][b,d][d,f][f,]
a-abc002
g-abc003
f-abc004
d-abc005
........
方式3:hashcode散列/取模(也是加前缀)
abc001
abc002
abc003
abc004
.....
abc019
--- 可以对这些rowkey调用hashcode函数或者是其他算法,比如md5等得到一个固定的字符串。
如abc001-->md5算法--->9bf049097142c168c38a94c626eddf3d-->取前四位作为rowkey的前缀
9bf0-abc001
7006-abc002
95e6-abc003
1ba5-abc010
如果是纯数字的rowkey
100001
100002
100003
100004
如果不想连续,而是散列存储,可以使用原始的rowkey对某一个数字比如4进行取模,结果作为前缀
1-100001
1-100005
2-100002
3-100003
0-100004
5.4 案例:多条件的rowkey设计
案例需求:
List<XXXX> find(starttime,endtime,filename,filetype,userid)
-1. 文件创建时间区间 (比如从20120901到20120914期间创建的文件)
-2. 文件名(“中国好声音”),
-3. 分类(“综艺”),
-4. 所有者(“浙江卫视”)。
设计rowkey时,可以是userid+createtime+fileid
rowKey(userID 6 + time 8 + fileID 6) name category ….
00000120120902000001
00000120120904000002
00000120120906000003
00000120120908000004
00000120120910000005
00000120120914000007
00000220120912000006
00000220120916000008
00000320120918000009
00000420120920000010
真正应用时:传入一下参数
find(20120901,20120914,“中国好声音”,“综艺”,1){
将形参拼接成想要的rowkey,传入到scan里
scan.setRowKeyRange(starttime,endtime)
}
六、二级索引表的概念(熟悉)
rk0001 f1:name=zhangsan f1:age=23 的f2:province的值
rk0002 f1:name=lisi f1:age=23
rk0003 f1:name=wangwu f1:age=23
rk0004 f1:age=23
rk0005 f1:name=zhaoliu f1:gender=xxx
...........................
rk0010 f1:name=zhangsan f1:gender=xxx f2:province
rk0011 f1:name=zhangsan f1:gender=xxx
创建一张表: 存储 f1:name===xxxxx所有的行号
rowkey
f1:name=zhangsan f1:rk1 rk0001
f1:rk2 rk0010
f1:rk3 rk0011
f1:name=lisi f1:rk rk0002
二级索引表的概念:
二级索引表存储的就是一些单元格与rowkey的映射关系。在查询原表时,为了不遍历整张表,可以通过二级索引表查询出要查询的行号,然后再去原表中通过行号查询对应的数据,这样就避免了全表扫描,提高了查询效率
七、协处理器(重要)
在hbase的低版本中,想要维护一张二级索引表是非常困难的。后来在高版本中,引入了协处理器的API。可以很轻松的完成二级索引表的建立。
还有就是求和,计数,排序等操作,低版本无法轻易完成,而引入的协处理器可以在server端进行并发运算,从而提高性能。
7.1 协处理器的分类
一、Observer: 相当于关系型数据库支持的触发器(重点)
-- RegionObserver:针对Region的观察者,可以监听关于Region的操作
-- RegionServerObserver:针对RegionServer的观察者,可以监听关于RegionServer的操作
-- WALObserver:针对WAL的观察者,可以监听关于WAL的操作
-- MasterObserver:针对Master的观察者,可以监听关于Master的操作
二、EndPoint: 相当于关系型数据库支持的存储过程,提供的就是一个接口API。
7.2 两种类型的区别
- Observer 允许集群在正常的客户端操作过程中可以有不同的行为表现
- Observer 类似于 RDBMS 中的触发器,主要在服务端工作
- Observer 可以实现权限管理、优先级设置、监控、ddl 控制、二级索引等功能(重点)
- Endpoint 允许扩展集群的能力,对客户端应用开放新的运算命令
- Endpoint 类似于 RDBMS 中的存储过程,主要在服务端工作
- Endpoint 可以实现 min、max、avg、sum、distinct、group by 等功能(重点)
7.3 协处理器的应用
需求:帮助"关注表"创建一个二级索引表"粉丝表"
表名:guanzhu
rowkey cell cell
liushuai-canglaoshi f1:from liushuai f1:to canglaoshi
liushuai-bolaoshi f1:from liushuai f1:to bolaoshi
...............
jiashuai-longzelaoshi f1:from jiashuai f1:to longzelaoshi
......
二级索引表:fensi
rowkey
canglaoshi-liushuai f1:from liushuai f1:to canglaoshi
canglaoshi-jiashuai f1:from jiashuai f1:to canglaoshi
..........................
需要提前创建好两张表
hbase(main):027:0> create 'guanzhu','f1'
hbase(main):028:0> create 'fensi','f1'
步骤1:
1. 自定义类型,继承BaseRegionObserver 重写prePut方法,拦截put对象
2. 打包程序,上传到HDFS上
3. 将此协处理器挂载到关注表上
alter 'guanzhu',METHOD =>'table_att','coprocessor'=>'hdfs://qianfeng01:8020/jar/mycoprocessor.jar|com.qf.hbase.coprocessor.FensiObserver|1001|'
4. 进行测试
注意:在加载协处理器的时候,如果报 set hbase.table.sanity.checks to false等提示,需要在hbase-site.xml里配置以下属性,并分发到其他机器上,然后重启hbase服务
<property>
<name>hbase.table.sanity.checks</name>
<value>false</value>
</property>
加载方式的介绍
1) 静态加载
通过修改 hbase-site.xml 这个文件来实现,启动全局 aggregation,能过操纵所有的表上 的数据。只需要添加如下代码:
<property>
<name>hbase.coprocessor.user.region.classes</name>
<value>类全名</value>
</property>
可以用”,”分割加载多个 class
2) 动态加载
只对特定的表生效。通过 HBase Shell 来实现。
1. 停用表 disable 'mytable'
2. 添加协处理器 alter 't_guanzhu',METHOD => 'table_att','coprocessor'=>'hdfs://supercluster/jar/mycoprocessor.jar|com.qf.hbase.coprocessor.MyIndexCoprocessor|1001|'
3. 启用表 enable 'mytable'
longzelaoshi …
二级索引表:fensi rowkey canglaoshi-liushuai f1:from liushuai f1:to canglaoshi canglaoshi-jiashuai f1:from jiashuai f1:to canglaoshi …
需要提前创建好两张表 hbase(main):027:0> create ‘guanzhu’,‘f1’ hbase(main):028:0> create ‘fensi’,‘f1’
步骤1:
自定义类型,继承BaseRegionObserver 重写prePut方法,拦截put对象打包程序,上传到HDFS上将此协处理器挂载到关注表上 alter ‘guanzhu’,METHOD =>‘table_att’,‘coprocessor’=>‘hdfs://qianfeng01:8020/jar/mycoprocessor.jar|com.qf.hbase.coprocessor.FensiObserver|1001|’进行测试
注意:在加载协处理器的时候,如果报 set hbase.table.sanity.checks to false等提示,需要在hbase-site.xml里配置以下属性,并分发到其他机器上,然后重启hbase服务 hbase.table.sanity.checks false
加载方式的介绍
静态加载 通过修改 hbase-site.xml 这个文件来实现,启动全局 aggregation,能过操纵所有的表上 的数据。只需要添加如下代码: hbase.coprocessor.user.region.classes 类全名
可以用”,”分割加载多个 class
动态加载 只对特定的表生效。通过 HBase Shell 来实现。
停用表 disable ‘mytable’添加协处理器 alter ‘t_guanzhu’,METHOD => ‘table_att’,‘coprocessor’=>‘hdfs://supercluster/jar/mycoprocessor.jar|com.qf.hbase.coprocessor.MyIndexCoprocessor|1001|’启用表 enable ‘mytable’
## 八、优化参数的讲解