1、Hbase简介
1、是什么?
Hbase是分布式的存储海量数据的NoSql数据库
2、场景: 实时场景
3、Hbase数据模型:
Table: 数据存储的形式
列簇: Hbase的表结构,一般在创建表的时候指定
rowkey:数据的主键,数据在hbase中是按照rowkey字典序进行排序
列限定符: 类似mysql的字段
region: table的一个分段
store: store是region的分段,store的个数和列簇的个数一样
Namespace: 命名空间,类似mysql的库
Column: 列簇:列限定符表示Column
Cell: rowkey+列簇:列限定符+时间戳
hbase中数据是保存在Table中,要想创建一个Table必须指定表结构[列簇]
Table要想分布式存储,是划分为region保存在不同的机器上。
region中根据列簇划分为不同的store
hbase的表时保存在Namespace下
4、架构:
Master:
职责:
1、负责表的增删改操作
2、负责region的负载均衡,监听regionserver的状态,一旦regionserver宕机,会将宕机的regionserver上的region分配到其他的regionserver上
RegionServer:
职责:
1、负责数据的增删改查
2、负责region的split、storeFile的compact
region: table的一个分段
Store: 一个列簇一个store
memstore: 是一个内存区域。memstore当达到一定阈值或者一定的时间之后就会flush,每次flush都会生成storefile文件
storeFile: 数据存储单元,由memstore flush生成。最终是以HFile这种文件格式保存在HDFS
HLog: 预写日志,数据在写入memstore之前必须先写入HLOG中,主要是为了防止regionserver宕机导致memstore的数据丢失。一个regionserver有一个HLOG
2、Hbase安装和shell使用
1、namespace方面
1、创建namespace: create_namespace '命名空间名称'
2、查看所有的namespace: list_namespace
3、查看某个namespace的所有表: list_namespace_tables '命名空间名称'
4、删除某个namespace: drop_namespace '命名空间名称'
删除namespace之前必须删除namespace下所有的表
5、查看namespace的详情: describe_namespace '命名空间名称'
2、table方面
1、创建表: create '表名','列簇名1','列簇名2',..
2、修改表: alter '表名',{COLUMN=>'列簇名',VERSIONS=>'版本数'}
3、删除表:
1、禁用表: disable '表名'
2、删除: drop '表名'
3、数据方面
1、插入数据: put '表名','rowkey','列簇:列限定符','值'
2、查询:
1、根据rowkey查询数据:
1、查询整行数据: get '表名','rowkey'
2、查询某个列簇的数据: get '表名','rowkey','列簇名'
3、查询某个列的数据: get '表名','rowkey','列簇名:列限定符'
4、查询多个版本的数据: get '表名','rowkey',{COLUMN=>'列簇名:列限定符',VERSIONS=>'版本数'}
2、扫描:
1、查询整个表所有数据: scan '表名'
2、查询某个列簇的数据: scan '表名',{COLUMN=>'列簇名'}
3、查询某个列的数据: scan '表名',{COLUMN=>'列簇名:列限定符'}
4、查询多个版本数据: scan '表名',{COLUMN=>'列簇名:列限定符',VERSIONS=>'版本数'}
5、查询某一段rowkey范围的数据:scan '表名',{STARROW=>'起始rowkey[包含]',STOPROW=>'结束rowkey[不包含]'}
3、修改: put '表名','rowkey','列簇:列限定符','值'
4、删除数据:
1、只删除Cell:
1、delete '表名','rowkey','列簇:列限定符'
2、deleteall '表名','rowkey','列簇:列限定符'
2、删除整行数据: deleteall '表名','rowkey'
5、清空整表数据: truncate '表名'
6、统计表的行数: count '表名'
3、原理
1、架构: 看上面的架构
2、写流程:
1、client向zookeeper请求获取元数据[hbase:meta]表所在的regionserver
2、zookeeper向client返回元数据所在的regionserver
3、client向元数据所在regionserver发起获取元数据的请求
4、元数据所在的regionserver向client返回元数据。client会缓存元数据,后续在查询的时候就可以不用在获取元数据直接从缓存获取
5、client通过元数据得知数据应该写往哪个region,该region处于哪个regionserver,向regionserver发起数据写入请求
6、client先将数据写入HLOG,便于数据的恢复
7、再将数据写入memstore中
8、向client返回消息告知写入完成
3、memstore flush[ flush的单位是region ]
触发条件:
1、达到一定的阈值
1、region中某个memstore的大小达到128M,当前该region的所有memstore都会flush
2、当处于读写高峰期的时候,第一个flush可以延迟,延迟到region中所有memstore的总大小达到128*4的大小会阻塞client的写入,优先flush
3、regionserver中所有的region所有的memstore的总大小达到 java_heap * hbase.regionserver.global.memstore.size * hbase.regionserver.global.memstore.size.lower.limit 大小会进行flush,在flush的时候会按照region中memstore占用的空间大小进行排序,优先flush占用空间大的region
4、当处于读写高峰期的时候,第三个flush可以适当延迟,延迟到regionserver中所有的region的所有的memstore的总大小达到java_heap * hbase.regionserver.global.memstore.size 大小会阻塞client的写入,按照region中memstore占用的空间大小进行排序,优先flush占用空间大的region,一直flush到regionserver所有的memstore占用的总空间的大小小于java_heap * hbase.regionserver.global.memstore.size * hbase.regionserver.global.memstore.size.lower.limit停止flush
5、hlog的文件数达到32的时候,会flush
2、达到一定的时间: 一个小时触发一次flush
3、手动flush: flush '表名'
4、读取数据的流程
1、client向zookeeper请求获取元数据[hbase:meta]表所在的regionserver
2、zookeeper向client返回元数据所在的regionserver
3、client向元数据所在regionserver发起获取元数据的请求
4、元数据所在的regionserver向client返回元数据。client会缓存元数据,后续在查询的时候就可以不用在获取元数据直接从缓存获取
5、client通过元数据得知数据应该写往哪个region,该region处于哪个regionserver,向regionserver发起数据读取请求
6、首先从block cache中获取数据
7、然后从memstore中获取数据
8、最后从storeFile中获取数据
通过布隆过滤器[特点: 如果判断存在则不一定存在,如果判断不存在则一定不存在]判断哪些storeFile中可能存在数据
然后通过StoreFile中HFile文件格式里面的数据索引来找到数据
9、将数据返回给client
5、storeFile compact
原因: memstore每次flush都会生成一个storefile文件,文件数越来越多会影响查询性能,所以需要对进行进行合并
minor compact:
结果: 尽可能的将小文件合并成大文件
过程: 单纯的合并小文件的内容,不会清除无效数据[无效version数据,删除数据..]
触发条件: 小文件[小于128M]数达到3个就会触发
major compact:
结果: 将store中所有的storefile文件合并成一个文件
过程: 在合并的过程中会清除掉无效数据[无效version数据,删除数据..]
触发条件: 默认7天触发一次[一般禁用,采用手动的方式 major_compact '表名']
6、region split
原因: 当region越来越大,后续客户端的读写请求,很大可能都是落在该大region上,对regionserver压力相对比较大,所有为了负载均衡,需要对大region进行split。
0.94版本之前split策略:
当region中某个store的大小达到10G的时候,region就会split
0.94-2.0版本split策略:
regioncounts == 0 || regioncounts>100 ? 10G : Min(10G,2 * 128 * regioncounts * regioncounts * regioncounts)
regioncounts: region所属表在当前regionserver上的region个数
2.0版本之后split策略:
regioncounts == 1 ? 2 * 128M :10G
regioncounts: region所属表在当前regionserver上的region个数
4、API
5、Hbase优化:
1、预分区: [创建表的时候预先分配好region]
1、shell:
1、create '表名','列簇名1',..,SPLITS=>['rowkey1','rowkey2',..]
2、create '表名','列簇名1',..,SPLITS_FILE=>'分割rowkey所处文件路径'
2、api:
admin.createTable(tableDescriber,splitKeys)
2、rowkey设计:
遵循原则:
1、长度原则: hbase的rowkey不能太长,太长之后会多占用存储空间,rowkey太长还会影响client缓存的元数据的条数
工作中,rowkey的长度一般保持在16字节以下
2、唯一性: hbase的两条数据不能rowkey一样
3、hash原则: 让hbase的数据尽可能的均匀分配
热点问题解决方案:
1、加盐[加随机数]
2、字符串反转
3、hash、拼接字符串
3、内存优化: 服务器分配给hbase regionserver/master的内存一般设置为16-48G
4、基础优化:
1、允许hdfs文件追加:hbase数据写入是采用追加的方式,所以需要hdfs支持追加[hdfs默认就是允许追加]
2、增大HDFS最大文件打开数:
如果hbase处于读写高峰期的时候,可能同时打开的文件数比较多,所以此时需要进行调整
3、增加超时时间
如果网络不够好,可能造成读写请求超时,遇到这种情况可以适当的调整超时时间
4、优化写入效率[压缩写入]
写入的时候如果采用压缩写入,速度相对比较快
5、调整client的缓存
调整client的缓存,如果调大能够更多的缓存元数据,减少与hbase的交互次数
6、调整RPC的监听数
如果同一个时间有很多client发起读写请求,就会造成hbase中用来处理client请求的线程数不够用,此时需要进行调整
7、调整hstore的文件大小
调整hstore的大小,能够更好的进行region的split,优化的读写的负载均衡
8、flush、split的参数调整
6、phoenix
1、是什么?
hbase的sql皮肤,主要用来建二级索引
2、phoenix基本应用
1、查看phoenix所有表: !tables
2、创建表
1、hbase中存在表[其实就是phoenix与hbase的已经存在的表建立映射关系]
create table 表名(
字段名 字段类型 primary key, //主键,主键对应Hbase表的rowkey
"列簇名"."列限定符名" 字段类型,
...
)COLUMN_ENCODED_BYTES=0
2、hbase中不存表[phoenix建表的时候会在hbase中同步创建表]:
create table 表名(
字段名 字段类型 primary key, //主键,主键对应Hbase表的rowkey
字段名 字段类型,
...
)COLUMN_ENCODED_BYTES=0 //COLUMN_ENCODED_BYTES=0 表示列名在hbase中不用编码存储
phoenix在建表的时候,会默认将表名、字段名转成大写,如果想要保持小写,需要用""将表名或者字段名括起来
3、查询: select * from ..
4、插入数据: upsert into 表名 values(值,...)
5、修改数据: upsert into 表名 values(值,...)
6、删除数据: delete from 表名 where ...
7、phoenix如果想要映射命名空间的表
1、需要在hbase conf目录配置hbase-site.xml,还需要在客户端配置hbase-site.xml
2、创建schema[创建的schema必须要与命名空间的名称一致]: create schema 命名空间名称
3、使用schema: use 命名空间的名称
4、映射表:
create table 表名(
字段名 字段类型 primary key, //主键,主键对应Hbase表的rowkey
"列簇名"."列限定符名" 字段类型,
...
)COLUMN_ENCODED_BYTES=0
3、二级索引
原因:
hbase中通过rowkey查询数据是可以通过meta元数据表得知rowkey对应的数据在哪个region、哪个regionserver,
但是如果根据value值查询数据的时候,这时候不能够根据meta表得知数据处于哪个region,此时查询是全表扫描,速度比较慢,所以一般都是将需要查询的字段建索引,提高查询性能
全局二级索引:
1、原理: 新建一个hbase的表,将建立索引字段的值与原来的rowkey作为新表的rowkey,后续查询的时候直接查询新表,如果新表查不到再全表扫描原表
2、建立索引的语法: create index 索引名 on 表名(字段名) [include(字段名2,..)]
1、表名大小,字段名也大写的情况:create index p_index on person(name)
2、映射表:create index p_index on "person"("列簇名"."列限定符")
3、删除索引: drop index 索引名 on 表名
本地二级索引:
1、原理:
在原表中插入索引数据,索引的数据的rowkey=__索引字段value值_原来的rowkey,后续根据索引字段的值查询的时候,先查索引数据得到原来的rowkey,在根据原来的rowkey查询原来的详情数据[工作中一般采用此种方式]
2、语法:create local index 索引名 on 表名(字段名)
1、表名大小,字段名也大写的情况:create local index p_index on person(name)
2、映射表:create local index p_index on "person"("列簇名"."列限定符")
3、删除索引:drop index 索引名 on 表名
4、协处理器
协处理器其实类似触发器,就是hbase在执行某些操作[put、get、scan、flush..]的时候触发我们指定的操作逻辑。
phoenix的二级索引其实就是通过协处理器完成的
自定义协处理器:
1、实现两个接口[RegionObserver、RegionCoprocessor ]:
2、重新一个方法
@Override
public Optional<RegionObserver> getRegionObserver() {
return Optional.of(this);
}
3、定义指定动作触发的方法
prePut: pre开头的方法是在指定动作执行之前执行
postPut: post开头的方法是在指定动作执行之后执行
4、打包上传HDFS
5、禁用表: disable '表名'
6、修改表,添加协处理器
alter '表名', METHOD => 'table_att', 'Coprocessor'=>'hdfs打包上传jar路径|自定义协处理器的全类名|优先级|参数'
7、启动表:enable '表名'
5、hive与hbase的整合
通过hive整合hbase,后续就可以在hive上写sql语句操作hbase的数据,进行统计分析
内部表:
创建表: hive建内部表的时候,hbase的表不能够存在,如果存在则报错
删除表: 内部删除的时候会同步删除Hbase的表
语法:
CREATE TABLE hive表名(hive字段名1 类型, hive字段名2 类型,..)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' //指定hive的数据存储在hbase中
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,列簇名:列限定符名,列簇名:列限定符名,..") //hbase.columns.mapping将hive的字段与hbase表的列簇、列限定符建立映射,:key代表rowkey
TBLPROPERTIES("hbase.table.name" = "hbase表名");
外部表:
创建表: 其实将hive表与hbase已经存在的表建立一个映射关系,如果hbase的表不存在,则报错
删除表: 内部删除的时候不会同步删除Hbase的表
语法:
CREATE EXTERNAL TABLE hive表名(hive字段名1 类型, hive字段名2 类型,..)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' //指定hive的数据存储在hbase中
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,列簇名:列限定符名,列簇名:列限定符名,..") //hbase.columns.mapping将hive的字段与hbase表的列簇、列限定符建立映射,:key代表rowkey
TBLPROPERTIES("hbase.table.name" = "hbase表名");