hbase
hbase前言
Google引爆大数据时代的三篇论文
- 谈到Hadoop的起源,就不得不提Google的三驾马车:Google FS、MapReduce、BigTable。虽然Google没有公布这三个产品的源码,但是他发布了这三个产品的详细设计论文,奠定了风靡全球的大数据算法的基础!
gfs:2003年,Google发布Google File System论文,这是一个可扩展的分布式文件系统,用于大型的、分布式的、对大量数据进行访问的应用。它运行于廉价的普通硬件上,提供容错功能。从根本上说:文件被分割成很多块,使用冗余的方式储存于商用机器集群上
- 紧随其后的就是2004年公布的 MapReduce论文,论文描述了大数据的分布式计算方式,主要思想是将任务分解然后在多台处理能力较弱的计算节点中同时处理,然后将结果合并从而完成大数据处理。
传说中,Google使用它计算他们的搜索索引。而Mikio L. Braun认为其工作模式应该是:Google把所有抓取的页面都放置于他们的集群上,然后每天使用MapReduce来重算。
- Bigtable发布于2006年,启发了无数的NoSQL数据库,比如:Cassandra、HBase等等。Cassandra架构中有一半是模仿Bigtable,包括了数据模型、SSTables以及提前写日志(另一半是模仿Amazon的Dynamo数据库,使用点对点集群模式)。
行式存储和列式存储
-
行式存储倾向于结构固定,列式存储倾向于结构弱化
-
行式存储存储一行数据仅需要一个主键,列式存储存储一行数据需要多份主键
-
列式存储存储的都是业务数据,而列式存储除了业务数据之外,还需要存储列名。
-
行式存储更像是一个Java Bean,所有的字段都提前定义好,且不能改变;列式存储更像是一个Map,不提前定义,随意往里面添加key/value
hbase逻辑结构
-
列名开始 烈簇划分 相同类型的数据放到一块 person office
-
rowkey 唯一标识 字典序
-
一张表被切的乱七八糟 横着是按照rowkey切的 竖着是按照列簇切的 (像mysql中的高表和宽表)
横向一刀是一个region(切片) 数据量大了就切分
-
store 实际的存在hdfs中的数据
hbase物理结构
- 这一块可以看成是一个mysql中的表
- hbase是怎么存的呢?继续看timestamp type 时间戳导致的插入删除问题
数据模型
-
namespace命名空间,类似于关系型数据库的 DataBase 概念,每个命名空间下有多个表。HBase有两个自带的命名空间,分别是"hbase"和"default","hbase"中存放的是HBase内置的表,"default"表是用户默认使用的命名空间。
-
Region 的概念和关系型数据库的分区或分片类似。
Region 是 HBase中 分布式存储 和 负载均衡 的最小单元,Region 包括完整的行,所以 Region 是以行为单位 表的一个子集。不同的 Region 可以分别在不同的 RegionServer 上。
每个表一开始只有一个 Region,每个 Region 会保存一个表里 某段连续的数据,随着数据不断插 入表,Region不断增大,当增大到一个阀值的时候,Region就会二等分,生成两个新的Region;
Table 中的所有行都按照 RowKsey 的字典序排列,Table 在行的方向上分割为多个 Region,基于 Rowkey 的不同范围分配到不通的 Region 中(Rowkey的范围,第一个Rowkey的起始索引 和 最后一Rowkey的结束索引为空串
" "
,每个Region是前闭后开[起始索引, 结束索引)
) -
HBase 表中的每行数据都由 一个 RowKey 和 一个或多个 Column(列)组成,数据是按照 RowKey 的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重要。
-
Hbase 通过列簇划分数据的存储,列簇下面可以包含任意多的列,实现灵活的数据存取。列簇是由一个一个的列组成(任意多),在列数据为空的情况下,不会占用存储空间。
Hbase 创建表的时候必须指定列簇。就像关系型数据库创建的时候必须指定具体的列是一样的。
Hbase的列簇不是越多越好,官方推荐的是列簇最好小于或者等于3。一般是1个列簇。
新的列簇成员(列)可以随后动态加入,Family下面可以有多个Qualifier,所以可以简单的理解为,HBase中的列是二级列,也就是说Family是第一级列,Qualifier是第二级列。
权限控制、存储以及调优都是在列簇层面进行的;
HBase把同一列簇里面的数据存储在同一目录下,由几个文件保存。
-
HBase中的每个列,由列簇加上列限定符组成,一般是“列簇:列标识(column family : column qualifier)”,例如 info : name,info : age。创建表的时候只需指明列簇,不用指定列标识。
列限定符属于数据的一部分,每条数据的列限定符都可以不一样
-
**Time Stamp(version)**用于标识数据的不同版本(version),针对每个列簇进行设置(若列簇的version=3,那它下面的所有列的version都有3个版本),若建表时没有指定version,默认值version=1
在写入数据的时候,如果没有指定相应的timestamp,HBase会自动添加一个timestamp(默认与服务器时间保持一致)。timestamp也可以由客户显式赋值,如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。
每个cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
为了避免数据存在过多版本造成的的管理(包括存贮和索引)负担,hbase 提供了两种数据版本回收方式: 1、保存数据的最后 n 个版本 ; 2、保存最近一段时间内的版本(设置数据的生命周期 TTL)。
-
Cell
由{Rowkey,ColumnFamily : ColumnQualifier, TimeStamp} 确定唯一的cell。
cell 中的所有字段数据 都没有数据类型,全部是字节码形式存储。被视为字节数组
byte[]
详细架构
- master 要做的是DDL的工作
- regionserverDML crud 管理region 一个region掉了要将数据给另一个region查询
- region类Hregion
- store对应列簇
- mem store 1个小时 100w条等(刷写条件有很多 其中可能还有整个regionserver一起刷写 这个时候不好控制 有许多小文件生成)
- storefile命名 Hfile是存储格式kv 像txt格式等
- HLOG 实时通过dfsclient刷写到datanode
- 无论是找master还是找regionserver先找zk crud找zk就行
hbase写流程(读比写慢)
-
像zk请求meta表所在的regionserver (namespace为hbase 的命名空间有俩张表一张 system meta)
因为meta表中存了所有表的位置 得到的数据缓存下来
-
到指定的regionserver上拿具体要写入的表的位置 比如在另一个regionserver 发送put请求到另一个regionserver
-
先写wal
-
然后写到memstore(对于client来说结束了)
-
-ROOT- 0.9版本维护meta表的切分
-
Hregion类中step9
具体Flush的参数值
- hbase.regionserver.global.memstore.size=0.4 regionserver的全局memstore的大小 超过该大小触发flush到磁盘默认是堆大小的40% flush会阻塞客户端的读写
- hbase.hregion.memstore.flush.size=134217728 128M单个region里memstore的缓存大小
- hbase.regionserver.global.memstore.size.lower.limit=0.95 当集群负载非常高的时候写入 一直在flush 这个时候就不需要等0.4了 数据会积压,到0.4×0.95的时候memstore就开始刷了 还有排序尽量先刷写大的memstore
- hbase.regionserver.optionalcacheflushinterval=3600000 1h 晚上的时候2条 数据进来一个小时没有东西操作内存就刷写进去
- hbase.regionserver.max.logs wal文件大小可以预估要开始刷了0.9版本
hbase数据的读流程
LRU(最近最少使用)
读取的时候会将storeFile和memstore都读取出来然后做一个merge (比较时间戳)到block cache中 这就是读慢的原因
hbase数据的合并
compaction分为俩种 分别是minor 和major
compact小合并不删除,大合并删除过期数据
hbase.hregion.majorcompaction=604800000 默认七天大合并 建议关掉 设置为0 手动触发
hbase.hstore.compactionThreshold=3一个store里面允许存的hfile的个数 设置的个数越大可以减少触发和并的时间,但是每次的合并时间会越长
hbase数据的切分
- hbase.hregion.max.filesize=10737418240 以前的版本10g 现在是Math.min(tableRegionsCount^3 * initialSize,defaultRegionMaxFileSize)
initialSize:如果定义了hbase.increasing.policy.initial.size,则使用该值,否则用memstore刷写值得2倍,即hbase.hregion.memstore.flush.size*2。
取最小值 r为当前regionserver中属于该table的个数
刚开始只有一个Region,上限为1^31282=256M
当有2个Region,上限为2^31282=2048M
当有3个Region,上限为3^31282=6912M
以此类推当有4个Region时候,为16G,上限达到了10GB,最大值就保持在了10G,Region数量再增加也不会增加上限
-
所以要做预分区 因为自动切分会照成数据的热点问题,一个region一直越切越大
-
切分都是按照rowkey来切的
-
这里设计另一个知识为什么官方建议列簇一个不建议使用多个 因为如果你一个列簇数据多另一个列簇只有几条数据那么region一切分,每一个store是不是都对应一个memstore 个别的mestore 全局flush的时候形成大量的小文件
(如果数据同步增长则没有这种烦恼)
数据的删除
- 大合并的时候 或flush的时候 就是同一个内存(memstore)的flush到storefile时候 就是不同时间戳的俩条数据见面的时候
- type=deletecolumn 删除标记 光flush数据删除 标记还在 因为这个时候磁盘中可能还有别的version的数据 如果这个时候标记删除了 则下次flush的时候可能会数据诈尸 只有compact的时候将数据全部拉倒一块删除 删除标记才能删除