【图解】详细讲解Hadoop中的一个简单数据库HBase

HBase是 Hadoop中的一个简单数据库。它与Google的Bigtable特别相似,但也存在许多的不同之处。

数据模型

HBase数据库使用了和 Bigtable非常相似的数据模型。用户在表格里存储许多数据行。每个数据行都包括一个可排序的关键字,和任意数目的列。表格是稀疏的,所以同一个表格 里的行可能有非常不同的列,只要用户喜欢这样做。

列 名是“<族 名>:<标签>”形式,其中<族名>和<标签>可以是任意字符串。一个表格的<族名>集合(又叫 “列族”集合)是固定的,除非你使用管理员权限来改变表格的列族。不过你可以在任何时候添加新的<标签>。HBase在磁盘上按照列族储存数 据,所以一个列族里的所有项应该有相同的读/写方式。

写操作是行锁定的,你不能一次锁 定多行。所有对行的写操作默认是原子的。

所有数据库更新操作都有时间戳。HBase对每个数据单元,只存储指定个数的最新版本。客户端可以查询“从某个时刻起的最新数据”,或者一次得到所有的数据版本。

概念模型

从概念上,一个表格是一些行 的集合,每行包含一个行关键字(和一个可选的时间戳),和一些可能有数据的列(稀疏)。下面的例子很好的说明了问题:

物理模型

在概念上表格是一个稀疏的行/列矩阵,但是在物理上,它们按照 列存储。这是我们的一个重要设计考虑。

上面“概念上的”表格在物理上 的存储方式如下所示:

请大家注意,在上面的图中,没 有存储空的单元格。所以查询时间戳为t8的“content:”将返回null,同样查询时间戳为t9,“anchor:”值为“my.look.ca” 的项也返回null。

不 过,如果没有指明时间戳,那么应该返回指定列的最新数据 值,并且最新的值在表格里也时最先找到的,因为它们是按照时间排序的。所以,查询“contents:”而不指明时间戳,将返回t6时刻的数据;查询 “anchor:”的“my.look.ca”而不指明时间戳,将返回t8时刻的数据。

例子

为了展示数据在磁盘上是怎么 存储的,考虑下面的例子:

程序先写了行“[0-9]”, 列“anchor:foo”;然后写了行“[0-9]”,列“anchor:bar”;最后又写了行“[0-9]”,列“anchor:foo”。当把 memcache刷到磁盘并紧缩存储后,对应的文件可能如下形式:

row=row0, column=anchor:bar, timestamp=1174184619081
row=row0, column=anchor:foo, timestamp=1174184620720
row=row0, column=anchor:foo, timestamp=1174184617161
row=row1, column=anchor:bar, timestamp=1174184619081
row=row1, column=anchor:foo, timestamp=1174184620721
row=row1, column=anchor:foo, timestamp=1174184617167
row=row2, column=anchor:bar, timestamp=1174184619081
row=row2, column=anchor:foo, timestamp=1174184620724
row=row2, column=anchor:foo, timestamp=1174184617167
row=row3, column=anchor:bar, timestamp=1174184619081
row=row3, column=anchor:foo, timestamp=1174184620724
row=row3, column=anchor:foo, timestamp=1174184617168
row=row4, column=anchor:bar, timestamp=1174184619081
row=row4, column=anchor:foo, timestamp=1174184620724
row=row4, column=anchor:foo, timestamp=1174184617168
row=row5, column=anchor:bar, timestamp=1174184619082
row=row5, column=anchor:foo, timestamp=1174184620725
row=row5, column=anchor:foo, timestamp=1174184617168
row=row6, column=anchor:bar, timestamp=1174184619082
row=row6, column=anchor:foo, timestamp=1174184620725
row=row6, column=anchor:foo, timestamp=1174184617168
row=row7, column=anchor:bar, timestamp=1174184619082
row=row7, column=anchor:foo, timestamp=1174184620725
row=row7, column=anchor:foo, timestamp=1174184617168
row=row8, column=anchor:bar, timestamp=1174184619082
row=row8, column=anchor:foo, timestamp=1174184620725
row=row8, column=anchor:foo, timestamp=1174184617169
row=row9, column=anchor:bar, timestamp=1174184619083
row=row9, column=anchor:foo, timestamp=1174184620725
row=row9, column=anchor:foo, timestamp=1174184617169

注意,列“anchor:foo”存储了2次(但是时间戳不同),而且新时间戳排在前面(于是最新的总是最先找到)。

HRegion (Tablet)服务器

对 用户来说,一个表格是是一些数据元组的集合,并按照行关键字 排序。物理上,表格分为多个HRegion(也就是子表,tablet)。一个子表用它所属的表格名字和“首/尾”关键字对来标识。一个首/尾关键字为和 的子表包含[,)范围内的行。整个表格由子表的集合构成,每个子表存储在适当的地方。

物理上所有数据存储在Hadoop的DFS上,由一些子表服务器来提供数据服务,通常一台计算机只运行一个子表服务器程 序。一个子表某一时刻只由一个子表服务器管理。

当 客户端要进行更新操作的时 候,先连接有关的子表服务器,然后向子表提交变更。提交的数据添加到子表的HMemcache和子表服务器的HLog。HMemcache在内存中存储最 近的更新,并作为cache服务。HLog是磁盘上的日志文件,记录所有的更新操作。客户端的commit()调用直到更新写入到HLog中后才返回。

提供服务时,子表先查HMemcache。如果没有,再查磁盘上的HStore。子表里的每个列族都对应一个 HStore,而一个HStore又包括多个磁盘上的HStoreFile文件。每个HStoreFile都有类似B树的结构,允许快速的查找。

我 们定期调用HRegion.flushcache(),把HMemcache的内容写到磁盘上HStore的文件 里,这样给每个HStore都增加了一个新的HStoreFile。然后清空HMemcache,再在HLog里加入一个特殊的标记,表示对 HMemcache进行了flush。

启动时,每个子表检查最后的 flushcache()调用之后是否还有写操作在HLog里未应用。如果没有,那么子表里的所有数据就是磁盘上HStore文件里的数据;如果有,那么 子表把HLog里的更新重新应用一遍,写到HMemcache里,然后调用flushcache()。最后子表会删除HLog并开始数据服务。

所 以,调用flushcache()越少,工作量就越少,而HMemcache就要占用越多的内存空间,启动时 HLog也需要越多的时间来恢复数据。如果调用flushcache()越频繁,HMemcache占用内存越少,HLog恢复数据时也越快,不过 flushcache()的消耗费也需要考虑。

flushcache()调用 会给每个HStore增加一个HStoreFile。从一个HStore里读取数据可能要访问它的所有HStoreFile。这是很耗时的,所以我们需要 定时把多个HStoreFile合并成为一个HStoreFile,通过调用HStore.compact()来实现。

Google的Bigtable论文对主要紧缩和次要紧缩描述有些模糊,我们只注意到2件事:

1.一次flushcache()把所有的更新从内存写到磁盘里。通过flushcache(),我们把启动时的日志 重建时间缩短到0。每次flushcache()都给每个HStore增加一个HStoreFile文件。

2.一次compact()把所有的HStoreFile变成一个。

和Bigtable不同的是,Hadoop的HBase可以把更新“提交”和“写入日志”的时间周期缩短为0(即“提 交”就一定写到了日志里)。这并不难实现,只要它确实需要。

我们可以调用 HRegion.closeAndMerge()把2个子表合并成一个。当前版本里2个子表都要处于“下线”状态来进行合并。

当 一个子表大到超过了某个指定值,子表服务器就需要调用HRegion.closeAndSplit(),把它分割成 2个新的子表。新子表上报给master,由master决定哪个子表服务器接管哪个子表。分割过程非常快,主要原因是新的子表只维护了到旧子表的 HStoreFile的引用,一个引用HStoreFile的前半部分,另一个引用后半部分。当引用建立好了,旧子表标记为“下线”并继续存留,直到新子 表的紧缩操作把对旧子表的引用全部清除掉时,旧子表才被删除。

总结:

1.客户端访问表格里的数据。

2.表格分成许多子表。

3.子表由子表服务器维护, 客户端连接子表服务器来访问某子表关键字范围内的行数据。

4.子表又包括:

A.HMemcache,存储最近更新的内存缓冲

B.HLog,存储最近更新的日志

C.HStore,一群高效 的磁盘文件。每个列族一个HStore。

HBase的 Master服务器

每个子表服务器都维持与唯一主 服务器的联系。主服务器告诉每个子表服务器应该装载哪些子表并进行服务。

主服务器维护子表服务器在任 何时刻的活跃标记。如果主服务器和子表服务器间的连接超时了,那么:

A. 子表服务器“杀死”自己,并以一个空白状态重启。

B. 主服务器假定子表服务器已经“死”了,并把它的子表分配给其他子表服务器。

注 意到这和Google的 Bigtable不同,他们的子表服务器即使和主服务器的连接断掉了,还可以继续服务。我们必须把子表服务器和主服务器“绑”在一起,因为我们没有 Bigtable那样的额外锁管理系统。在Bigtable里,主服务器负责分配子表,锁管理器(Chubby)保证子表服务器原子的访问子表。 HBase只使用了一个核心来管理所有子表服务器:主服务器。

Bigtable这样做并没 有什么问题。它们都依赖于一个核心的网络结构(HMaster或Chubby),只要核心还在运行,整个系统就能运行。也许Chubby还有些特殊的优 点,不过这超过了HBase现在的目标范围。

当子表服务器向一个新的主服 务器“报到”时,主服务器让每个子表服务器装载0个或几个子表。当子表服务器死掉了,主服务器把这些子表标记为“未分配”,然后尝试给别的子表服务器。

每个子表都用它所属的表格名字和关键字范围来标识。既然关键字范围是连续的,而且最开始和最后的关键字都是NULL, 这样关键字范围只用首关键字来标识就够了。

不 过情况并没这么简单。因为 有merge()和split(),我们可能(暂时)会有2个完全不同的子表是同一个名字。如果系统在这个不幸的时刻挂掉了,2个子表可能同时存在于磁盘 上,那么判定哪个子表“正确”的仲裁者就是元数据信息。为了区分同一个子表的不同版本,我们还给子表名字加上了唯一的region Id。

这 样,我们的子表标识符最终的形式就是:表名+首关键字+region Id。下面是一个例子,表名字是hbaserepository,首关键字是w-nk5YNZ8TBb2uWFIRJo7V==,region Id是6890601455914043877,于是它的唯一标识符就是:

hbaserepository, w-nk5YNZ8TBb2uWFIRJo7V==,6890601455914043877

元数据表

我们也可以使用这种标识符 作为不同子表的行标签。于是,子表的元数据就存储在另一个子表里。我们称这个映射子表标识符到物理子表服务器位置的表格为元数据表。

元数据表可能增长,并且可以分裂成多个子表。为了定位元数据表的各个部分,我们把所有元数据子表的元数据保存在根子表 (ROOT table)里。根子表总是一个子表。

在启动时,主服务器立即扫 描根子表(因为只有一个根子表,所以它的名字是硬编码的)。这样可能需要等待根子表分配到某个子表服务器上。

一旦根子表可用了,主服务器扫描它得到所有的元数据子表位置,然后主服务器扫描元数据表。同样,主服务器可能要等待所有 的元数据子表都被分配到子表服务器上。

最后,当主服务器扫描完了元 数据子表,它就知道了所有子表的位置,然后把这些子表分配到子表服务器上去。

主服务器在内存里维护当前 可用的子表服务器集合。没有必要在磁盘上保存这些信息,因为主服务器挂掉了,整个系统也就挂掉了。

Bigtable与此不同,它在Google的分布式锁服务器Chubby里储存“子表”到“子表服务器”的映射信息。 但我们把这些信息存储到元数据表里,因为Hadoop里没有等价Chubby的东西。

这样,元数据和根子表的每行“info:”列族包含3个成员:

1.Info:regioninfo包含一个序列化的HRegionInfo对象。

2.Info:server包含一个序列化的HServerAddress.toString()输出字符串。这个字 符串可以用于HServerAddress的构造函数。

3.Info:startcode 是一个序列化的long整数,由子表服务器启动的时候生成。子表服务器把这个整数发送给主服务器,主服务器判断元数据和根子表里的信息是否过时了。

所以,客户端只要知道了根子表的位置,就不用连接主服务器了。主服务器的负载相对很小:它处理超时的子表服务器,启动 时扫描根子表和元数据子表,和提供根子表的位置(还有各个子表服务器间的负载均衡)。

HBase 的客户端则相当复杂,并且经常需要结合根子表和元数据子表来满足用户扫描某个表格的需求。如果某个子表服务器 挂了,或者本来应该在它上面的子表不见了,客户端只能等待和重试。在启动的时候,或最近有子表服务器挂掉的时候,子表到子表服务器的映射信息很可能不正 确。

结论:

1.子表服务器提供对子表的访问,一个子表只由一个子表服务器管理。

2.子表服务器需要向主服务器“报到”。

3.如果主服务器挂了,整 个系统就挂了。

4.只有主服务器知道当前的子表服务器集合。

5.子表到子表服务器的映射存储在2种特殊的子表里,它们和其他子表一样被分配到子表服务器上。

6.根子表是特殊的,主服务器总是知道它的位置。

7.整合这些东西是客户端的任务。

时间: 2024-09-27 08:42:38

【图解】详细讲解Hadoop中的一个简单数据库HBase的相关文章

Android一个简单数据库应用

问题描述 Android一个简单数据库应用 我做了一个简单的Android数据库应用,就是一个在edittext中输入文字,点击按钮就把输入的文字保存到数据库中,但是db=dbHelper.getWritableDatabase(); 总是报错 package com.example.shujukushiyan; import android.app.Activity; import android.content.ContentValues; import android.content.Co

.NET ,winform窗体中实现一个 sql 数据库的内容更新到另一个sql数据库里面的低级问题

问题描述 请问怎么在.NET,winform窗体中实现一个sql数据库的内容更新到另一个sql数据库里面,比如数据库a里面增加一条信息,然后通过winform窗体的某个按钮触发事件,把这条信息同样添加到数据库b里面,两个数据库内容都一样的,本人很菜的, 解决方案 解决方案二:创建另一个数据库的链接,跟创建你当前数据库链接一样,然后用command通过sql更新解决方案三:a里面增加一条信息,那信息是不是这个程序增加的?如果是,代码里数据库连接字符串改下指向b库就行,如果不是,通过记录a库id主键

详细讲解JavaScript中的this绑定_javascript技巧

this 可以说是 javascript 中最耐人寻味的一个特性,就像高中英语里各种时态,比如被动时态,过去时,现在时,过去进行时一样,无论弄错过多少次,下一次依然可能弄错.本文启发于<你不知道的JavaScript上卷>,对 javasript 中的 this 进行一个总结. 学习 this 的第一步就是明白 this 既不是指向函数自身也不指向函数的作用域.this 实际上是在函数被调用时发生的绑定,它指向什么地方完全取决于函数在哪里被调用. 默认绑定 在 javascript 中 ,最常

详细讲解Java中的main()方法_java

前言 JAVA中的主函数是我们再熟悉不过的了,相信每个学习过JAVA语言的人都能够熟练地写出这个程序的入口函数,但对于主函数为什么这么写,其中的每个关键字分别是什么意思,可能就不是所有人都能轻松地答出来的了.我也是在学习中碰到了这个问题,通过在网上搜索资料,并加上自己的实践终于有了一点心得,不敢保留,写出来与大家分享. Java中的main()方法 java虚拟机通过main方法找到需要启动的运行程序,并且检查main函数所在类是否被java虚拟机装载.如果没有装载,那么就装载该类,并且装载所有

SharePoint中创建一个简单的Web Part 部件

标准的Web部件有时候可以非常强大,可以执行许多函数.本文主要讲解如何使用Visual Studio 创建一个简单的Web部件. 1. 打开VS,点击文件----新建项目. 2. 选择空白SharePoint项目.命名SmallvilleWebPartProject,点击确定.选择部署为场解决方案. 3. 右击项目添加新项目. 4. 选择Web部件. 5. 命名CustomerInformation,点击添加. 6. 右击新的Web部件项目,选择添加类,命名CustomerData,点击确定.

详细讲解PostgreSQL中的全文搜索的用法_数据库其它

开发Web应用时,你经常要加上搜索功能.甚至还不知能要搜什么,就在草图上画了一个放大镜. 搜索是项非常重要的功能,所以像elasticsearch和SOLR这样的基于lucene的工具变得很流行.它们都很棒.但使用这些大规模"杀伤性"的搜索武器前,你可能需要来点轻量级的,但又足够好的搜索工具. 所谓"足够好",我是指一个搜索引擎拥有下列的功能:     词根(Stemming)     排名/提升(Ranking / Boost)     支持多种语言     对拼

详细讲解PHP 中的批处理

如果 Web 应用程序中的一个特性需要超过 1 秒或 2 秒才能完成,那么应该怎么办?需要某种离线处理解决方案.学习几种对 PHP 应用程序中长时间运行的作业进行离线服务的方法.大型的连锁店有一个大问题.每天,在每家商店会发生数千次交易.公司执行官希望对这些数据进行挖掘.哪些产品卖得好?哪些不好?有机产品在哪里卖得好?冰淇淋的销售情况怎么样? 为了捕捉这些数据,组织必须将所有事务性数据装载进一个数据模型,以便更适合生成公司所需的报告类型.但是,这很花费时间,而且随着连锁规模的增长,处理一天的数据

Hadoop白皮书(2):分布式数据库HBase简介

HBase 是一个面向列的分布式数据库.HBase 不是一个关系型数据库,其设计目标是用来解决关系型数据库在处理海量数据时的理论和实现上的局限性.传统关系型数据库在上世纪七十年代为交易系统设计,以满足数据一致性 (ACID)为目标,并没有考虑数据规模扩大时的扩展性,以及单点系统失效时的可靠性.虽然经过多年的技术发展,产生了一些对关系性数据库的修补(并行数据库),然而受限于理论和实现上的约束,扩展性从来没有超过 40 个服务器节点.而 HBase 从一开始就是为 Terabyte 到Petabyt

iOS开发中实现一个简单的图片浏览器的实例讲解_IOS

一.程序实现要求 1.要求 2. 界面分析 (1) 需要读取或修改属性的控件需要设置属性 序号标签 图片 图片描述 左边按钮 右边按钮 (2) 需要监听响应事件的对象,需要添加监听方法 左边按钮 右边按钮 二.实现基本功能的程序 复制代码 代码如下: // //  YYViewController.m //  03-图片浏览器初步 // //  Created by apple on 14-5-21. //  Copyright (c) 2014年 itcase. All rights rese