namespace:命名空间的作用是把多个属于相同业务领域的表分成一个组。一个表可以自由选择是否有命名空间,如果创建表的时候加上了命名空间后,这个表名字就成为了:<NameSpace> : <Table>。通过命名空间我们可以像关系型数据库一样将表分组,对于不同的组进行不同的环境设定,比如配额管理、安全管理

Hbase:系统表空间,用于HBase内部表。default:那些没有定义表空间的表都被自动分配到这个表空间下

Table(表):一个表由一个或者多个列族组成。数据属性,比如超时时间(TTL),压缩算法(COMPRESSION)等,都在列族的定义中定义。定义完列族后表是空的,只有添加了行,表才有数据

Row(行):一个行包含了多个列,这些列通过列族来分类。行中的数据所属列族只能从该表所定义的列族中选取,不能定义这个表中不存在的列族,否则你会得到一个NoSuchColumnFamilyException。由于HBase是一个列式数据库,所以一个行中的数据可以分布在不同的服务器上

Column Family(列簇):HBase会尽量把同一个列族的列放到同一个服务器上,这样可以提高存取性能,并且可以批量管理有关联的一堆列。所有的数据属性都是定义在列族上。

如下图为表设置了两个列族,而且,定义了每一个列族中要存放的列,如图所示:{Name} -> Column Family-A, {City, Phone, Gender} -> Column Family-B,不同列族的数据会被存储在不同的路径中。即,设置多个列族时一行数据可能存在于两个路径中。整行读取的时候,需要将两个路径中的数据合并在一起才可以获取到完整的一行记录。但如果仅仅读取Name一列的话,只需要读取Column Family-A即可

HBASE列族配置属性 hbase列族的作用_数据结构与算法

 

Column Qualifier(列):多个列组成一个行。列族和列经常用Column Family: Column Qualifier来一起表示。列是可以随意定义的,一个行中的列不限名字、不限数量,只限定列族

一个列中可以存储多个版本的数据。而每个版本就称为一个单元格(Cell),所以在HBase中的单元格跟传统关系型数据库的单元格概念不一样。HBase中的数据细粒度比传
统数据结构更细一级,同一个位置的数据还细分成多个版本

Timestamp(时间戳/版本号):你既可以把它称为是时间戳,也可以称为是版本号,因为它是用来标定同一个列中多个单元格的版本号的。当你不指定版本号的时候,系统会自动采用当前的时间戳来作为版本号

rowkey的排序规则:每一个行都有一个类似主键的rowkey,而这个rowkey在HBase中是严格按照字典排序的,也就是说11比2小,row11会排在row1和row2中间

ACID就是Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)的首字母缩写。ACID是事务正确执行的保证。HBase部分支持了ACID

一、Hbase宏观架构

 

HBASE列族配置属性 hbase列族的作用_数据结构与算法_02

Master:负责启动的时候分配Region到具体的RegionServer,执行各种管理操作,比如Region的分割和合并,创建表、修改列族配置

RegionServer:RegionServer上有一个或者多个Region。我们读写的数据就存储在Region上。如果你的HBase是基于HDFS的,那么Region所有数据存取操作都是调用了HDFS的客户端接口来实现的

Region:表的一部分数据。HBase是一个会自动分片的数据库。一个Region就相当于关系型数据库中分区表的一个分区

HDFS:Hadoop的一部分。HBase并不直接跟服务器的硬盘交互,而是跟HDFS交互,所以HDFS是真正承载数据的载体

ZooKeeper:把Master服务器关掉你的业务系统照样跑,能读能写;但是把ZooKeeper关掉,你就不能读取数据了,因为你读取数据所需要的元数据表hbase:meata的位置存储在ZooKeeper上

1、RegionServer架构

HBASE列族配置属性 hbase列族的作用_数据_03

一个WAL:预写日志,WAL是Write-Ahead Log的缩写。从名字就可以看出它的用途,就是:预先写入。当操作到达Region的时候,HBase先不管三七二十一把操作写到WAL里面去。HBase会先把数据放到基于内存实现的Memstore里,等数据达到一定的数量时才刷写(flush)到最终存储的HFile内。而如果在这个过程中服务器宕机或者断电了,那么数据就丢失了。WAL是一个保险机制,数据在写到Memstore之前,先被写到WAL了。这样当故障恢复的时候可以从WAL中恢复数据

多个Region:Region相当于一个数据分片。每一个Region都有起始rowkey和结束rowkey,代表了它所存储的row范围

1.1、Region结构

HBASE列族配置属性 hbase列族的作用_数据结构与算法_04

多个Store:每一个Region内都包含有多个Store实例。一个Store对应一个列族的数据,如果一个表有两个列族,那么在一个Region里面就有两个Store。在最右边的单个Store的解剖图上,我们可以看到Store内部有MemStore和HFile这两个组成部分。

1.1.1 Store 结构

  

HBASE列族配置属性 hbase列族的作用_数据结构与算法_05

MemStore:每个Store中有一个MemStore实例。数据写入WAL之后就会被放入MemStore。MemStore是内存的存储对象,只有当MemStore满了的时候才会将数据刷写(flush)到HFile中。

HFile:在Store中有多个HFile。当MemStore满了之后HBase就会在HDFS上生成一个新的HFile,然后把MemStore中的内容写到这个HFile中。HFile直接跟HDFS打交道,它是数据的存储实体

  WAL是存储在HDFS上的,Memstore是存储在内存中的,HFile又是存储在HDFS上的。数据是先写入WAL,再被放入Memstore,最后被持久化到HFile中。
数据在进入HFile之前已经被存储到HDFS一次了,为什么还需要被放入Memstore?
  这是因为HDFS上的文件只能创建、追加、删除,但是不能修改。那就是使用内存先把数据整理成顺序存放,然后再一起写入硬盘。这就是Memstore存在的意义。Memstore存在的意义是维持数据按照rowkey顺序排列,而不是做一个缓存

1.1.2 MemStore

  数据被写入WAL之后就会被加载到MemStore中去。MemStore的大小增加到超过一定阀值的时候就会被刷写到HDFS上,以HFile的形式被持久化起来

  (1)由于HDFS上的文件不可修改,为了让数据顺序存储从而提高读取效率,HBase使用了LSM树结构来存储数据。数据会先在Memstore中整理成LSM树,最后再刷写到HFile上。不过不要想当然地认为读取也是先读取Memstore再读取磁盘哟!读取的时候是有专门的缓存叫BlockCache,这个BlockCache如果开启了,就是先读BlockCache,读不到才是读HFile+Memstore,

  (2)优化数据的存储。比如一个数据添加后就马上删除了,这样在刷写的时候就可以直接不把这个数据写到HDFS上

  每一次的刷写都会产生一个全新的HFile文件,由于HDFS的特性,所以这个文件不可修改

1.1.3 HFile

  

HBASE列族配置属性 hbase列族的作用_数据_06

  HFile是数据存储的实际载体,我们创建的所有表、列等数据都存储在HFile里面  

  我们可以看到HFile是由一个一个的块组成的。在HBase中一个块的大小默认为64KB,由列族上的BLOCKSIZE属性定义。
  Data:数据块。每个HFile有多个Data块。我们存储在HBase表中的数据就在这里。Data块其实是可选的,但是几乎很难看到不包含Data块的HFile。
  Meta:元数据块。Meta块是可选的,Meta块只有在文件关闭的时候才会写入。Meta块存储了该HFile文件的元数据信息,
  FileInfo:文件信息,其实也是一种数据存储块。FileInfo是HFile的必要组成部分,是必选的。它只有在文件关闭的时候写入,存储的是这个文件的信息,比如最后一个Key(LastKey),平均的Key长度(Avg Key Len)等。
  DataIndex:存储Data块索引信息的块文件。索引的信息其实也就是Data块的偏移值(offset)。DataIndex也是可选的,有Data块才有DataIndex。
  MetaIndex:存储Meta块索引信息的块文件。MetaIndex块也是可选的,有Meta块才有MetaIndex。
  Trailer:必选的,它存储了FileInfo、DataIndex、MetaIndex块的偏移值

   布隆过滤器:所有数据在Hbase中都以一个个HFile为单元来存储,一个HFile大小概为几百兆字节到几千兆字节,每一个检索请求到来的时候,扫描器都会从头到尾地扫描整个HFile。为了提高HFile的检索速度,HBase使用了块索引机制。原理就是在HFile中增加一个部分,单独存储该HFile中的所有行键,这样扫描器可以先通过检索块索引来查找行键,当找到行键后再去具体的位置获取该行的其他信息。

  布隆过滤器可以知道元素在集合中是否“不存在”或者“可能存在”,也就是说如果布隆过滤器认为该元素不存在,那么就是不存在。这样可以极大地加速检索的速度,因为当布隆过滤器认为要检索的数据不在该块索引中时,扫描器可以跳过那些绝对不需要扫描的块索引

  

HBASE列族配置属性 hbase列族的作用_数据库_07

  

 

1.2 WAL预写日志

预写日志(Write-ahead log,WAL)就是设计来解决宕机之后的操作恢复问题的。数据到达Region的时候是先写入WAL,然后再被加载到Memstore的。就算Region的机器宕掉了,由于WAL的数据是存储在HDFS上的,所以数据并不会丢失

延迟写入WAL:当你的改动,比如Put、Delete、Append来到Region的时候会先放在内存中,这些改动立刻就会被写入WAL。

Region会等到条件满足的时候才把操作写入WAL。这里提到的条件主要指的是时间间隔hbase.regionserver.optionallogflushinterval,这个时间间隔的意思是HBase间隔多久会把操作从内存写入WAL,默认值是1s

WAL滚动:WAL一定是一个环状的滚动日志结构,因为这种结构写入效果最高,而且可以保证空间不会持续变大。

WAL滚动的条件是:

  1、WAL的检查间隔由hbase.regionserver.logroll.period定义,默认值为1小时。检查的内容是把当前WAL中的操作跟实际持久化到HDFS(HFile中)上的操作比较,看哪些操作已经被持久化了,被持久化的操作就会被移动到.oldlogs文件夹内(这个文件夹也是在HDFS上的)。

  2、当WAL文件所在的块(Block)快要满了。

  3、当WAL所占的空间大于或者等于某个阀值:你如果是基于HDFS的,默认WAL所占块空间大于或等于95%块大小,这个WAL文件就会被归档到.oldlogs文件夹内

删除.oldlogs条件:  

  Master会负责定期地去清理.oldlogs文件夹,条件是“当这个WAL不需要作为用来恢复数据的备份”的时候。判断的条件是“没有任何引用指向这个WAL文件。

  1、TTL进程:该进程会保证WAL文件一直存活直到达到hbase.master.logcleaner.ttl定义的超时时间(默认10分钟)为止

       2、备份(replication)机制:如果你开启了HBase的备份机制,那么HBase要保证备份集群已经完全不需要这个WAL文件了,才会删除这个WAL文件。

 

二、增删改查

  1、当你新增一个单元格的时候,HBase在HDFS上新增一条数据。

  2、当你修改一个单元格的时候,HBase在HDFS又新增一条数据,只是版本号比之前那个大(或者你自己定义)。

  3、当你删除一个单元格的时候,HBase还是新增一条数据!只是这条数据没有value,类型为DELETE,这条数据叫墓碑标记(Tombstone)。

  真正的删除数据

  由于数据库在使用过程中积累了很多增删查改操作,数据的连续性和顺序性必然会被破坏。为了提升性能,HBase每间隔一段时间都会进行一次合并(Compaction),合并的对象为HFile文件。合并分为两种minor compaction和major compaction。

  在HBase进行major compaction的时候,它会把多个HFile合并成1个HFile,在这个过程中,一旦检测到有被打上墓碑标记的记录,在合并的过程中就忽略这条记录。这样在新产生的HFile中,就没有这条记录了,自然也就相当于被真正地删除了

 

三、总结归纳

  

HBASE列族配置属性 hbase列族的作用_数据结构与算法_08

1、一个RegionServer包含多个Region,划分规则是:一个表的一段键值在一个RegionServer上会产生一个Region。不过当你1行的数据量太大了(要非常大,否则默认都是不切分的),HBase也会把你的这个Region根据列族切分到不同的机器上去。

2、一个Region包含多个Store,划分规则是:一个列族分为一个Store,如果一个表只有一个列族,那么这个表在这个机器上的每一个Region里面都只有一个Store。

3、一个Store里面只有一个Memstore。

4、一个Store里面有多个HFile(StoreFile是HFile的抽象对象,所以如果说到StoreFile就等于HFile)。每次Memstore的刷写(flush)就产生一个新的HFile出来

 

写入和读出过程

1、写入

HBASE列族配置属性 hbase列族的作用_数据库_09

WAL:数据被发出之后第一时间被写入WAL。由于WAL是基于HDFS来实现的,所以也可以说现在单元格就已经被持久化了,但是WAL只是一个暂存的日志,它是不区分Store的。这些数据是不能被直接读取和使用。
Memstore:数据随后会立即被放入Memstore中进行整理。Memstore会负责按照LSM树的结构来存放数据。这个过程就像我们在打牌的时候,抓牌之后在手上对牌进行整理的过程。
HFile:最后,当Memstore太大了达到尺寸上的阀值,或者达到了刷写时间间隔阀值的时候,HBaes会被这个Memstore的内容刷写到HDFS系统上,称为一个存储在硬盘上的HFile文件。至此,我们可以称为数据真正地被持久化到硬盘上,就算宕机,断电,数据也不会丢失了

2、读出

读取顺序是先从BlockCache中找数据,找不到了再去Memstore和HFile中查询数据

为了要过滤墓碑标记的数据,HBase的Scan操作在取到所需要的所有行键对应的信息之后还会继续扫描下去,直到被扫描的数据大于给出的限定条件为止,这样它才能知道哪些数据应该被返回给用户,而哪些应该被舍弃。所以你增加过滤条件也无法减少Scan遍历的行数,只有缩小STARTROW和ENDROW之间的行键范围才可以明显地加快扫描的速度。在Scan扫描的时候store会创建StoreScanner实例。StoreScanner会把MemStore和HFile结合起来扫描。其中红色块部分都是属于指定row的数据,Scan要把所有符合条件的StoreScanner都扫描过一遍之后才会返回数据给用户

HBASE列族配置属性 hbase列族的作用_大数据_10

 

Region 定位

(1)客户端先通过ZooKeeper的/hbase/meta-region-server节点查询到哪台RegionServer上有hbase:meta表。

(2)客户端连接含有hbase:meta表的RegionServer。hbase:meta表存储了所有Region的行键范围信息,通过这个表就可以查询出你要存取的rowkey属于哪个Region的范围里面,以及这个Region又是属于哪个RegionServer。

(3)获取这些信息后,客户端就可以直连其中一台拥有你要存取的rowkey的RegionServer,并直接对其操作。

(4)客户端会把meta信息缓存起来,下次操作就不需要进行以上加载hbase:meta的步骤了

  habse:meta.:是一张元数据表,它存储了所有Region的简要信息。.META.表中的一行记录就是一个Region,该行记录了该Region的起始行、结束行和该Region的连接信息,这样客户端就可以通过这个来判断需要的数据在哪个Region上。

HBASE列族配置属性 hbase列族的作用_数据_11

 

四、rowkey设计

1、reversing

初步设计出的RowKey在数据分布上不均匀,但RowKey尾部的数据却呈现出了良好的随机性,此时,可以考虑将RowKey的信息翻转,或者直接将尾部的bytes提前到RowKey的前部

缺点:场景利于Get但不利于Scan,因为数据在原RowKey上的自然顺序已被打乱

2、salting

Salting的原理是在原RowKey的前面添加固定长度的随机bytes,随机bytes能保障数据在所有Regions间的负载均衡

缺点:既然是随机bytes,基于原RowKey查询时无法获知随机bytes信息是什么,也就需要去各个可能的Regions中去查看。可见, Salting对于读取是糟糕的

3、hashing

基于RowKey的完整或部分数据进行Hash,而后将Hashing后的值完整替换原RowKey或部分替换RowKey的前缀部分

缺点:与Reversing类似, Hashing也不利于Scan,因为打乱了原RowKey的自然顺序。

六、Hbase问题

1、Hbase多列簇

每个regionServer有多个region,每个region有多个store。每个store包含一个memstore和多个storeFile

在Hbase表中,每个列簇对应region中的一个store,region的大小达到阈值会分裂,因此如果表中有多个列簇的时候,可能会出现以下情况

(1) 一个region中有多个store:如果每个CF的数据分布不均匀,比如CF1有100万,CF2有1万,则region分裂会导致CF2在每个region中数据量太少,查询CF2时候会横跨多个region导致效率变低

(2) 如果每个CF均匀分部:CF1为50万,CF250万,CF350万,则region分裂时候,导致每个CF在region的数据量偏少,查询某个CF时会导致很快多个Region的概率增大

(3)多个CF:有多个CF代表有多个store,也就是有多个memestore,也就是会导致内存的开销增大,使得效率降低

(4)缓存刷新和压缩:region中的缓存刷新和压缩是基本操作,即一个CF出现缓存刷新或压缩操作,其他CF也会做同一样的操作,当列簇太多时就会导致IO频繁