hbase官方文档翻译——Data Model章(20-32节)

HBase Data Model

Table

一个Hbase Table由多个row组成。

Row

在hbase中一个row由一个row key和多个column组成。在存储row时,row会按照字母顺序进行排序。因此,良好的row key设计十分重要。设计的目标是让相关的row尽可能地相互靠近。以用网站域名作为row key示例。如果你使用域名作为row key,你应该将域名反转后再存储(org.apache.www, org.apache.mail, org.apache.jira)。这样,表中的所有Apache域都彼此靠近,而不是根据子域的第一个字母分布。

Column

在hbase中一个column由一个column family和一个column qualifier组成,它们通过分隔符:进行分隔。

Column Family 

出于性能原因,column family位于一组column和column的值上。每个column family都有一组存储属性。例如其值是否存储在缓存中,数据怎么压缩以及row key如何编码等等。表中的每个row都会有相同的column family,虽然row在给定的column family上可能不会存储任何内容。

Column Qualifier

column qualifier被添加到column family上,作为给定数据的索引(查找给定数据,要经过row key,column family,column qualifier)。假设给定一个名为content的column family,一个column qualifier可能是content:html,也可能是content:pdf,虽然column family在表创建时就必须明确和固定,但column qualifier是可变的,并且行之间差别很大。

cell

cell是row,column family和column qualifier的结合,它存储着一个value和一个timestamp。timestamp代表该value的版本。

Timestamp

一个timestamp与每个value一起写入,它是给定版本的value的标示符。默认情况下,timestamp是数据写入时regionserver上的时间。但你也可以在写入数据到cell时自行设置另外一个timestamp值。

Conceptual View (概念视图)

以下表webtable为例,它包含2个row,分别是com.cnn.www和com.example.www ;3个column family,分别是contents、anchor和people。对于row key为com.cnn.www的row,它包含了5个版本。而row key为com.example.www 的row只有一个版本。row key为com.cnn.www的row上,它的“anchor”column family上有2个column,分别是anchor:cnnsi.com和anchor:my.look.ca。

Table 6. Table  webtable
Row Key Time Stamp ColumnFamily contents ColumnFamily anchor ColumnFamily people

"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 = "…​"

   

"com.example.www"

t5

contents:html = "…​"

 

people:author = "John Doe"

在上表中为空的cell并不会占用空间,这就是hbase看起来稀疏(sparse)的原因。表格视图不是唯一说明hbase数据模型的方法,也可以使用下面的数据格式。

{
  "com.cnn.www": {
    contents: {
      t6: contents:html: "..."
      t5: contents:html: "..."
      t3: contents:html: "..."
    }
    anchor: {
      t9: anchor:cnnsi.com = "CNN"
      t8: anchor:my.look.ca = "CNN.com"
    }
    people: {}
  }
  "com.example.www": {
    contents: {
      t5: contents:html: "..."
    }
    anchor: {}
    people: {
      t5: people:author: "John Doe"
    }
  }
}

Physical View

物理上hbase table是按照column family列式存储的。一个新的column qualifier (column_family:column_qualifier) 在任何时候都可以添加到一个已经存在的column family上。

Table 7. 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 8. 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不会存储。因此,请求在time stamp t8上的“contents:html ”column不会返回任何值。同样的,在time stamp t9上上请求“anchor:my.look.ca”column不会返回值。但是,如果不提供timestamp,会返回该column上最近的值。假设有多个版本,最近的也是一个被找到的,因为timestamp是按照降序存储的。如果不使用timestamp请求“com.cnn.www”row的所有column的值将会是这样的:在timestamp t6上的contents:html 值,在timestamp t9上的 anchor:cnnsi.com值,在timestamp t8上的anchor:my.look.ca值

Namespace

一个namespace是table的逻辑分组。

Namespace management

一个namespace可以被创建,删除或者修改。可以使用下列语句在table创建时指定table所属的namespace。

:

Example 7. Examples
#Create a namespace
create_namespace 'my_ns'
#create my_table in my_ns namespace
create 'my_ns:my_table', 'fam'
#drop namespace
drop_namespace 'my_ns'
#alter namespace
alter_namespace 'my_ns', {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}

Predefined namespaces

在hbase中有2个已经预先定义好的特殊namespace:

hbase ——特殊namespace,用于包含hbase中内部table。

defalut —— 没有明确定义namespace的table,会被归纳到这个namespace。

Example 8. Examples
#namespace=foo and table qualifier=bar
create 'foo:bar', 'fam'

#namespace=default and table qualifier=bar
create 'bar', 'fam'

Table

table在定义schema时就提前声明。

Row

row key是未解释的字节。 row按字典顺序排列,最低顺序首先出现在表中。

Column Family

在hbase中,所有的column都必须组合进某个column family里。一个column family的所有column成员都必须有相同的前缀。例如column“courses:history”和“courses:math”都是“courses”column family的成员。冒号分隔符分隔了column family和column qualifier。column family prefix必须由可打印字符组成。column qualifier可由任意字符组成。column family需要在schema设计阶段就提前声明。column不需要在schema设计阶段就提前声明,可以在table启动时动态添加。

物理上,所有的column family成员在文件系统中是存储在一起的。因为tuning规范和storeage规范是在column family级别完成的,所以建议所有的column family成员具有相同的一般访问模式和大小特征。

Cell

{row,column,version}元组精确地指定了HBase中的cell。 cell内容是未解释的字节。

Data Model Operations

4个主要的data model operation是get、put、scan和delete。可以通过table实例使用这些操作。

Get

get返回特定row的属性。通过Table.get执行get操作。

Put

如果一个key是新的,put操作添加新的row到table中,如果key已经存在,则更新原有的row。通过Table.put执行put操作。

Scan

scan允许对指定属性的多个row进行迭代。

下面是在一个table实例上执行scan操作的示例。假设table填充了一批row,row key分别为”row1“,”row2“,”row3“,还有一批row,它们的row key是”abc1“,”abc2“,”abc3“。下面示例展示了使用scan实例返回以”row“开头的row。

public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...

Table table = ...      // instantiate a Table instance

Scan scan = new Scan();
scan.addColumn(CF, ATTR);
scan.setRowPrefixFilter(Bytes.toBytes("row"));
ResultScanner rs = table.getScanner(scan);
try {
  for (Result r = rs.next(); r != null; r = rs.next()) {
    // process result...
  }
} finally {
  rs.close();  // always close the ResultScanner!
}

Delete

Delete操作从table中删除一个row。通过table.delete执行delete操作。

hbase不会修改数据,因此通过创建名为tombstone的新标记来处理删除操作。这些tombstone,以及已经死去的value,会在主要的compaction操作中清除干净。

关于删除某个版本的column的信息,请查阅version.delete

Versions

一个{row,column,version}元祖精确定义了在hbase中的一个cell。即使row和column相同也可能有无限个cell,但在version这个维度上仅有一个。

虽然row和column key是用字节表示的,但version是用long类型表示的。通常这个long类型包含了时间实例,例如java.util.Date.getTime() 或者 System.currentTimeMillis()返回的时间实例,即:1970年1月1日UTC 午夜和当前时间之间的差值(以毫秒为单位)。

在hbase中version是按降序存储的,所以读取一个store file时,最近的value总是第一个被找到。

在cell version的语义上有很多混淆,特别的:

如果对一个cell的多次写入具有相同的version,则只有最后一次写入是可以读取的。
以非递增版本顺序编写cell是可以的。

Specifying the Number of Versions to Store

对于给定的一个column,确定它在存储时的最大version数,是column schema的一部分。可以在表创建时确定,也可以通过alter命令修改HColumnDescriptor.DEFAULT_VERSIONS。在hbase 0.96之前,默认保持的version是3,在hbase 0.96以及更新的版本已经改为默认为1。

Example 9. Modify the Maximum Number of Versions for a Column Family

This example uses HBase Shell to keep a maximum of 5 versions of all columns in column family f1. You could also use HColumnDescriptor.

hbase> alter ‘t1′, NAME => ‘f1′, VERSIONS => 5
Example 10. Modify the Minimum Number of Versions for a Column Family

You can also specify the minimum number of versions to store per column family. By default, this is set to 0, which means the feature is disabled. The following example sets the minimum number of versions on all columns in column family f1 to 2, via HBase Shell. You could also use HColumnDescriptor.

hbase> alter ‘t1′, NAME => ‘f1′, MIN_VERSIONS => 2

Versions and HBase Operations

在这个章节中,我们将会看到hbase核心操作在version维度上的行为。

Get/Scan

get操作基于scan实现,下面对get操作的讨论等同于scan。

默认情况下,当你没有明确一个version就执行get操作时,返回的是值最大的version的cell。这个默认行为可以通过以下方式修改。

要返回多个版本,请参阅Get.setMaxVersions()

要返回除最新版本以外的其他版本,请参阅Get.setTimeRange()

要检索小于或等于给定值的最新版本,从而在某个时间点给出记录的“最新”状态,只需使用从0到所需版本的范围,并将最大版本设置为1。

Default Get Example

下面的get操作会返回row的当前版本。

public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...
Get get = new Get(Bytes.toBytes("row1"));
Result r = table.get(get);
byte[] b = r.getValue(CF, ATTR);  // returns current version of value


Versioned  Get Example

下面的get操作会返回row的最近的3个版本。

public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...
Get get = new Get(Bytes.toBytes("row1"));
get.setMaxVersions(3);  // will return last 3 versions of row
Result r = table.get(get);
byte[] b = r.getValue(CF, ATTR);  // returns current version of value
List kv = r.getColumn(CF, ATTR); 


Put

put操作总是基于时间戳创建一个新版本的cell。默认情况下系统使用server的currentTimeMillis,但你可以自己明确该version(=long整型),在column级别上。这意味着您可以分配过去或未来的时间,或将长时间值用于非时间目的。

要覆盖已经存在的value,请将其放入你要重写的cell的同一个row,column,version中。

下列put操作隐式使用当前时间作为版本号添加数据。

public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...
Put put = new Put(Bytes.toBytes(row));
put.add(CF, ATTR, Bytes.toBytes( data));
table.put(put);

下面put操作有明确设置的version timestamp。

public static final byte[] CF = "cf".getBytes();
public static final byte[] ATTR = "attr".getBytes();
...
Put put = new Put( Bytes.toBytes(row));
long explicitTimeInMs = 555;  // just an example
put.add(CF, ATTR, explicitTimeInMs, Bytes.toBytes(data));
table.put(put);

注意:version timastamp由HBase内部使用,用于诸如生存时间计算之类的事情。 通常最好避免自己设置时间戳。 优先使用该行的单独时间戳属性,或者将时间戳记作为row key的一部分,或者同时使用两者。

Delete

hbase有三种不同的内部delete标记。查阅Scanning in HBase: Prefix Delete Marker.关于这三种类型的讨论:

Delete:删除一个column的一个特定版本。

Delete column:删除一个column的所有版本。

Delete family:删除一个特定column family的所有column。

当删除整行时,HBase将在内部为每个ColumnFamily(即不是每个单独的列)创建一个tombstone。

delete操作通过tombstone标记完成。例如,假设我们想要删除一行。 为此,您可以指定一个版本,否则默认使用currentTimeMillis。这意味着删除版本小于或等于此版本的所有单元格。 HBase从不修改数据,例如删除不会立即删除(或标记为已删除)存储文件中对应于删除条件的条目。相反,写一个所谓的tombstone,它会掩盖删除的值。 当HBase进行重大压缩时,tombstone将被处理以实际移除死亡值以及tombstone本身。 如果您在删除行时指定的版本大于行中任何值的版本,则可以考虑删除整行。


Sort Order

所有数据模型操作HBase以排序顺序返回数据。 首先按行,然后按ColumnFamily,然后是column qualifier,最后是timestamp(timestamp反向排序,因此最新的记录会首先返回)。


Column Metadata

ColumnFamily的内部KeyValue实例之外不存储列元数据。 因此,尽管HBase不仅可以支持每行大量的列,还可以支持行之间的异构列,但您有责任跟踪列名。

获得ColumnFamily存在的一组完整列的唯一方法是处理所有行。 有关HBase如何在内部存储数据的更多信息,请参阅keyvalue。


Joins

hbase是否支持join操作是dist-list上的一个常见问题,一个简单的答案是,它不支持。 如本章所述,HBase中读取的数据模型操作是Get和Scan。

但是,这并不意味着您的应用程序不支持等效的join功能,但您必须自己动手。 两个主要策略是在写入HBase时对数据进行非规格化,或者在您的应用程序或MapReduce代码中使用查找表并进行HBase表之间的连接(并且正如RDBMS'演示的那样,有几种策略取决于HBase的大小 表格,例如,嵌套循环与散列连接)。 那么最好的方法是什么? 这取决于你想要做什么,因此没有一个适用于每个用例的答案。


ACID

See ACID Semantics. Lars Hofhansl has also written a note on ACID in HBase.

完。

你可能感兴趣的:(hbase)