Apache HBase【从无到有从有到无】【AH6】Data Model

目录

Data Model

1.概念观点(Conceptual View)

2.物理视图(Physical View)

3.命名空间(Namespace)

3.1.命名空间管理

3.2.预定义的名称空间(Predefined namespaces)

4.Table

5.Row

6.列族(Column Family)

7.单元格(Cells)

8.数据模型操作

8.1.Get

8.2.Put

8.3.Scans

8.4.Delete

9.Versions

9.1.指定要存储的版本数

9.2.版本和HBase操作

9.2.1.Get/Scan

9.2.2.默认获取示例

9.2.3.版本化获取示例

9.2.4.Put

9.2.5.Delete

9.3.HBase-2.0.0中的可选新版本和删除行为

9.4.当前限制

9.4.1.Deletes mask Puts

9.4.2.Major compactions更改查询结果

10.排序(Sort Order)

11.列元数据(Column Metadata)

12.Joins

13.ACID


Data Model

在HBase中,数据存储在具有行和列的表中。 这是与关系数据库(RDBMS)的术语重叠,但这并不是一个有用的类比。 相反,将HBase表视为多维映射可能会有所帮助。

HBase数据模型术语

Table

一个HBase表由多行组成。

Row

HBase中的一行由行键和一列或多列与它们相关联的值组成。 行在存储时按行键按字母顺序排序。 因此,行键的设计非常重要。 目标是以相关行彼此靠近的方式存储数据。 常见的行密钥模式是网站域。 如果行键是域,则可能应该反向存储它们(org.apache.www,org.apache.mail,org.apache.jira)。 这样,所有Apache域在表中都彼此靠近,而不是根据子域的第一个字母散布开来。

Column

HBase中的列由列族和列限定符组成,它们由:(冒号)字符分隔。

Column Family

出于性能考虑,列族实际上将一组列及其值并置在一起。 每个列族都有一组存储属性,例如是否应将其值缓存在内存中,如何压缩其数据或对其行键进行编码等。 表中的每一行都具有相同的列族,尽管给定的行可能不会在给定的列族中存储任何内容。

Column Qualifier

将列限定符添加到列族,以提供给定数据段的索引。 给定列族content,列限定符可能是content:html,另一个可能是content:pdf。 尽管列族在创建表时是固定的,但列限定符是可变的,并且行之间的差异可能很大。

Cell

单元格是行,列族和列限定符的组合,并包含一个值和一个时间戳,代表该值的版本。

Timestamp

时间戳记写在每个值旁边,并且是值的给定版本的标识符。 默认情况下,时间戳表示写入数据时在RegionServer上的时间,但是在将数据放入单元格时可以指定其他时间戳值。

 

1.概念观点(Conceptual View)

您可以在Jim R. Wilson的博客文章《Understanding HBase and BigTable》中阅读有关HBase数据模型的非常容易理解的解释。 Amandeep Khurana的PDF《基本模式设计简介》中提供了另一个很好的解释。

可能有助于阅读不同的观点,以便对HBase模式设计有深入的了解。 链接的文章与本节中的信息内容相同。

以下示例是BigTable论文第2页上的形式的略微修改形式。 有一个名为webtable的表,其中包含两行(com.cnn.wwwcom.example.www)和三个列族,分别名为contentsanchorpeople。在这个例子中,对于第一行(com.cnn.www),anchor 包含两列(anchor:cssnsi.comanchor:my.look.ca)和内容包含一个柱(contents:html)。 本示例包含具有行键com.cnn.www的行的5个版本,以及具有行键com.example.www的行的一个版本。 contents:html列限定符包含给定网站的整个HTML。 锚定列族的限定词均包含链接到该行表示的站点的外部站点,以及在其链接的锚定中使用的文本。 people 列族代表与该站点关联的人员。

列名
按照约定,列名由其列族前缀和限定符组成。 例如,contents:html 列由列族contents和html限定符组成。 冒号(:)分隔列族和列族限定符。

表6.表webtable

Row KeyTime StampColumnFamily contentsColumnFamily anchorColumnFamily 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 = "<html>…​"

  

"com.cnn.www"

t5

contents:html = "<html>…​"

  

"com.cnn.www"

t3

contents:html = "<html>…​"

  

"com.example.www"

t5

contents:html = "<html>…​"

 

people:author = "John Doe"

该表中看起来为空的单元格在HBase中不占用空间,或者实际上不存在。 这就是使HBase“稀疏”的原因。 表格视图不是查看HBase中数据的唯一可能方法,甚至不是最准确的方法。 以下代表与多维地图相同的信息。 这仅是出于说明目的的模型,可能并非严格准确。

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

2.物理视图(Physical View)

尽管从概念上讲,表可以看作是行的稀疏集合,但它们实际上是按列族存储的。 可以随时将新的列限定符(column_family:column_qualifier)添加到现有的列族。

表7.的ColumnFamily anchor

Row KeyTime StampColumn Family anchor

"com.cnn.www"

t9

anchor:cnnsi.com = "CNN"

"com.cnn.www"

t8

anchor:my.look.ca = "CNN.com"

表8. ColumnFamily contents

Row KeyTime StampColumnFamily contents:

"com.cnn.www"

t6

contents:html = "<html>…​"

"com.cnn.www"

t5

contents:html = "<html>…​"

"com.cnn.www"

t3

contents:html = "<html>…​"

概念视图中显示的空单元格根本不存储。 因此,在时间戳记t8处对contents:html列的值的请求将不返回任何值。 同样,在时间戳t9处请求anchor:my.look.ca值的请求将不返回任何值。 但是,如果未提供时间戳,则将返回特定列的最新值。 给定多个版本,因为时间戳以降序存储,所以最新的也是找到的第一个版本。 因此,如果未指定时间戳,则对com.cnn.www行中所有列的值的请求将是:来自时间戳t6contents:html的值,来自时间戳t9anchor:cnnsi.com的值, 时间戳记t8中的anchor:my.look.ca

有关Apache HBase如何存储数据的内部信息的更多信息,请参见 regions.arch

3.命名空间(Namespace)

命名空间是表的逻辑分组,类似于关系数据库系统中的数据库。 这种抽象为即将到来的多租户相关功能奠定了基础:

  • 配额管理(HBASE-8410)-限制名称空间可消耗的资源(即区域,表)数量。
  • 命名空间安全管理(HBASE-9206)-为租户提供另一级别的安全管理。
  • 区域服务器组(HBASE-6721)-可以将名称空间/表固定到RegionServers的子集上,从而保证了粗略的隔离级别。

3.1.命名空间管理

可以创建,删除或更改名称空间。 命名空间成员资格是在表创建期间通过指定以下格式的标准表名来确定的:

<table namespace>:<table qualifier>

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'}

3.2.预定义的名称空间(Predefined namespaces)

有两个预定义的特殊名称空间:

  • hbase-系统名称空间,用于包含HBase内部表
  • 默认-没有显式指定名称空间的表将自动落入该名称空间

Example 8. Examples

#namespace=foo and table qualifier=bar
create 'foo:bar', 'fam'

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

4.Table

表在架构定义时预先声明

5.Row

行键是未解释的字节。 行按字典顺序排序,最低顺序在表中排在最前面。 空字节数组用于表示表名称空间的开始和结束。

6.列族(Column Family)

Apache HBase中的列被分组为列族。 列族的所有列成员都具有相同的前缀。 例如,列courses:historycourses:math都是courses列族的成员。 冒号(:)分隔列族和列族限定符。 列族前缀必须由可打印字符组成。 限定尾部(列族限定符)可以由任意字节组成。 列族必须在架构定义时预先声明,而不必在架构时定义列,但可以在表启动并运行时即时对其进行构想。

实际上,所有列族成员都一起存储在文件系统上。 由于调整和存储规范是在列族级别上完成的,因此建议所有列族成员具有相同的常规访问模式和大小特征。

7.单元格(Cells)

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

8.数据模型操作

四个主要的数据模型操作是Get, Put, Scan和Delete。 通过Table实例应用操作。

8.1.Get

Get 指定行的返回属性。 通过Table.get执行获取

8.2.Put

Put 可以将新行添加到表中(如果密钥是新的),也可以更新现有行(如果键已经存在)。Puts 通过Table.put(non-writeBuffer)或Table.batch(non-writeBuffer)执行。

8.3.Scans

Scan允许对指定属性的多行迭代。

以下是“扫描表”实例的示例。 假设一个表中填充了键为“ row1”,“ row2”,“ row3”的行,然后填充了另一组键为“ abc1”,“ abc2”和“ abc3”的行。 以下示例显示如何设置Scan实例以返回以“ 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!
}

请注意,通常,为扫描指定特定停止点的最简单方法是使用InclusiveStopFilter类。

8.4.Delete

Delete从表中删除一行。 删除是通过Table.delete执行的。

HBase不会就地修改数据,因此删除是通过创建新标记来处理的被称为tombstones。这些tombstones,连同死去的价值观,在主要的compaction上被清理。。

有关删除列版本的更多信息,请参见version.delete;有关压缩的更多信息,请参见compaction

9.Versions

{row, column, version} 元组精确地指定了HBase中的一个单元(cell )。 可能会有无数个单元格,其中行和列相同,但单元格地址仅在其版本维度上有所不同。

当行键和列键表示为字节时,使用长整数指定版本。 通常,此long包含一些时间实例,例如java.util.Date.getTime()或System.currentTimeMillis()返回的那些实例,即:当前时间与1970年1月1日午夜之间的差(以毫秒为单位)。

HBase版本维以降序存储,因此从存储文件中读取时,将首先找到最新值。

在HBase中,关于单元(cell )版本的语义存在很多混乱。 尤其是:

  • 如果对一个单元的多次写入具有相同版本,则只有最后一次写入是可获取的。
  • 可以按非递增版本顺序写入单元格。

下面,我们描述HBase中的版本维度当前如何工作。 有关HBase版本的讨论,请参见 HBASE-2406Bending time in HBase使您可以更好地阅读HBase中的版本或时间维度。 与此处提供的版本相比,它具有更多有关版本控制的详细信息。

在撰写本文时,HBase中不再存在本文中提到的在现有时间戳上覆盖值的限制。 本部分基本上是Bruno Dumon撰写的本文的提要。

9.1.指定要存储的版本数

给定列存储的最大版本数是列模式的一部分,并在创建表时指定,或通过HColumnDescriptor.DEFAULT_VERSIONS通过alter命令指定。 在HBase 0.96之前,保留的默认版本数是3,但在0.96和更高版本中已更改为1

例子9.修改一个列族的最大版本数

本示例使用HBase Shell保留列族f1中所有列的最多5个版本。 您也可以使用 HColumnDescriptor

hbase> alter ‘t1′, NAME => ‘f1′, VERSIONS => 5

 

示例10.修改列族的最小版本数

您还可以指定每个列系列要存储的最小版本数。 默认情况下,它设置为0,这意味着该功能被禁用。 以下示例通过HBase Shell将列族f1中所有列的最小版本数设置为2。 您也可以使用HColumnDescriptor

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

 

从HBase 0.98.2开始,可以通过在hbase-site.xml中设置hbase.column.max.version来为所有新创建的列保留的最大版本数指定全局默认值。 请参见hbase.column.max.version

9.2.版本和HBase操作

在这一节中,我们看一下版本尺寸为每个核心的HBase操作的行为。

9.2.1.Get/Scan

获取是在“Scans”之上实现的。 下面有关获取的讨论同样适用于扫描。

默认情况下,即,如果您未指定任何显式版本,则在执行get时,将返回其版本值最大的单元格(该单元格可能不是最新写入的单元格,请参阅下文)。 可以通过以下方式修改默认行为:

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

9.2.2.默认获取示例

以下“Get”将仅检索该行的当前版本

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

9.2.3.版本化获取示例

下面的Get将返回该行的最后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<Cell> cells = r.getColumnCells(CF, ATTR);  // returns all versions of this column

9.2.4.Put

进行放置操作总是会在某个时间戳记下创建单元(cell)的新版本。 默认情况下,系统使用服务器的currentTimeMillis,但是您可以在每个列的级别上自行指定版本(=长整数)。 这意味着您可以分配过去或将来的时间,或将long值用于非时间目的。

要覆盖现有值,请在与要覆盖的单元格完全相同的行,列和版本上进行放置。

隐式版本示例

HBase将使用当前时间对以下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具有明确设置的版本时间戳。

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);

注意:HBase在内部使用版本时间戳记来进行生存时间计算。 通常最好避免自己设置此时间戳。 首选使用行的单独时间戳属性,或将时间戳作为行键的一部分,或两者兼而有之。

9.2.5.Delete

有三种不同类型的内部删除标记。 有关他尝试添加另一个“Scanning in HBase: Prefix Delete Marker”的尝试,请参见Lars Hofhansl的博客。

  • 删除:列的特定版本。
  • 删除列:适用于列的所有版本。
  • 删除族:针对特定ColumnFamily的所有列

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

通过创建逻辑删除标记来删除工作。 例如,假设我们要删除一行。 为此,您可以指定一个版本,否则默认使用currentTimeMillis。 这意味着删除版本小于或等于此版本的所有单元格。 HBase永远不会在适当位置修改数据,例如,删除操作不会立即删除(或标记为已删除)存储文件中与删除条件相对应的条目。 而是写了一个所谓的墓碑,它将掩盖已删除的值。 当HBase进行重大压缩时,将处理逻辑删除以实际删除无效值以及逻辑删除本身。 如果删除行时指定的版本大于该行中任何值的版本,则可以考虑删除整个行。

有关删除和版本控制如何交互的信息性讨论,请参见线程在用户邮件列表上出现 Put w/timestamp → Deleteall → Put w/ timestamp fails
另请参阅 keyvalue,以获取有关内部键值格式的更多信息。

除非在列族中设置了KEEP_DELETED_CELLS选项,否则在下次商店的主要压缩期间将删除删除标记(请参阅保留已删除的单元格)。 要使删除保持可配置的时间,可以通过hbase-site.xml中的hbase.hstore.time.to.purge.deletes属性设置删除TTL。 如果未设置hbase.hstore.time.to.purge.deletes或将其设置为0,则在下一次主要压缩期间将清除所有删除标记,包括将来带有时间戳的标记。 否则,将保留将来带有时间戳的删除标记,直到在该标记的时间戳加上hbase.hstore.time.to.purge.deletes的值所表示的时间之后的主要压缩为止(以毫秒为单位)。

此行为表示对HBase 0.94中引入的意外更改的修复,并在HBASE-10118中进行了修复。 所做的更改已反向移植到HBase 0.94和更新的分支中。

9.3.HBase-2.0.0中的可选新版本和删除行为

在hbase-2.0.0中,操作员可以通过将列描述符属性NEW_VERSION_BEHAVIOR设置为true来指定备用版本并删除处理(要在列族描述符上设置属性,必须首先禁用表,然后更改列族描述符 ;有关在列族描述符上编辑属性的示例,请参见保留已删除的单元格

“新版本行为”消除了以下列出的限制,即如果在同一位置(即相同的行,列族,限定词和时间戳记)(无论先到达哪个位置),“删除总是”都会使“放置”蒙上阴影。 版本记帐也发生了变化,因为删除的版本被视为总版本数。 这样做是为了确保在进行大型压实时不会改变结果。 有关讨论,请参见HBASE-15968和链接的问题。

目前,使用此新配置运行需要花费很多; 我们在每次比较时都将Cell MVCC包括在内,因此可以消耗更多的CPU。 减速将取决于。 在测试中,我们发现性能下降了0%到25%。

如果要复制,建议您现在使用新的串行复制功能(请参阅HBASE-9465;串行复制功能未将其放入hbase-2.0.0中,但应在后续的hbase-2.x版本中提供) 突变到达的顺序是一个因素。

9.4.当前限制

hbase-2.0.0中解决了以下限制。 请参阅上面的HBase-2.0.0中的“可选新版本和删除行为”部分。

9.4.1.Deletes mask Puts

Deletes mask puts,甚至在输入删除后发生的放置。 请参阅HBASE-2256。 请记住,删除操作会写入一个tombstone,该tombstone只有在下一次重大压缩运行后才会消失。 假设您删除了所有内容⇐T。此后,您将使用时间戳记⇐T进行新的放置。此放置,即使它在删除后发生,也会被删除逻辑删除掩盖。 进行认沽不会失败,但是当您进行认沽时,您会注意到认沽没有任何效果。 在大型压实运行之后,它将重新开始工作。 如果您为新的看跌期权连续使用不断增加的版本,那么这些问题应该不会成为问题。 但是,即使您不在乎时间,它们也会发生:只需删除并立即放置就可以了,它们有可能在同一毫秒内发生。

9.4.2.Major compactions更改查询结果

…在t1,t2和t3处创建三个单元格版本,最大版本设置为2。因此,获取所有版本时,将仅返回t2和t3处的值。 但是,如果您在t2或t3删除该版本,则t1的版本将再次出现。 显然,一旦进行了重大压缩,这种行为就不再存在了......(请参阅Bending time in HBase中的垃圾回收)。

10.排序(Sort Order)

HBase的所有数据模型操作均按排序顺序返回数据。 首先是按行,然后是ColumnFamily,然后是列限定符,最后是时间戳(按相反顺序排序,因此首先返回最新记录)。

11.列元数据(Column Metadata)

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

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

12.Joins

HBase是否支持联接是dist-list上的一个常见问题,并且有一个简单的答案:它至少在RDBMS支持联接的方式上不支持(例如,在SQL中使用等联接或外部联接) )。 如本章所述,HBase中的读取数据模型操作是Get和Scan。

但是,这并不意味着您的应用程序不支持等效的联接功能,而是您必须自己做。 两种主要策略是在写入HBase时对数据进行非规范化,或者在应用程序或MapReduce代码中具有查找表并在HBase表之间进行联接(并且正如RDBMS所演示的那样,有多种策略可用于此操作,具体取决于 表,例如嵌套循环与哈希联接)。 那么哪种方法最好呢? 这取决于您要执行的操作,因此,没有一个适用于每个用例的答案。

13.ACID

请参阅ACID Semantics。 Lars Hofhansl还在HBase中撰写了有关ACID in HBase的说明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值