技术背景
起源于谷歌旧三篇论文中bigtable。
设计目的
为了解决大数据环境中海量结构化数据的实时读写问题。为了弥补hadoop生态中没有实时存储的缺陷。
设计思想
分布式
技术本质
- 概念:分布式列存储nosql数据库
- 解释:
列存储:底层数据文件存储格式是列式存储
nosql:非关系型,可以存储结构化和半结构化数据
数据库:符合数据库的特征
核心特性
大表: 为存储海量数据而生,亿级别的行和百万级别的列
分布式:
1.分布式内存:为实现实时读写,将数据放在内存中
2.分布式外存:内存中放置不了的数据,刷写到hdfs上
多版本:一个列簇允许设置版本数,即允许一个数值存储多个版本,通过"ns:tb cf:c ts"唯一缺点一条数据
nosql:
1.RDBMS:关系型数据库,例如"mysql,oracle,postgresql,sqlserver"
2.nosql:非关系型数据库,例如"hbase,redis,mongodb"
随机实时读写
1.数据优先存储在内存中,命中率高数据
2.命中率中等的数据,存在缓存中
3.命中率低的数据,存在hdfs上
代码语言:javascript复制依赖于hdfs:将海量的数据存到hdfs上,即保证了数据了安全性,又保证了数据的持久化
不支持join操作,只支持行级事务,复杂join操作需要集成hive
只支持字节数组类型
稀疏:对于空null的列,并不占用存储空间,因此表的设计可以非常稀疏
集群角色
物理架构
client
种类:
- bin/hbase shell:不支持sql语句,可以使用help 'command',exit,
- java api:
hue:hbase实现了ThriftServer服务端,用于提供hue客户端连接,thriftserver来实现整个过程的访问以及执行
sql on hbase:底层使用的mapreduce操作hbase
zookeeper
概念:hbase中的zk被称为真·主节点
职责:
- 辅助集群的主节点选取,实现HA机制
- 为集群的访问提供入口,存贮所有Region 的寻址入口
- 负责存储hbase中几乎所有的元数据,如节点信息,master的主备节点,regionserver的节点,表的状态
- 负责实现系统容错,若发现regionserver注册节点消失,即认为此regionserver故障,需要通知hmaster启动恢复程序
- 负责存储meta表的region[rootregion]所在的regionserver地址
- 负责flush,splitWAL的状态等
- 存储Hbase 的schema,包括有哪些table,每个table 有哪些column family
内容:
每一个节点都会在zookeeper上去注册
zookeeper存储了meta表的region信息,客户端通过访问zookeeper才能请求集群
hdfs:
职责:存储hfile,wal
Hmaster:
概念:辅助主节点
职责:
- 负责管理所有从节点信息,
- 负责region在regionserver上的分配,实现regionserver的负载均衡,
- 负责region的数据迁移,例如分裂场景
- 负责meta表以及元数据的修改,meta表不等于元数据,可将meta表看做元数据的一部分,meta表只是存储region的信息,而元数据包括regionserver,region,表,namespace等信息
- 负责接收客户端的DDL请求,用于更改元数据,修改zookeeper中的存储
- 通过zookeeper监听每个regionserver的状态,如果regionserver故障,就会启动恢复regionserver的进程
- 发现失效的region server 并重新分配其上的region
- HDFS 上的垃圾文件回收
- 处理schema 更新请求
RegionServer:
职责:
- 负责接收并处理所有客户端的数据的读写请求
- 负责管理该节点上所有的region,处理对这些region 的IO 请求
- 负责记录WAL
- 负责读缓存:memcache,占堆内存40%,并刷新缓存到HDFS
- 执行压缩
- 负责处理Region 分片
补充:
- regionserver就是从节点,有多台
- 一台regionserver中管理很多个不同的表分区【region】
- meta表中的数据也存在regionserver中
Hlog:
概念:预写日志,类似于hdfs上的edits文件,也类似于mysql中的binlog文件。
补充:
每个Region Server 维护一个Hlog,而不是每个Region 一个。这样不同region(来自不同table)的日志会混在一起,这样做的目的是不断追加单个文件相对于同时写多个文件而言,可以减少磁盘寻址次数,因此可以提高对table 的写性能。带来的麻烦是,如果一台region server 下线,为了恢复其上的region,需要将regionserver 上的log 进行拆分,然后分发到其它region server 上进行恢复,HLog文件就是一个普通的Hadoop Sequence File。
Region:
概念:
regionserver中的数据存储单元,不是region级别的
补充:
- 一张表将数据按照rowkey的范围来划分到多个region中,以此来散列表的数据,
- 默认情况下创建的表只有一个region
- 同一张表的不同region的范围总和是从负无穷到正无穷
- 一个region只会被一台regionserver管理
- region是整个hbase中最小的负载均衡单元,是实现分布式的基本
- region的分配不均衡会导致数据倾斜及热点问题
- 用户读写时,会根据rowkey进行判断,从哪个分区读取,从哪个分区写入
- 每一个分区是一个目录
- store个数/2 = region个数
Store
意义:设计列簇时将具有相同IO属性的列放在同一个列簇里,因为底层存储是一个列簇存储独占一个store;放在一个storefile中,可以提供快速的读写操作
概念:
region是表的划分,store是列簇的划分,机器内存 * 40% /128M=store个数
MemStore
功能:
补充:
属于Store中的内存存储区域。每个store中都有一个memstore,用于存储刚写入的数据。写内存,占堆内存的40%,
StoreFile /HFile
功能:
解释:
1.memstore满足条件会进行flush,刷写出来的数据文件就是storefile,存储在hdfs上被称为hfile 2.一个store中会有多个storefile文件,但最终会优化合并成一个文件【二进制文件】 3.storefile是hfile的封装,也可认为是同一个文件,不同的称呼
补充:
HFile分为六个部分:
1.Data Block 段–保存表中的数据,这部分可以被压缩
2.Meta Block 段(可选的)–保存用户自定义的kv 对,可以被压缩。
3.File Info 段–Hfile 的元信息,不被压缩,用户也可以在这一部分添加自己的元信息。
4.Data Block Index 段–Data Block 的索引。每条索引的key 是被索引的block的第一条记录的key。
5.Meta Block Index段(可选的)–Meta Block 的索引。
6.Trailer–这一段是定长的。保存了每一段的偏移量
逻辑存储
NameSpace
名称空间,物理存在。默认创建时就已经存在两个名称空间default和hbase。属于表的一部分,用于区分表的存储,每个表必定有自己是名称空间,若不指定,默认属于default
Table
表,访问时通过namespace:table_name来访问这张表,不加默认访问是default下的表
Rowkey
行键,通过rowkey可以唯一指定一行的数据(注意不是一条),在创建表时就已经存在,不需要再次创建。
rowkey的设计,影响了底层region的节点数据分配,会造成热点问题,影响读写性能。
rowkey是整个hbase中的唯一索引,在hbase中不支持自定义索引,处理rowkey之外,没有别的索引,查找数据时,要么通过rowkey进行查找,要么全表扫描,区别于mysql中可以自定义索引。
ColumnFamily
列簇,列的分组,一个列簇下有多个列,每个列必然属于某个列簇,可通过列簇指定这一个列簇中所有列的版本号。一般将具有相同io属性的字段放入同一个列簇。创建表时必须指定至少一个列簇。一张表一般不建议超过两个列簇,一般在一到两个列簇,超过两个之后,hbase的读写性能会降低。
Column
列,列标签,一个列簇下有多个列,一个列必属于某个列簇,两个列簇中的列个数不必相等。两个行同一列簇下运行有不同的列,即半结构化。通过cf:c 唯一指定一列。
Value
值,每一列的值,hbase底层没有数据类型,都是字节存储,允许存储多版本的值
Version
版本数,属于列簇的一个属性,默认版本为1个,可以配置多版本来存储多版本的数据 默认查询时,只显示每一列的最新的那个版本数据 如果每条存储数据超过版本数,旧的版本会被覆盖 设计多版本是为了存储,默认显示最新版本是为了读取
TimeStamp
时间戳,用来区分版本, version版本数是一个设置,timestamp是一个标识 默认值是数据插入的格林尼治时间 查询是需要指定时间戳才能唯一确定一条数据 每一列都有一个时间戳,同行同列簇同版本下的不同列的时间戳相同
列存储
概念:
区别于rdbms的行式存储格式,nosql使用列式存储格式。
优点:
行式存储操作实时行级别的,列式存储操作是列级别的。所以粒度更细 行式存储一行中的列都是一样的(结构化),列式存储每一行中的列可以不一样(半结构化)。所以支持的数据格式更广泛,半结构化可以支持结构化数据,结构化数据不可以支持半结构化数据 性能更优化,读写速度更快
DDL
代码语言:javascript复制1. namespace
list_namespace
create_namespace
drop_namespace
describe_namespace
list_namespace_tables
2. ddl
备注:只有管理员admin才能操作
list
create:创建表时只需要给定表名和列簇名称,两种方式:配置列簇属性和不配置列簇属性
describe/desc
drop:hbase中表是enable状态的话,不允许对表结构进行修改和删除,需要先disable禁用
disable
enable
DML
代码语言:javascript复制1.dml
put:hbase中没有更新和删除操作,所有的更新都是插入
scan:用于查询一行或多行,或全表扫描
get:用于查询某一行,就是某一个rowkey的数据
delete
热点问题与数据倾斜
概念
热点问题:客户端的请求大量集中在某一个节点,导致该节点上的负载非常高,而其他节点负载过低的-
现象
数据倾斜:由于计算任务大量集中在某一个节点,导致该节点上的负载非常高,而其他节点负载过低的现象
区别
- 热点问题:请求层面,热点问题可能会造成数据倾斜
- 数据倾斜:计算层面,数据倾斜不一定由热点问题造成
原因
热点问题在很多场景中都会发生,在hbase中表现造成热点问题的原因是:
1.客户端大量请求的rowkey连续集中在某一个region上 2.表中只有一个分区,所有的请求都集中在这个region上
解决
1.需要设计好表的分区region范围,避免分区范围不均衡,通过分区范围解决 2.需要创建预分区,多分区,通过分区个数解决
预分区
背景:在创建表的时候,默认只有一个分区,此时容易操作服务端热点问题
概念:在创建表的时候,根据rowkey的分布来设计一张表刚开始就有多个分区,分区需要根据rowkey的前缀或者完整的rowkey来设计,因为region的分配就是根据rowkey来划分分区的
意义:
1.增加数据读写效率
2.负载均衡,防止数据倾斜
3.方便集群容灾调度region
4.优化Map 数量
方式:
1.指定分区范围 create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']
2.适用于分区较多场景 指定规则文件 create 'ns1:t2', 'f1', SPLITS_FILE => '/export/datas/splits.txt'
3.通过java API 来划分分区admin.createTable(HtableDescriptor desc,byte splitKeys)
RowKey的设计规则
原因:为什么rowkey设计至关重要?
- 所有数据在存储时都是根据rowkey来读写对应分区的,即分区是根据rowkey来划分的。
- rowkey是hbase中的唯一索引。
- rowkey在查询时是前缀匹配的。
后果:rowkey设计不好会产生什么后果?
- 会造成热点问题
- 会造成全表扫描
设计规则:
唯一原则:任何一个rowkey,必须唯一标记一行,类似于主键的唯一性
散列原则:
构建rowkey的随机散列,不允许rowkey是连续的,避免热点问题。
若rowkey前缀是一个固定的字段,且会产生连续,如何解决呢?
- 编码,通过md5/crc32等方式来将固定字段进行编码然后作为rowkey
- 反转,rowkey是按照ascii码的字典顺序,固定字段反转之后,就不连续
- 随机数,将固定字段加随机数,不推荐,因为不便于维护
业务原则:根据业务维度来设计rowkey,例如将经常作为查询条件的维度
组合原则:将各个业务维度组合来设计rowkey
长度原则:一般rowkey的长度不要超过100个字节,可以使用十六进制或三十二进制压缩长度
JavaAPI
代码语言:javascript复制介绍:
1.hbase中 hbase shell 一般用来封装脚本,执行ddl操作,一般用来封装做ddl操作通过hbase shell xxx.query指定,脚本操作结尾要加exit
2.javaapi则是实际使用场景,一般用来执行dml操作,和分布式框架使用,做中间件技术,提供程序访问hbase的接口
API方法:
1.HBaseConfiguration; 创建hbase的初始化对象
2.HBaseAdmin;hbase的管理员对象,需要进行ddl操作时需要的对象
1.tableExists
2.disableTable
3.deleteTable
4.deleteNamespace
5.listNamespaceDescriptors
3.HTableDescriptor;hbase的表描述对象
1.addFamily
2.createTable
3.getTableName
4.TableName;表名称对象
1.getNameAsString
5.HColumnDescriptor;hbase的列簇描述对象
1.setMaxVersions
2.setBlockCacheEnabled
3.
6.NamespaceDescriptor;hbase的名称空间描述对象
1.createNamespace
2.create
7.Get;get命令获取数据
8.Result;一个result代表一个rowkey的所有数据
1.rawCells
9.Cell;一个cell就是一列的数据
10.Table;表对象
1.get
2.put
3.delete
4.getScanner
11.Put;
1.addColumn
12.Delete;
1.addColumn
13.ResultScanner;result对象的集合
读取
代码语言:javascript复制1.驱动类,封装了mr程序中的参数集合,调用方法TableMapReduceUtil.inittablemapper(输入和map的参数);
2.mapper类,需要继承TableMapper类,默认input输入的key和value为ImmutableBytesWritable和Result
实现:
底层通过tableinpputformat实现;sqoop底层通过dbinputformat实现
<解释>
因为mr读取hbase数据,只是做了数据的读取,并不做数据的聚合,所有不需要reduce
写入
代码语言:javascript复制1.驱动类,封装了mr程序中的参数集合,调用方法TableMapReduceUtil.inittablereduce(reduce和输出的参数);
2.reducer类,需要继承TableReducer类,默认output输出的value为put对象
实现:
底层通过TableOutputFormat实现;sqoop底层通过dboutputformat实现
<解释>
需要在reduce类中构建put对象,用于存储数据,所以需要reduce
Filter 过滤器
背景知识:
- 在hbase中,所有数据在存储时按照rowkey的ASCII码表字典顺序进行排序的
- 在hbase中,rowkey的查询都是前缀匹配
- rowkey是hbase中的唯一索引,所有数据的读写要么通过rowkey查询,要么就是全表扫描
- hbase底层没有数据类型,数据全部都是按照字节数组存储,
- 按照keyvalue对格式存储每一列数据,key=ns tb cf c ts,value=value
- 一般搭配scan来使用
shell过滤器种类:
1. TimestampsFilter,时间戳过滤器
2. QualifierFilter,列名过滤器,需要指定比较参数和比较值 QualifierFilter (>=, 'binary:xyz')
3. ROWPREFIXFILTER,rowkey前缀过滤器
javaapi过滤器种类:
第一类:比较过滤器;通过比较的工具类,来实现过滤,返回符合的rowkey所有的数据
1.RowFilter
2.FamilyFilter
3.QualifierFilter
4.ValueFilter
第二类:专有过滤器;用的比较多,封装好的方法来实现
1.prexfixFilter:rowkey前缀过滤器
2.singleColumnValueFilter/singleColumnValueExcludeFilter:单列值过滤器
3.MultipleColumnPrefixFilter:多列过滤器
4.pageFilter:分页过滤器,一般在工作中,必须指定对应的起始位置,一般搭配startrow来使用
第三类:组合过滤器,就是各种过滤器组合在一起,FilterList
如何使用过滤器:
1.shell中使用"{列属性}"指定,例如:
{COLUMNS => ''};{STARTROW => '闭区间',STOPROW => '开区间'}
{ROWPREFIXFILTER => '',FILTER => ''}
Hbase:ns/Hbase:meta
"hbase:namespace"表,这张表会记录hbase中所有namespace的信息,当用户进行请求时,会读取这张表的信息来判断用户访问的namespace是否存在
"hbase:meta"表,hbase表中的元数据表,meta表中记录了:
meta表中的rowkey包含了regioninfo和server两个部分,其中包含regioninfo的rowkey,记录了region的名称,startkey和stopkey。包含server的rowkey记录了这个region所在的regionserver的地址和端口。
meta表记录了除了自己之外所有的在hbase中的表的region的名称;其中“region名称”的构成:表名称 region的起始范围 region的时间 region的唯一id。
补充:
1.数据读写请求,客户端如何找到对应的region的region的?
A:zk记录了meta表的"region信息","region信息"包括region的名称,startkey和stopkey,还包含了这个region所在的节点地址和端口,通过这些去对应regionserver上找到的meta表的数据,然后meta表中记录了除了自己之外的hbase中所有表的"region信息",这些"region信息"也包括region的名称,startkey和stopkey,还包含了这个region所在的节点地址和端口。然后通过这些信息再去找到对应regionserver上的region
Hbase的存储原理
读写请求概述:
- 客户端第一次请求时,本地没有记录region信息,先去请求zk,获取meta表的region信息。
- 然后再跟据region信息请求对应的regionserver,获取meta表的数据,得到要查询表中rowkey对应的region信息。
- 客户端再次跟据返回的表中rowkey对应的region信息去请求regionserver提交读写请求,并缓存本地请求的地址及region信息,供下次使用,直接请求regionserver获取表数据。
- regionserver跟据请求来操作region,跟据列簇来判断读写哪个store。
读请求:
请求会优先读取内存中数据,即memstore,若数据没有在内存中,就读缓存,然后读hdfs
写请求:
- 请求会先写入内存memstore中,当memstore满足条件时,触发flush刷写,将memstore中的数据变成storefile文件
- 通过hbase底层封装的hdfs客户端,将文件生成在以hfile的文件类型存储在hdfs上
Hbase读写请求流程
写请求流程
- 客户端提交一条写的命令,根据这个请求的rowkey来获取对应的region信息,来请求对应的regionserver,以此来对rowkey进行操作
- 将请求提交给对应的regionserver,regionserver根据请求写入的region的名称来操作
- 根据请求中的列簇来判断要写入该region中的哪个store
- 将数据以keyvalue形式写入memstore
- memstore达到一定条件以后,会触发flush,内存中数据会写入hdfs,
- hdfs中的storefile达到一定条件以后,会触发compaction,将storefile文件归并排序进行合并
- 当整个region的数据大小达到一定条件以后,会触发split,一个region会分裂成两个region,原来的region被销毁
读请求流程
客户端请求zk,从zk获取meta表的地址
客户端读取meta表的数据,根据读命令中的表名来获取表所有的reigon信息,
找到要读取的region以及对应的regionserver地址
如果是get请求,指定了rowkey,则返回这个rowkey对应的region信息
如果是scan请求,则返回这张表对应的所有的region信息
根据返回的reigon地址,请求对应的regionserver,
regionserver根据region名称来找到对应的region,并进行读操作
若请求中指定了列,则读取列簇对应的store,若请求中没有指定列,则读取所有store的数据
请求在读取store时,优先读取内存中的store,即memstore,若memstore中没有找到,若开启了缓存,则取memcache缓存中找,若没有开启缓存,则直接去hdfs上找storefile,若hdfs上也没有,就是数据不存在,直接返回,若是hdfs上有,就将这条数据返回,若开启了缓存,还需要将数据添加到memcache中,方便下次使用
代码语言:javascript复制查询内存:
有:返回
没有:缓存:
有:返回
没有:hdfs
有:返回并添加缓存
没有:返回
解释:
- 经实践分析,新写入的数据在请求访问是命中率最高,所以保存在内存中
- 根据LRU最近最少使用算法,将访问频率高的数据添加到缓存中,提高缓存访问的命中率
LSM-tree模型
概念:Log-Struct-Merge 模型树,即wal,flush,compaction,split等过程;
功能:将随机无序的数据变成有序的数据,通过有序的算法来加快数据的读取,因为写入时需要进行排序,所有牺牲了一定的写入数据的效率,都用内存来实现数据的读写的存储
主要步骤:
- 请求时现将数据保存到预写日志,然后写入内存
- 在内存中对数据进行排序
- 将内存中的数据写入文件,构成一个有序数据文件
- 合并:将有序的多个数据文件进行合并生成一个新的有序的文件
- 删除和更新:在模型设计中,本身并没有删除和更新,通过插入来模拟的,一般都是在文件合并时来进行实际的物理删除操作
WAL
- 概念:预写日志, write-ahead-log
- 职责:当产生写的请求后,写请求提交给regionserver,regionserver会将这个写的操作记录在WAL中,即Hlog,然后再写入memstore
- 存储:将WAL存储在hdfs上,每台regionserver维护一份WAL,
- 场景:用于数据恢复,hfile由hdfs恢复,memstore由WAL恢复
补充:
- oldwals:不再被需要的Hlog日志文件
Flush
- 概念:刷写
- 功能:数据在memstore中先做一次按rowkey的字典顺序排序,然后将memstore中的数据写入持久化到hdfs上,会生成一个有序的storefile文件
- 场景:由于memstore是使用的内存,内存容量有限,为了提高新数据的命中率,需要将老的数据吸入hdfs进行安全存储
Compaction
概念:合并
功能:将hdfs上的多个storefile文件进行合并,构建统一的有序文件
场景:为提供文件的快速读取,将多个storefile文件合并成一个整体有序的storefile文件,因为读取多个数据源没有读取一个数据源快
过程:
代码语言:javascript复制minor compaction:将hdfs上早些生成的一些文件进行合并
major compaction:将所有的文件进行合并,合并的过程中会将标记为删除或过期的数据真正的删除
Split
- 概念:分裂.
- 功能:若一个region分区的数据过多,会分裂成两个region.
- 场景:一个分区的数据过多时,就会容易造成服务端引起的热点问题,无法实现分布式高性能,此时由regionserver将这一个分区均分为两个分区,由master重新将两个分区分配到 不同的regionserver中,分配成功后旧的region下线.
数据加载
概念:将数据导入到hbase中,有两种实现方式。
- 使用put对象
例如:hbase shell,java api,mr程序封装。缺点:数据流经过WAL,然后经过内存,最后再到hdfs上,当导入海量数据是,容易导致region和hdfs的io过高,增加服务端负载,影响其他应用。
- Bulkload
原理:hbase底层存储是hdfs上的hfile文件,然后通过meta表关联数据,所以可以先本地将数据转换为hfile文件,然后上传到hdfs上去,同时补充上meta表数据。
场景:适用于导入大量数据的批量hbase场景,要求稳定性能
缺点:
数据第一次读取时都是在hdfs上的,没有存在memstore中,所以第一次会变慢,但是如果数据量特别大的时候,两种方式最终的数据第一次读取都是在hdfs上的,所以没差别
数据直接从传到hdfs上的,没经过WAL,所以当出现数据丢失,没办法恢复数据,需要重新再转换一次
实现:
应用程序实现:
- 负责将普通文件转换成为hfile文件
- 负责将转换好的hfile文件加载到hbase表中
hbase自带实现:
1.ImportTSV,是hbase-server.jar包中自带的一个命令,可以实现使用put方式将数据导入hbase表中,也实现了将普通文件转换成一个hfile文件的两种方式
2.completebulkload,上面的importtsv命令将普通文件转换成一个hfile文件,但是并没有加载到hbase表中,completebulkload负责将hfile文件加载到hbase表中
代码语言:javascript复制<命令>:
1. yarn jar /export/servers/hbase-1.2.0-cdh5.14.0/lib/hbase-server-1.2.0-cdh5.14.0.jar
importtsv -Dimporttsv.columns=a,b,c <tablename> <inputdir> -Dimporttsv.separator=','
-Dimporttsv.bulk.output=/bulkload/output2
2. yarn jar export/servers/hbase-1.2.0-cdh5.14.0/lib/hbase-server-1.2.0-cdh5.14.0.jar
completebulkload /bulkload/output2 mrhbase
<解释>:
-Dimporttsv.columns 指定文件中列映射的列簇及列
Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:age,info:sex mrhbase /bulkload/input
-Dimporttsv.separator 指定读取文件中的列分隔符,默认以制表符分隔
-Dimporttsv.bulk.output 指定生成的HFILE文件所在的hdfs的位置
SQL on Hbase
背景:
hbase是nosql数据库,有自己的api实现,不支持sql语言,不利于开发和数据分析人员,sql on hbase 解决了这一场景,在hbase上使用sql/jdbc操作
原理:
- 基于java api/mapreduce 实现
- 基于hbase shell 实现
- 搭桥通过hdfs实现
实现:
hive集成hbase:
原理:通过hive中的hql语句,底层转换为mapreduce操作,在mapreduce操作的同时,也用mapreduce操作hbase表 实现:在hive中创建一张与hbase关联的表,操作hive中关联表,实际上是对hbase在操作
phoenix第三方工具:
原理:基于hbase构建了二级索引,直接调用hbase的api实现,因此在于hbase集成度和性能是最优选
sqoop第三方工具:
原理:底层也是使用mapreduce程序导入数据,从关系型数据库中导入到hdfs,然后使用importtsv命令和completebulkload命令来完成从hdfs上的导入,sqoop可以导入,但是不能导出,因为半结构化数据支持结构化数据存储,结构化数据不支持半结构化数据存储
补充:
- 若hbase表已经存在,hive中只能创建外部表,使用key来表示rowkey
- 若hbase表不存在,默认以hive表的第一列作为hbase的rowkey
- hbase与hive关联,hive中的关联表加载数据时不能使用load加载,因为load命令底层没有使用mapreduce,因为load命令是使用hdfs的put命令,只能用insert命令
二级索引
概念:基于rowkey再构建一个索引,称为二级索引。
意义:rowkey是唯一索引,而且rowkey是前缀匹配,若我们不知道前缀,但知道rowkey部分字段,只能全部扫描吗?二级索引就是解决了rowkey唯一索引这个问题。
解决:构建二级索引。
方式:
代码语言:javascript复制创建索引表,将原表中的查询条件作为索引表的rowkey,将原表中的rowkey作为索引表中的value;
查询是若不指定原表的前缀,就先根据查询条件去查询索引表,找到原表的rowkey,再根据获得的rowkey去查原表
协处理器
- 背景:构建二级索引,因为索引表和原表是两张不同的表,如何保证两张表的数据同步?
- 同步方式:手动进行两次插入,在插入原表的同时,也插入索引表
- 缺点:性能较差,操作繁琐,请求数加倍,对服务器负载过高
- 构建协处理器,构建类似于mysql中的触发器
- 依靠第三方工具,让他们来实现二级索引:例如:solr,ES 构建索引类型丰富,可以实现自动同步,Phoenix用sql构建索引
- 概念:Coprocessor,协助开发者构建自定义的二级索引
- 本质:自定义实现hbase提供对应接口的一段程序
分类:
- observer:观察者的协处理器,类似于监听功能,类似于触发器,一般用于二级索引同步 功能:监听原表,只看客户端往原表中插入一条数据,协处理器自动往索引表中插入一条数据
- endpoint:终端者协处理器,类似于存储过程,或java中达到一个方法,一般用来做分布式聚合统计 功能:监听一张表,这张表的每个region都会计算自己的rowkey个数,当客户端调用时,就会返回每个region的个数
- 补充:如何快速统计一张表的rowkey个数
- 通过编写mapreduce方法,最慢的最蠢的方式
- 使用hbase自带的count命令
- 开发endpoint协处理,最快最有效的方式
Hbase优化
概念:hbase优化可通过以下几个方面,flush,compaction,split和列簇属性来实现
Flush
- 意义:用于将memstore中的数据写入HDFS,变成storefile文件,空出内存,用于存储新数据
- 自动触发规则:
默认单个memstore的存储达到128M,就会触发。
默认整个region的memstore的数据达到128M * 4,就会触发。
默认整个regionserver中的memstore的使用率达到堆内存的40%,就会触发。
缺点:
自动触发,会导致磁盘IO的负载过高,会影响业务,一般会关闭自动触发,根据实际情况定期的在业务比较少的时候,手动触发
手动触发:
关闭自动触发:将所有自动触发的条件调高,定期的在没有达到自动触发的条件之前通过flush命令手动触发
总结:
将自动触发条件调高,以此来关闭自动触发flush,并在业务量较少时通过手动触发flush,以此来达到优化目的
Compaction
- 意义:用于实现将多个storefile文件,合并成一个storefile文件,变成一个整体有序的文件,加快读的效率
自动触发规则:
代码语言:javascript复制minor compaction:轻量级的合并,合并部分storefile成一个文件,smart算法,将最早生成的三个文件合并,不定期执行,占用IO比较小
major compaction:重量级的合并,合并所有的storefile文件成一个文件
默认七天会触发一次compaction
缺点:
总结:
关闭自动触发major compaction,并在业务量较少时通过手动触发major_compact
Split
意义:将一个region分裂成两个region,分摊region的负载
自动触发规则:
缺点:
代码语言:javascript复制自动触发,会导致磁盘IO的负载过高,会影响业务,一般会关闭自动触发,根据实际情况定期的在业务比较少的时候,手动触发
手动触发:
关闭自动触发:hbase.client.keyvalue.maxsize=100GB,不可能达到这个值,根据集群region的数据量,来定期手动触发
- 总结:
关闭自动触发,并在业务量较少时通过手动触发split
列簇属性
- BLOOMFILTER:布隆过滤器
用于在检索storefile文件时,根据索引判断该storefile文件中是否有想要的数据,若没有直接跳过。
级别:none 不开启。
row 判断是否有需要的rowkey,没有就跳过。
rowcol 判断是否有需要的rowkey和cf:c,没有就跳过。
- Versions:MaxVersions,最大版本数,该列族中的列最多允许存储多少个版本。
- TTL:版本存活时间,从该版本的timestamp进行计算,一旦到达这个时间,就表示该数据过期,用户hbase自动清理数据。
- MIN_VERSIONS:最小版本数,该列族中的数据当达到TTL时间以后,最小保留几个版本。
- BLOCKCACHE:读缓存,是否将这个列簇的数据读到memcache缓存中,默认开启,建议将不经常读取的列簇关闭。
- IN_MEMORY:代表BLOCKCACHE中的一种缓存的最高级别,默认是关闭的,IN_MEMORY是最高级别的缓存,缓存会定期的进行LRU清理,缓存级别越低,优先会被清理,不要随便开启IN_MEMORY,因为meta表就在IN_MEMORY级别的缓存中
- BLOCKSIZE:storefile文件块的大小,默认64k,数据块越小,数据块越多,维护索引越多,内存占用越多,读快写慢,数据块越大,数据块越少,维护索引越少,内存占用越少,读慢写快
- COMPRESSION:Hbase中的压缩其实就是hadoop的压缩,本身就是压缩storefile的存储
Hbase和RDBMS对比
Hbase
代码语言:javascript复制支持向外扩展
使用API 和MapReduce 来访问HBase 表数据
面向列,即每一列都是一个连续的单元
数据总量不依赖具体某台机器,而取决于机器数量
HBase 不支持ACID(Atomicity、Consistency、Isolation、Durability)
适合结构化数据和非结构化数据
一般都是分布式的
HBase 不支持事务
不支持Join
rdbms
代码语言:javascript复制支持向上扩展
使用SQL 查询
面向行,即每一行都是一个连续单元
数据总量依赖于服务器配置
具有ACID 支持
适合结构化数据
传统关系型数据库一般都是中心化的
支持事务
支持Join
Hbase的增删改查
- 功能:hbase中实际没有更新和删除操作,hbase中只有增加和查询操作;更新是将新数据插入内存,,删除是将数据逻辑删除,等后续合并时再做覆盖旧版本数据和物理删除数据
- 意义:为了快速返回处理读写请求,数据只要写入就里面返回结果
- get与scan的区别:
get命令是hbase中最快的查询方式,因为指定了索引rowkey,但是只能查询一条数据。
scan命令可以返回多条数据,一般结合过滤器使用。
put和delete没有返回结果。
hbase/hive/hdfs/rdbms的区别
hbase
- 概念:实时读写的数据库
- 用途:存储数据
- 场景:用于实现实现大量数据的实时读写文件,例如搜素引擎,一般用于高性能计算架构中,能存储半结构或结构化数据
hive
- 概念:结构化数据仓库
- 用途:存储数据
- 场景:只能用于存储结构化数据,用来构建数据仓库的工具
hdfs
- 概念:文件存储系统
- 用途:存储数据
- 场景:可以用于存储结构化,半结构化,非结构化的数据,适用于一次存入多次读取,例如网盘,用于做数据归档
rdbms
- 概念:关系型数据库
- 用途:存储数据