HBase是运行在Hadoop集群上的一个数据库,与传统的数据库有严格的ACID(原子性、一致性、隔离性、持久性)要求不一样,HBase降低了这些要求从而获得更好的扩展性,它更适合存储一些非结构化和半结构化的数据。
Apache HBase is a database that runs on a Hadoop cluster. HBase is not a traditional RDBMS, as it relaxes the ACID (Atomicity, Consistency, Isolation, and Durability) properties of traditional RDBMS systems in order to achieve much greater scalability. Data stored in HBase also does not need to fit into a rigid schema like with an RDBMS, making it ideal for storing unstructured or semi-structured data.
传统数据库vs.HBase
首先要理解一个概念,什么是NoSql,可以去下面的地址先了解一下:http://www.runoob.com/mongodb/nosql.html。
HBase也是基于NoSql的思想,那么为什么需要他们呢?先来看看传统数据库:
Why do we need NoSQL/HBase? First, let’s look at the pros of relational databases before we discuss its limitations:
- Relational databases have provided a standard persistence model
- SQL has become a de-facto standard model of data manipulation (SQL)
- Relational databases manage concurrency for transactions
- Relational database have lots of tools
传统数据库已经存在很长时间了,那么为什么又要造出来一个NoSql或者HBase呢?那是因为数据量不够大,举个例子,如果在关系数据库中存了几百条记录,那么全表扫描查询的时间还ok,但一旦涉及到几千万条或者上亿的记录,那这个时间就不敢恭维了,随着数据量的增大,我们需要扩展数据库,其中一种方法当然是换个更好的服务器,但是缺点也很明显, 贵啊!而且随着服务器容量的增大可能又会有一些其他的限制。
Relational databases were the standard for years, so what changed? With more and more data came the need to scale. One way to scale is vertically with a bigger server, but this can get expensive, and there are limits as your size increases.
NoSql跟传统数据库的区别
另外一种扩展的方式是横向扩展机器,这个相比换更好的服务器方法不需要花太多的人民币,你只要再购买一些相同的机器就好了。这样原先在一台机器上存储的数据就要分布式地存储到每台机器上,而这些数据是根据行为单位进行划分的。比如10000行数据,1-2500行存储到A机器上,2500-5000行数据存储到B机器上,以此类推。但是你是不能对传统数据库干这种事的。不过要注意的是,因为使用了这样的存储方式,也失去了其他一些关系数据库的操作,因为关系数据库是运行在一个节点上,而不是运行在集群中,所以两者的操作必然会有其针对的目标。
An alternative to vertical scaling is to scale horizontally with a cluster of machines, which can use commodity hardware. This can be cheaper and more reliable. To horizontally partition or shard a RDBMS, data is distributed on the basis of rows, with some rows residing on a single machine and the other rows residing on other machines, However, it’s complicated to partition or shard a relational database, and it was not designed to do this automatically. In addition, you lose the querying, transactions, and consistency controls across shards. Relational databases were designed for a single node; they were not designed to be run on clusters.
传统数据模型的局限性
数据库的课程中我们说过关系数据库的规范化,它通过关系模式的拆分,可以解决数据库中的数据冗余问题,但同样随着关系模式的标准化程度越高,拆分的关系模式越多,也就意味着在查询的过程中需要连接很多个表,这当然会造成性能问题。而hbase没有这样的标准化过程,它将可能会同时访问的数据存储在一起,所以它避免了传统数据库在查询时的性能问题。两者存储的区别如下图所示:
Database normalization eliminates redundant data, which makes storage efficient. However, a normalized schema causes joins for queries, in order to bring the data back together again. While HBase does not support relationships and joins, data that is accessed together is stored together so it avoids the limitations associated with a relational model. See the difference in data storage models in the chart below:
前面提到,可能同时访问的数据会存储在一起,HBase正是基于这个理念进行扩展的。它使用了Key-Value的方式,将数据按照key进行分组,再通过这个key决定存储到哪个的节点上去。这样每个节点上都有数据,并且是原始数据的一部分。HBase实际上是谷歌的BigTable的一个实现。
HBase was designed to scale due to the fact that data that is accessed together is stored together. Grouping the data by key is central to running on a cluster. In horizontal partitioning or sharding, the key range is used for sharding, which distributes different data across multiple servers. Each server is the source for a subset of data. Distributed data is accessed together, which makes it faster for scaling. HBase is actually an implementation of the BigTable storage architecture, which is a distributed storage system developed by Google that’s used to manage structured data that is designed to scale to a very large size.
HBase的特点:分布式、可扩展、快速
Hbase是基于列族的!他的每一行数据都有一个key,整个数据库是按照key进行索引的,你可以用一个key来查询到数据库的某一行数据。这个列族到底是个什么东西?其实就是列的集合,比如地址(Adress)这个列族,他可以是省份(Province)、城市(City)、街道(Street)这些列的集合,这样,一行可能有若干个列族,其实就是将关系数据库中的有关联的字段结合在一起了,所以每一行可以看成是这些列族的结合。
HBase is referred to as a column family-oriented data store. It’s also row-oriented: each row is indexed by a key that you can use for lookup (for example, lookup a customer with the ID of 1234). Each column family groups like data (customer address, order) within rows. Think of a row as the join of all values in all column families.
HBase是一个分布式的数据库,通过key将数据进行分组,这些key是更新操作时的基本单位。
HBase is also considered a distributed database. Grouping the data by key is central to running on a cluster and sharding. The key acts as the atomic unit for updates. Sharding distributes different data across multiple servers, and each server is the source for a subset of data.
HBase数据模型
说了这么多抽象的东西,终于可以看看HBase的到底是怎么存储数据的了。之前说HBase每一行都有一个key,我们管他叫RowKey,它的作用有点像关系数据库中的主键,根据这个RowKey就能找到这一行的数据。每行是按照RowKey进行排序的,这个是HBase数据存储的一个基础模型,在HBase中是严格遵守的。
Data stored in HBase is located by its “rowkey.” This is like a primary key from a relational database. Records in HBase are stored in sorted order, according to rowkey. This is a fundamental tenet of HBase and is also a critical semantic used in HBase schema design.
既然是分布式存储,那么必然要考虑怎么讲一张完整的表切分成若干块分别存储。我们已经知道了表中的数据是按照key进行排序的,那么切分就按行的顺序往下切就好了,比如10个一组地切。这样我们就把原始数据切成了好多块,每块叫一个Region,每个Region被分配到一个节点上存储。
Tables are divided into sequences of rows, by key range, called regions. These regions are then assigned to the data nodes in the cluster called “RegionServers.” This scales read and write capacity by spreading regions across the cluster. This is done automatically and is how HBase was designed for horizontal sharding.
每个Region中是若干行的数据,而每行又是若干个列族的结合,每个列族存储时对应于一个存储文件(HFile)
The image below shows how column families are mapped to storage files. Column families are stored in separate files, which can be accessed separately.
列族和RowKey已经说过了,还剩下的就是具体存储的数据了,数据当然是存储在表的每个格子中。考虑在这个模型中,想要获得一个格子中的数据应该怎么定位?首先是RowKey定位到一行,然后通过column family定位到这行的某个列族,一个列族中又有若干列,所以在通过列名定位到这一列,看起来定位一个数据只需要这些就够了。实际上还需要一个timestamp。
timestamp是更新数据的时候引入的,HBase在更新数据时不会简单的覆盖原始数据,而是在保留原始数据的同时存储新数据,那么就需要引入一个版本一样的属性来区别两个数据,timestamp时间戳做为这个属性在合适不过。
这些定位的数据加上那个格子里存储的数据叫一个KeyValue结构。key就是rowkey+column family+column name+timestamp,用于定位数据,value就是存储的那个数据。
The data is stored in HBase table cells. The entire cell, with the added structural information, is called Key Value. The entire cell, the row key, column family name, column name, timestamp, and value are stored for every cell for which you have set a value. The key consists of the row key, column family name, column name, and timestamp.
Logically, cells are stored in a table format, but physically, rows are stored as linear sets of cells containing all the key value information inside them.
In the image below, the top left shows the logical layout of the data, while the lower right section shows the physical storage in files. Column families are stored in separate files. The entire cell, the row key, column family name, column name, timestamp, and value are stored for every cell for which you have set a value.
HBase在存储的过程中可能会遇到很多格子没有数据的情况,对于这种情况,HBase不会存储空数据,这种特性使得hbase在应对稀疏表的时候也不会浪费存储空间。对于表格中的每个数据,都对应着一个版本,默认会采用timestamp作为版本,当然这个版本也可以自定义。所以对于row+family+column定位的格子,里面可能有若干个版本的数据。
As mentioned before, the complete coordinates to a cell’s value are: Table:Row:Family:Column:Timestamp ➔ Value. HBase tables are sparsely populated. If data doesn’t exist at a column, it’s not stored. Table cells are versioned uninterpreted arrays of bytes. You can use the timestamp or set up your own versioning system. For every coordinate row : family : column, there can be multiple versions of the value.
有了版本的概念后,我们会发现,一个put操作可以看成一个insert和一个update两个,一方面并没有直接更改已有的数据,而是创建了一个新的数据,另一方面由于这个新数据的版本号比较新,因此同时也是更新的操作。
如果是删除一个数据,那么会给删除的数据加上一个墓碑的标记,有了这个墓碑的标记的数据在查询的时候就不会返回了。
在查询数据时,如果指定了相应的参数,会返回特定版本的数据,如果没有指定版本的参数的话,默认会返回最新版本的数据。
每个列族中的数据可以存储多少个版本是可以通过配置进行设置的,默认会保存三个版本的数据,如果超过了这个值,那么最老的版本就会被淘汰,始终维持最新的三个版本。
Versioning is built in. A put is both an insert (create) and an update, and each one gets its own version. Delete gets a tombstone marker. The tombstone marker prevents the data being returned in queries. Get requests return specific version(s) based on parameters. If you do not specify any parameters, the most recent version is returned. You can configure how many versions you want to keep and this is done per column family. The default is to keep up to three versions. When the max number of versions is exceeded, extra records will be eventually removed.
总结
引用网上一篇博客的内容作为总结(http://www.cnblogs.com/tgzhu/p/5857035.html):
Table: 与传统关系型数据库类似,HBase以表(Table)的方式组织数据,应用程序将数据存入HBase表中。
Row: HBase表中的行通过 RowKey 进行唯一标识,不论是数字还是字符串,最终都会转换成字段数据进行存储;HBase表中的行是按RowKey字典顺序排列。
Column Family: HBase表由行和列共同组织,同时引入列族的概念,它将一列或多列组织在一起,HBase的列必须属于某一个列族,在创建表时只需指定表名和至少一个列族。
Cell: 行和列的交叉点称为单元格,单元格的内容就是列的值,以二进制形式存储,同时它是版本化
Version: 每个cell的值可保存数据的多个版本(到底支持几个版本可在建表时指定),按时间顺序倒序排列,时间戳是64位的整数,可在写入数据时赋值,也可由RegionServer自动赋值。
注意:
- HBase没有数据类型,任何列值都被转换成字符串进行存储。
- 与关系型数据库在创建表时需明确包含的列及类型不同,HBase表的每一行可以有不同的列。
- 相同RowKey的插入操作被认为是同一行的操作。即相同RowKey的二次写入操作,第二次可被可为是对该行某些列的更新操作
- 列由列族和列名连接而成, 分隔符是冒号,如 d:Name (d: 列族名, Name: 列名)
以一个示例来说明关系型数据表和HBase表各自的解决方案(示例:博文及作者),关系型数据库表结构设计及数据如下图:
文章的作者是一个外键,指向作者表中的PK,下面是两个表的示例数据:
如果用HBase来设计的话,就会变成这个样子:
小结:
HBase不支持条件查询和Order by等查询,读取记录只能按Row key(及其range)或全表扫描
在表创建时只需声明表名和至少一个列族名,每个Column Family为一个存储单元。
在上例中设计了一个HBase表blog,该表有两个列族:article和author,但在实际应用中强烈建议使用单列族。
- Column不用创建表时定义即可以动态新增,同一Column Family的Columns会群聚在一个存储单元上,并依Column key排序,因此设计时应将具有相同I/O特性的Column设计在一个Column Family上以提高性能。注意:这个列是可以增加和删除的,这和我们的传统数据库很大的区别。所以他适合非结构化数据。
- HBase通过row和column确定一份数据,这份数据的值可能有多个版本,不同版本的值按照时间倒序排序,即最新的数据排在最前面,查询时默认返回最新版本。如上例中row key=1的author:nickname值有两个版本,分别为1317180070811对应的“一叶渡江”和1317180718830对应的“yedu”(对应到实际业务可以理解为在某时刻修改了nickname为yedu,但旧值仍然存在)。Timestamp默认为系统当前时间(精确到毫秒),也可以在写入数据时指定该值。
- 每个单元格值通过4个键唯一索引,tableName+RowKey+ColumnKey+Timestamp—>value, 例如上例中{tableName=’blog’,RowKey=’1’,ColumnName=’author:nickname’,Timestamp=’ 1317180718830’}索引到的唯一值是“yedu”。
- 存储类型
- TableName 是字符串
- RowKey 和 ColumnName 是二进制值(Java 类型 byte[])
- Timestamp 是一个 64 位整数(Java 类型 long)
- value 是一个字节数组(Java类型 byte[])