起源于谷歌旧三篇论文中bigtable。
为了解决大数据环境中海量结构化数据的实时读写问题。为了弥补hadoop生态中没有实时存储的缺陷。
分布式
列存储:底层数据文件存储格式是列式存储
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上
依赖于hdfs:将海量的数据存到hdfs上,即保证了数据了安全性,又保证了数据的持久化
不支持join操作,只支持行级事务,复杂join操作需要集成hive
只支持字节数组类型
稀疏:对于空null的列,并不占用存储空间,因此表的设计可以非常稀疏
物理架构
种类:
hue:hbase实现了ThriftServer服务端,用于提供hue客户端连接,thriftserver来实现整个过程的访问以及执行
sql on hbase:底层使用的mapreduce操作hbase
概念:hbase中的zk被称为真·主节点
职责:
内容:
每一个节点都会在zookeeper上去注册
zookeeper存储了meta表的region信息,客户端通过访问zookeeper才能请求集群
hdfs:
职责:存储hfile,wal
Hmaster:
概念:辅助主节点
职责:
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级别的
补充:
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使用列式存储格式。
优点:
行式存储操作实时行级别的,列式存储操作是列级别的。所以粒度更细 行式存储一行中的列都是一样的(结构化),列式存储每一行中的列可以不一样(半结构化)。所以支持的数据格式更广泛,半结构化可以支持结构化数据,结构化数据不可以支持半结构化数据 性能更优化,读写速度更快
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
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的随机散列,不允许rowkey是连续的,避免热点问题。
若rowkey前缀是一个固定的字段,且会产生连续,如何解决呢?
业务原则:根据业务维度来设计rowkey,例如将经常作为查询条件的维度
组合原则:将各个业务维度组合来设计rowkey
长度原则:一般rowkey的长度不要超过100个字节,可以使用十六进制或三十二进制压缩长度
JavaAPI
介绍:
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对象的集合
1.驱动类,封装了mr程序中的参数集合,调用方法TableMapReduceUtil.inittablemapper(输入和map的参数);
2.mapper类,需要继承TableMapper类,默认input输入的key和value为ImmutableBytesWritable和Result
实现:
底层通过tableinpputformat实现;sqoop底层通过dbinputformat实现
<解释>
因为mr读取hbase数据,只是做了数据的读取,并不做数据的聚合,所有不需要reduce
1.驱动类,封装了mr程序中的参数集合,调用方法TableMapReduceUtil.inittablereduce(reduce和输出的参数);
2.reducer类,需要继承TableReducer类,默认output输出的value为put对象
实现:
底层通过TableOutputFormat实现;sqoop底层通过dboutputformat实现
<解释>
需要在reduce类中构建put对象,用于存储数据,所以需要reduce
背景知识:
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: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
读写请求概述:
读请求:
请求会优先读取内存中数据,即memstore,若数据没有在内存中,就读缓存,然后读hdfs
写请求:
写请求流程
读请求流程
客户端请求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中,方便下次使用
查询内存:
有:返回
没有:缓存:
有:返回
没有:hdfs
有:返回并添加缓存
没有:返回
解释:
概念:Log-Struct-Merge 模型树,即wal,flush,compaction,split等过程;
功能:将随机无序的数据变成有序的数据,通过有序的算法来加快数据的读取,因为写入时需要进行排序,所有牺牲了一定的写入数据的效率,都用内存来实现数据的读写的存储
主要步骤:
补充:
概念:合并
功能:将hdfs上的多个storefile文件进行合并,构建统一的有序文件
场景:为提供文件的快速读取,将多个storefile文件合并成一个整体有序的storefile文件,因为读取多个数据源没有读取一个数据源快
过程:
minor compaction:将hdfs上早些生成的一些文件进行合并
major compaction:将所有的文件进行合并,合并的过程中会将标记为删除或过期的数据真正的删除
概念:将数据导入到hbase中,有两种实现方式。
例如:hbase shell,java api,mr程序封装。缺点:数据流经过WAL,然后经过内存,最后再到hdfs上,当导入海量数据是,容易导致region和hdfs的io过高,增加服务端负载,影响其他应用。
原理:hbase底层存储是hdfs上的hfile文件,然后通过meta表关联数据,所以可以先本地将数据转换为hfile文件,然后上传到hdfs上去,同时补充上meta表数据。
场景:适用于导入大量数据的批量hbase场景,要求稳定性能
缺点:
数据第一次读取时都是在hdfs上的,没有存在memstore中,所以第一次会变慢,但是如果数据量特别大的时候,两种方式最终的数据第一次读取都是在hdfs上的,所以没差别
数据直接从传到hdfs上的,没经过WAL,所以当出现数据丢失,没办法恢复数据,需要重新再转换一次
实现:
应用程序实现:
hbase自带实现:
1.ImportTSV,是hbase-server.jar包中自带的一个命令,可以实现使用put方式将数据导入hbase表中,也实现了将普通文件转换成一个hfile文件的两种方式
2.completebulkload,上面的importtsv命令将普通文件转换成一个hfile文件,但是并没有加载到hbase表中,completebulkload负责将hfile文件加载到hbase表中
<命令>:
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的位置
背景:
hbase是nosql数据库,有自己的api实现,不支持sql语言,不利于开发和数据分析人员,sql on hbase 解决了这一场景,在hbase上使用sql/jdbc操作
原理:
实现:
hive集成hbase:
原理:通过hive中的hql语句,底层转换为mapreduce操作,在mapreduce操作的同时,也用mapreduce操作hbase表 实现:在hive中创建一张与hbase关联的表,操作hive中关联表,实际上是对hbase在操作
phoenix第三方工具:
原理:基于hbase构建了二级索引,直接调用hbase的api实现,因此在于hbase集成度和性能是最优选
sqoop第三方工具:
原理:底层也是使用mapreduce程序导入数据,从关系型数据库中导入到hdfs,然后使用importtsv命令和completebulkload命令来完成从hdfs上的导入,sqoop可以导入,但是不能导出,因为半结构化数据支持结构化数据存储,结构化数据不支持半结构化数据存储
补充:
概念:基于rowkey再构建一个索引,称为二级索引。
意义:rowkey是唯一索引,而且rowkey是前缀匹配,若我们不知道前缀,但知道rowkey部分字段,只能全部扫描吗?二级索引就是解决了rowkey唯一索引这个问题。
解决:构建二级索引。
方式:
创建索引表,将原表中的查询条件作为索引表的rowkey,将原表中的rowkey作为索引表中的value;
查询是若不指定原表的前缀,就先根据查询条件去查询索引表,找到原表的rowkey,再根据获得的rowkey去查原表
分类:
概念:hbase优化可通过以下几个方面,flush,compaction,split和列簇属性来实现
默认单个memstore的存储达到128M,就会触发。
默认整个region的memstore的数据达到128M * 4,就会触发。
默认整个regionserver中的memstore的使用率达到堆内存的40%,就会触发。
缺点:
自动触发,会导致磁盘IO的负载过高,会影响业务,一般会关闭自动触发,根据实际情况定期的在业务比较少的时候,手动触发
手动触发:
关闭自动触发:将所有自动触发的条件调高,定期的在没有达到自动触发的条件之前通过flush命令手动触发
总结:
将自动触发条件调高,以此来关闭自动触发flush,并在业务量较少时通过手动触发flush,以此来达到优化目的
自动触发规则:
minor compaction:轻量级的合并,合并部分storefile成一个文件,smart算法,将最早生成的三个文件合并,不定期执行,占用IO比较小
major compaction:重量级的合并,合并所有的storefile文件成一个文件
默认七天会触发一次compaction
缺点:
总结:
关闭自动触发major compaction,并在业务量较少时通过手动触发major_compact
意义:将一个region分裂成两个region,分摊region的负载
自动触发规则:
缺点:
自动触发,会导致磁盘IO的负载过高,会影响业务,一般会关闭自动触发,根据实际情况定期的在业务比较少的时候,手动触发
手动触发:
关闭自动触发:hbase.client.keyvalue.maxsize=100GB,不可能达到这个值,根据集群region的数据量,来定期手动触发
关闭自动触发,并在业务量较少时通过手动触发split
用于在检索storefile文件时,根据索引判断该storefile文件中是否有想要的数据,若没有直接跳过。
级别:none 不开启。
row 判断是否有需要的rowkey,没有就跳过。
rowcol 判断是否有需要的rowkey和cf:c,没有就跳过。
Hbase
支持向外扩展
使用API 和MapReduce 来访问HBase 表数据
面向列,即每一列都是一个连续的单元
数据总量不依赖具体某台机器,而取决于机器数量
HBase 不支持ACID(Atomicity、Consistency、Isolation、Durability)
适合结构化数据和非结构化数据
一般都是分布式的
HBase 不支持事务
不支持Join
rdbms
支持向上扩展
使用SQL 查询
面向行,即每一行都是一个连续单元
数据总量依赖于服务器配置
具有ACID 支持
适合结构化数据
传统关系型数据库一般都是中心化的
支持事务
支持Join
get命令是hbase中最快的查询方式,因为指定了索引rowkey,但是只能查询一条数据。
scan命令可以返回多条数据,一般结合过滤器使用。
put和delete没有返回结果。
hbase
hive
hdfs
rdbms