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即可

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宏观架构

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架构

一个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结构

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

1.1.1 Store 结构

  

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

  

  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中的所有行键,这样扫描器可以先通过检索块索引来查找行键,当找到行键后再去具体的位置获取该行的其他信息。

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

  

  

 

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中,就没有这条记录了,自然也就相当于被真正地删除了

三、总结归纳

  

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、写入

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都扫描过一遍之后才会返回数据给用户

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上。

四、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频繁

Hbase基础篇的更多相关文章

  1. 大数据存储利器 - Hbase 基础图解

    由于疫情原因在家办公,导致很长一段时间没有更新内容,这次终于带来一篇干货,是一篇关于 Hbase架构原理 的分享. Hbase 作为实时存储框架在大数据业务下承担着举足轻重的地位,可以说目前绝大多数大 ...

  2. flink基础篇

    Flink面试--核心概念和基础考察 1.简单介绍一下 Flink 2.Flink 相比传统的 Spark Streaming 有什么区别? 3.Flink 的组件栈有哪些?         面试知识 ...

  3. C#多线程之基础篇3

    在上一篇C#多线程之基础篇2中,我们主要讲述了确定线程的状态.线程优先级.前台线程和后台线程以及向线程传递参数的知识,在这一篇中我们将讲述如何使用C#的lock关键字锁定线程.使用Monitor锁定线 ...

  4. 一步步学习javascript基础篇(0):开篇索引

    索引: 一步步学习javascript基础篇(1):基本概念 一步步学习javascript基础篇(2):作用域和作用域链 一步步学习javascript基础篇(3):Object.Function等 ...

  5. 2000条你应知的WPF小姿势 基础篇<15-21>

    在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师,对C#和WPF有着极深的热情.最为出色的是他维护了两个博客:2,000Things You Should Know ...

  6. ABP框架实践基础篇之开发UI层

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 说明 其实最开始写的,就是这个ABP框架实践基础篇.在写这篇博客之前,又回头复习了一下ABP框架的理论,如果你还没学习,请查看AB ...

  7. C#多线程之基础篇2

    在上一篇C#多线程之基础篇1中,我们主要讲述了如何创建线程.中止线程.线程等待以及终止线程的相关知识,在本篇中我们继续讲述有关线程的一些知识. 五.确定线程的状态 在这一节中,我们将讲述如何查看一个线 ...

  8. C#多线程之基础篇1

    在多线程这一系列文章中,我们将讲述C#语言中多线程的相关知识,在多线程(基础篇)中我们将学习以下知识点: 创建线程 中止线程 线程等待 终止线程 确定线程的状态 线程优先级 前台线程和后台线程 向线程 ...

  9. iOS系列 基础篇 03 探究应用生命周期

    iOS系列 基础篇 03 探究应用生命周期 目录: 1. 非运行状态 - 应用启动场景 2. 点击Home键 - 应用退出场景 3. 挂起重新运行场景 4. 内存清除 - 应用终止场景 5. 结尾 本 ...

随机推荐

  1. C# 解决无法识别的属性 configProtectionProvider

    在使用.Net自身提供的加密本配置文件后再用System.Configuration.ConfigurationManager.AppSettings["key"]获取值时会出现“ ...

  2. C#基础视频教程1 背景知识和安装配置

    安装过程比较简单,用虚拟光驱工具加载ISO文件,然后打开EXE安装即可,主要使用VS2013(VS2015也出来了,但是用的还不算多)     建议设置为深色(比较容易看清)   建议显示行号,不要自 ...

  3. Cocos2d-x -- 如何让背景从上到下滚动

    1. 首先,声明一个2个大小的sprite数组 class GameScreen : public cocos2d::Layer { public: ... cocos2d::Sprite *back ...

  4. iOS 真机调试多台mac电脑共用一个证书

    大致流程:在你的开发电脑上导出一个 p12文件,以及在 apple开发者中心下载一个profile文件,把这两个文件导出给你的mac开发友就行了 打开 mac  钥匙工具-----找到你的证书---- ...

  5. es5 - array - join

    /** * join描述:将数组(或类数组对象)的所有元素连接到一个字符串中并返回此字符串. * join语法:arr .join([ separator]) * join参数:指定用于分隔数组的每对 ...

  6. UNIX网络编程读书笔记:recv和send函数

    这两个函数类似于标准的read和write函数,不过需要一个额外的参数. #include <sys/socket.h> ssize_t recv(int sockfd, void *bu ...

  7. (转)更新Java final常量后,请重新编译你的class

    程序中使用的各种常量用一个类来统一管理,类似: public class AppConst { public static final String STR_1 = "String1&quo ...

  8. mysql 流程函数 存储引擎 InnoDB简单特性

    建表及插入数据语句: mysql> create table salary(userid int,salary decimal(9,2)); Query OK, 0 rows affected ...

  9. ‘close’ was not declared in this scope

    ‘close’ was not declared in this scope ‘read’ was not declared in this scope ‘sysconf’ was not decla ...

  10. 一次安装win10 ubuntu16.0经过记录

    步骤摘要 三个U盘: 1.制作WIN8 PE启动盘,使用的软件为“U深度装机版”,可自行百度下载 2.制作WIN10系统安装盘,使用UltraISO,这里使用的win10镜像为: 链接: http:/ ...