标签:
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服务器,实现负载均衡
客户端检索数据,先在memstore找,找不到再找storefileHRegion是HBase中分布式存储和负载均衡的最小单元。最小单元就表示不同的HRegion可以分布在不同的 HRegion server上。
– HRegion由一个或者多个Store组成,每个store保存一个columns family。
– 每个Strore又由一个memStore和0至多个StoreFile组成。如图:StoreFile以HFile格式保存在HDFS上。
标签:
原文地址:http://blog.csdn.net/jxcypress/article/details/51221711