HBase

Chapter 5. 数据模型

Table of Contents

5.1. 概念视图
5.2. 物理视图
5.3. 表
5.4. 行
5.5. 列族
5.6. Cells
5.7. 版本号

5.7.1. HBase的操作(包括版本号操作)
5.7.2. 现有的限制

简单来说,应用程序是以表的方式在HBase存储数据的。表是由行和列构成的,全部的列是从属于某一个列族的。

行和列的交叉点称之为cell,cell是版本号化的。

cell的内容是不可切割的字节数组。

表的行键也是一段字节数组,所以不论什么东西都能够保存进去,不论是字符串或者数字。HBase的表是按key排序的,排序方式之针对字节的。全部的表都必需要有主键-key.
5.1. 概念视图

以下是依据BigTable 论文稍加改动的样例。

有一个名为webtable的表,包括两个列族:contents和anchor.在这个样例里面,anchor有两个列 (anchor:cssnsi.com, anchor:my.look.ca),contents仅有一列(contents:html)
列名

一个列名是由它的列族前缀和修饰符(qualifier)连接而成。比如列contents:html是列族 contents加冒号(:)加 修饰符 html组成的。

Table 5.1. 表 webtable
Row Key Time Stamp ColumnFamily contents ColumnFamily anchor
“com.cnn.www” t9 anchor:cnnsi.com = “CNN”
“com.cnn.www” t8 anchor:my.look.ca = “CNN.com”
“com.cnn.www” t6 contents:html = “…”
“com.cnn.www” t5 contents:html = “…”
“com.cnn.www” t3 contents:html = “…”

5.2. 物理视图

虽然在概念视图里。表能够被看成是一个稀疏的行的集合。但在物理上,它的是区分列族 存储的。新的columns能够不经过声明直接添加一个列族.

Table 5.2. ColumnFamily anchor
Row Key Time Stamp Column Family anchor
“com.cnn.www” t9 anchor:cnnsi.com = “CNN”
“com.cnn.www” t8 anchor:my.look.ca = “CNN.com”

Table 5.3. ColumnFamily contents
Row Key Time Stamp ColumnFamily “contents:”
“com.cnn.www” t6 contents:html = “…”
“com.cnn.www” t5 contents:html = “…”
“com.cnn.www” t3 contents:html = “…”

值得注意的是在上面的概念视图中空白cell在物理上是不存储的,由于根本没有必要存储。

因此若一个请求为要获取t8时间的contents:html。他的结果就是空。类似的。若请求为获取t9时间的anchor:my.look.ca。结果也是空。可是。假设不指明时间,将会返回最新时间的行,每一个最新的都会返回。比如,假设请求为获取行键为”com.cnn.www”。没有指明时间戳的话,活动的结果是t6下的contents:html,t9下的anchor:cnnsi.com和t8下anchor:my.look.ca。

For more information about the internals of how HBase stores data, see Section 9.7, “Regions”.
5.3. 表

表是在schema声明的时候定义的。
5.4. 行

行键是不可切割的字节数组。

行是按字典排序由低到高存储在表中的。一个空的数组是用来标识表空间的起始或者结尾。
5.5. 列族

在HBase是列族一些列的集合。

一个列族全部列成员是有着同样的前缀。比方,列courses:history 和 courses:math都是 列族 courses的成员.冒号(:)是列族的分隔符。用来区分前缀和列名。

column 前缀必须是可打印的字符,剩下的部分(称为qualify),能够又随意字节数组组成。列族必须在表建立的时候声明。column就不需要了。随时能够新建。

在物理上,一个的列族成员在文件系统上都是存储在一起。

由于存储优化都是针对列族级别的,这就意味着。一个colimn family的全部成员的是用同样的方式訪问的。

5.6. Cells

A {row, column, version} 元组就是一个HBase中的一个 cell。Cell的内容是不可切割的字节数组。
5.7. 数据模型操作

四个基本的数据模型操作是 Get, Put, Scan, 和 Delete. 通过 HTable 实例进行操作.
5.7.1. Get

Get 返回特定行的属性。

Gets 通过 HTable.get 运行。
5.7.2. Put

Put 要么向表添加新行 (假设key是新的) 或更新行 (假设key已经存在)。 Puts 通过 HTable.put (writeBuffer) 或 HTable.batch (non-writeBuffer)运行。
5.7.3. Scans

Scan 同意多行特定属性迭代。

以下是一个在 HTable 表实例上的演示样例。 假设表有几行键值为 “row1”, “row2”, “row3”, 另一些行有键值 “abc1”, “abc2”, 和 “abc3”. 以下的演示样例展示startRow 和 stopRow 能够应用到一个Scan 实例。以返回”row”打头的行。

HTable htable = … // instantiate HTable

Scan scan = new Scan();
scan.addColumn(Bytes.toBytes(“cf”),Bytes.toBytes(“attr”));
scan.setStartRow( Bytes.toBytes(“row”)); // start key is inclusive
scan.setStopRow( Bytes.toBytes(“row” + (char)0)); // stop key is exclusive
ResultScanner rs = htable.getScanner(scan);
try {
for (Result r = rs.next(); r != null; r = rs.next()) {
// process result…
} finally {
rs.close(); // always close the ResultScanner!
}

5.7.4. Delete

Delete 从表中删除一行. 删除通过HTable.delete 运行。

HBase 没有改动数据的合适方法。所以通过创建名为墓碑(tombstones)的新标志进行处理。这些墓碑和死去的值。在主紧缩时清除。

參考 Section 5.8.1.5, “Delete” 获取删除列版本号的很多其它信息。參考Section 9.7.5.5, “Compaction” 获取很多其它有关紧缩的信息。
5.8. 版本号

一个 {row, column, version} 元组是HBase中的一个单元(cell).可是有可能会有非常多的单元的行和列是同样的,能够使用版本号来区分不同的单元.

rows和column key是用字节数组表示的,version则是用一个长整型表示。这个long的值使用 java.util.Date.getTime() 或者 System.currentTimeMillis()产生的。

这就意味着他的含义是“当前时间和1970-01-01 UTC的时间差,单位毫秒。

在HBase中,版本号是按倒序排列的,因此当读取这个文件的时候,最先找到的是近期的版本号。

有些人不是非常理解HBase单元(cell)的意思。

一个常见的问题是:

假设有多个包括版本号写操作同一时候发起,HBase会保存全部还是会保持最新的一个?[16]

能够发起包括版本号的写操作。可是他们的版本号顺序和操作顺序相反吗?[17]

以下我们介绍下在HBase中版本号是怎样工作的。[18].
5.8.1. HBase的操作(包括版本号操作)

在这一章我们来细致看看在HBase的各个主要操作中版本号起到了什么作用。
5.8.1.1. Get/Scan

Gets实在Scan的基础上实现的。

能够具体參见以下的讨论 Get 同样能够用 Scan来描写叙述.

默认情况下。假设你没有指定版本号。当你使用Get操作的时候,会返回近期版本号的Cell(该Cell可能是最新写入的,但不能保证)。默认的操作能够这样改动:

假设想要返回返回两个以上的把版本号,參见Get.setMaxVersions()

假设想要返回的版本号不仅仅是近期的,參见 Get.setTimeRange()

要向查询的最新版本号要小于或等于给定的这个值,这就意味着给定的'近期'的值能够是某一个时间点。能够使用0到你想要的时间来设置。还要把max versions设置为1.

5.8.1.2. 默认 Get 样例

以下的Get操作会仅仅获得最新的一个版本号。

    Get get = new Get(Bytes.toBytes("row1"));
    Result r = htable.get(get);
    byte[] b = r.getValue(Bytes.toBytes("cf"), Bytes.toBytes("attr"));  // returns current version of value          

5.8.1.3. 含有的版本号的Get样例

以下的Get操作会获得近期的3个版本号。

    Get get = new Get(Bytes.toBytes("row1"));
    get.setMaxVersions(3);  // will return last 3 versions of row
    Result r = htable.get(get);
    byte[] b = r.getValue(Bytes.toBytes("cf"), Bytes.toBytes("attr"));  // returns current version of value
    List<KeyValue> kv = r.getColumn(Bytes.toBytes("cf"), Bytes.toBytes("attr"));  // returns all versions of this column       

5.8.1.4. Put

一个Put操作会给一个cell,创建一个版本号,默认使用当前时间戳。当然你也能够自己设置时间戳。这就意味着你能够把时间设置在过去或者未来,或者随意使用一个Long值。

要想覆盖一个现有的值,就意味着你的row,column和版本号必须全然相等。
5.8.1.4.1. 不指明版本号的样例

以下的Put操作不指明版本号,所以HBase会用当前时间作为版本号。

      Put put = new Put(Bytes.toBytes(row));
      put.add(Bytes.toBytes("cf"), Bytes.toBytes("attr1"), Bytes.toBytes( data));
      htable.put(put);

5.8.1.4.2. 指明版本号的样例

以下的Put操作,指明了版本号。

      Put put = new Put( Bytes.toBytes(row ));
      long explicitTimeInMs = 555;  // just an example
      put.add(Bytes.toBytes("cf"), Bytes.toBytes("attr1"), explicitTimeInMs, Bytes.toBytes(data));
      htable.put(put);

5.8.1.5. Delete

有三种不同类型的内部删除标记 [19]:

Delete: 删除列的指定版本号.

Delete column: 删除列的全部版本号.

Delete family: 删除特定列族全部列

当删除一行,HBase将内部对每一个列族创建墓碑(非每一个单独列)。

删除操作的实现是创建一个墓碑标记。比如,我们想要删除一个版本号,或者默认是currentTimeMillis。就意味着“删除比这个版本号更早的全部版本号”.HBase不会去改那些数据,数据不会马上从文件里删除。

他使用删除标记来屏蔽掉这些值。[20]若你知道的版本号比数据中的版本号晚,就意味着这一行中的全部数据都会被删除。

參考 Section 9.7.5.4, “KeyValue” 获取内部 KeyValue 格式很多其它信息。
5.8.2. 现有的限制

关于版本号另一些bug(或者称之为未实现的功能),计划在下个版本号实现。
5.8.2.1. 删除标记误标新Put 的数据

删除标记操作可能会标记其后put的数据。

[21]记住,当写下一个墓碑标记后,仅仅有下一个主紧缩操作发起之后,墓碑才会清除。

假设你删除全部<= 时间T的数据。但之后,你又运行了一个Put操作,时间戳<= T。就算这个Put发生在删除操作之后,他的数据也打上了墓碑标记。这个Put并不会失败,但你做Get操作时,会注意到Put没有产生影响。仅仅有一个主紧缩运行后。一切才会恢复正常。

假设你的Put操作一直使用升序的版本号,这个问题不会有影响。可是即使你不关心时间,也可能出现该情况。仅仅需删除和插入迅速相互尾随,就有机会在同一毫秒中遇到。


5.8.2.2. 主紧缩改变查询的结果

“设想一下,你一个cell有三个版本号t1,t2和t3。你的maximun-version设置是2.当你请求获取全部版本号的时候。仅仅会返回两个,t2和t3。假设你将t2和t3删除,就会返回t1。可是假设在删除之前,发生了major compaction操作。那么什么值都不好返回了。[22]”

5.9. 排序

全部数据模型操作 HBase 返回排序的数据。

先是行。再是列族。然后是列修饰(column qualifier), 最后是时间戳(反向排序,所以最新的在前).
5.10. 列的元数据

对列族,没有内部的KeyValue之外的元数据保存。这样。HBase不仅在一行中支持非常多列。并且支持行之间不同的列。 由你自己负责跟踪列名。

唯一获取列族的完整列名的方法是处理全部行。HBase内部保存数据很多其它信息,请參考 Section 9.7.5.4, “KeyValue”.
5.11. 联合查询(Join)

HBase是否支持联合是一个网上常问问题。简单来说 : 不支持。至少不想传统RDBMS那样支持(如 SQL中带 equi-joins 或 outer-joins). 正如本章描写叙述的,读数据模型是 Get 和 Scan.

但并不表示等价联合不能在应用程序中支持。仅仅是必须自己做。 两种方法,要么指示要写到HBase的数据,要么查询表并在应用或MapReduce代码中做联合(如 RDBMS所展示,有几种步骤来实现。依赖于表的大小。如 nested loops vs. hash-joins). 哪个更好?依赖于你准备做什么,所以没有一个单一的回答适合全部方面。


个人笔记
这里写图片描写叙述
Client
• 包括訪问HBase的接口并维护cache来加快对HBase的訪问
Zookeeper
保证不论什么时候,集群中仅仅有一个master
存贮全部Region的寻址入口。
实时监控Region server的上线和下线信息。并实时通知Master
存储HBase的schema和table元数据
Master
为Region server分配region
负责Region server的负载均衡
发现失效的Region server并又一次分配其上的region
管理用户对table的增删改操作
RegionServer
• Region server维护region,处理对这些region的IO请求
• Region server负责切分在运行过程中变得过大的region
Region
HBase自己主动把表水平划分成多个区域(region),每一个region会保存一个表
里面某段连续的数据;每一个表一開始仅仅有一个region,随着数据不断插
入表,region不断增大,当增大到一个阀值的时候,region就会等分会
两个新的region(裂变);
当table中的行不断增多,就会有越来越多的region。这样一张完整的表
被保存在多个Regionserver 上。
Memstore 与 storefile
一个region由多个store组成,一个store相应一个CF(列族) store包括位于内存中的memstore和位于磁盘的storefile写操作先写入memstore,当memstore中的数据达到某个阈值,hregionserver会启动flashcache进程写入storefile,每次写入形成单独的一个storefile 当storefile文件的数量增长到一定阈值后,系统会进行合并(minor、major compaction),在合并过程中会进行版本号合并和删除工作(majar),形成更大的storefile当一个region全部storefile的大小和超过一定阈值后,会把当前的region切割为两个,并由hmaster分配到相应的regionserver服务器,实现负载均衡

client检索数据,先在memstore找,找不到再找storefileHRegion是HBase中分布式存储和负载均衡的最小单元。最小单元就表示不同的HRegion能够分布在不同的 HRegion server上。
– HRegion由一个或者多个Store组成,每一个store保存一个columns family。


– 每一个Strore又由一个memStore和0至多个StoreFile组成。如图:StoreFile以HFile格式保存在HDFS上。
这里写图片描写叙述
这里写图片描写叙述

posted @ 2018-01-31 16:09  llguanli  阅读(171)  评论(0编辑  收藏  举报