1.4 你必须懂的基本概念
1.4.1 部署架构
在安装HBase之前,有几个概念你必须弄懂,否则你可能就不知道自己在装什么。
我们先从大到小介绍一下HBase的架构。从HBase的部署架构上来说,HBase有两种服务器:Master服务器和RegionServer服务器。
一般一个HBase集群有一个Master服务器和几个RegionServer服务器。Master服务器负责维护表结构信息,实际的数据都存储在RegionServer服务器上,如图1-4所示。
HBase有一点很特殊:客户端获取数据由客户端直连RegionServer的,所以你会发现Master挂掉之后你依然可以查询数据,但就是不能新建表了。
图1-4
RegionServer是直接负责存储数据的服务器。RegionServer保存的表数据直接存储在Hadoop的HDFS上,架构如图1-5所示。
图1-5
RegionServer非常依赖ZooKeeper服务,可以说没有ZooKeeper就没有HBase。ZooKeeper在HBase中扮演的角色类似一个管家。ZooKeeper管理了HBase所有RegionServer的信息,包括具体的数据段存放在哪个RegionServer上。
客户端每次与HBase连接,其实都是先与ZooKeeper通信,查询出哪个RegionServer需要连接,然后再连接RegionServer。因此,以上的架构又可以拓展成如图1-6所示的这样:
图1-6
这就是HBase的整体架构。接下来,我们从微观角度去看具体的表数据是以怎样的结构存储。
1.4.1.1 Region是什么
Region就是一段数据的集合。HBase中的表一般拥有一个到多个Region。Region有以下特性:
- Region不能跨服务器,一个RegionServer上有一个或者多个Region。
- 数据量小的时候,一个Region足以存储所有数据;但是,当数据量大的时候,HBase会拆分Region。
- 当HBase在进行负载均衡的时候,也有可能会从一台RegionServer上把Region移动到另一台RegionServer上。
- Region是基于HDFS的,它的所有数据存取操作都是调用了HDFS的客户端接口来实现的。
1.4.1.2 RegionServer是什么
RegionServer就是存放Region的容器,直观上说就是服务器上的一个服务。一般来说,一个服务器只会安装一个RegionServer服务,不过你实在想在一个服务器上装多个RegionServer服务也不是不可以。
当客户端从ZooKeeper获取RegionServer的地址后,它会直接从RegionServer获取数据。
1.4.1.3 Master是什么
可能你们会想当然地觉得Master是HBase的领导,所有的数据、所有的操作都会经过它。错!其实在HBase中Master的角色不像领导,更像是打杂的。我们之前说过,客户端从 ZooKeeper获取了RegionServer的地址后,会直接从RegionServer获取数据。其实不光是获取数据,包括插入、删除等所有的数据操作都是直接操作RegionServer,而不需要经过Master。
Master只负责各种协调工作(其实就是打杂),比如建表、删表、移动Region、合并等操作。它们的共性就是需要跨RegionServer,这些操作由哪个RegionServer来执行都不合适,所以HBase就将这些操作放到了Master上了。
这种结构的好处是大大降低了集群对Master的依赖。而Master节点一般只有一个到两个,一旦宕机,如果集群对Master的依赖度很大,那么就会产生单点故障问题。在HBase中,即使Master宕机了,集群依然可以正常地运行,依然可以存储和删除数据。
1.4.2 存储架构
最基本的存储单位是列(column),一个列或者多个列形成一行(row)。传统数据库是严格的行列对齐。比如这行有三个列a、b、c,下一行肯定也有三个列a、b、c。而在HBase中,这一行有三个列a、b、c,下一个行也许是有4个列a、e、f、g。在HBase中,行跟行的列可以完全不一样,这个行的数据跟另外一个行的数据也可以存储在不同的机器上,甚至同一行内的列也可以存储在完全不同的机器上!
每个行(row)都拥有唯一的行键(row key)来标定这个行的唯一性。每个列都有多个版本,多个版本的值存储在单元格(cell)中。
若干个列又可以被归类为一个列族。
综上所述,HBase的存储结构可以表示成如图1-7所示的结构。
图1-7
1.4.2.1 行键是什么
rowkey和MySQL、Oracle中的主键比起来简单多了。这个rowkey完全是由用户指定的一串不重复的字符串,规则随你定!不过,话虽如此,你定的rowkey可是会直接决定这个row的存储位置的。HBase中无法根据某个column来排序,系统永远是根据rowkey来排序的。因此,rowkey就是决定row存储顺序的唯一凭证。而这个排序也很简单:根据字典排序。
比如,以下三个rowkey:
- row-1
- row-2
- row-11
根据字典排序结果:
- row-1
- row-11
- row-2
如果你插入HBase的时候,不小心用了之前已经存在的rowkey呢?
那你就会把之前存在的那个row更新掉。
之前已经存在的值呢?
会被放到这个单元格的历史记录里面,并不会丢掉,只是你需要带上版本参数才可以找到这个值。
什么是单元格呢?
一个列上可以存储多个版本的单元格。单元格就是数据存储的最小单元。
1.4.2.2 列族
在HBase中,若干列可以组成列族(column family)。
建表的时候是不需要制定列的,因为列是可变的,它非常灵活,唯一需要确定的就是列族。这就是为什么说一个表有几个列族是一开始就定好的。此外,表的很多属性,比如过期时间、数据块缓存以及是否压缩等都是定义在列族上,而不是定义在表上或者列上。这一点做法跟以往的数据库有很大的区别。同一个表里的不同列族可以有完全不同的属性配置,但是同一个列族内的所有列都会有相同的属性,因为他们都在一个列族里面,而属性都是定义在列族上的。
一个没有列族的表是没有意义的,因为列必须依赖列族而存在。
在HBase中一个列的名称前面总是带着它所属的列族。列名称的规范是列族:列名,比如brother:age、brother:name、parent:age、parent:name。
列族存在的意义是:HBase会把相同列族的列尽量放在同一台机器上,所以说,如果想让某几个列被放到一起,你就给他们定义相同的列族。
提示
一个表要设置多少个列族比较合适?官方的建议是:越少越好,因为HBase并不希望大家指定太多的列族。为什么?因为没有必要,虽然HBase是分布式数据库,但是数据在同一台物理机上依然会加速数据的查询过程。所以请根据实际需要来指定列族,列族太多会极大程度地降低数据库性能;而且根据目前的HBase实现,列族定得太多,容易出BUG。
1.4.2.3 单元格
你以为行键:列族:列就能唯一地确定一个值了吗?错!虽然列已经是HBase的最基本单位了,但是,一个列上可以存储多个版本的值,多个版本的值被存储在多个单元格里面,多个版本之间用版本号(Version)来区分。所以,唯一确定一条结果的表达式应该是行键:列族:列:版本号(rowkey:column family:column:version)。
不过,版本号是可以省略的。如果你不写版本号,HBase默认获取最后一个版本的数据返 回给你。每个列或者单元格的值都被赋予一个时间戳。这个时间戳默认是由系统制定的,也可以由用户显示指定。
1.4.2.4 Region跟行的关系
之前提到了Region的概念,后来我们又提到了行的概念,那么他们之间的关系是什么呢?其实很简单,一个Region就是多个行的集合。在Region中行的排序按照行键(rowkey)字典排序。
1.4.3 跟关系型数据库的对比
看完了前面的描述,如果你对HBase的存储结构还是没有一个直观的理解,那我们用它来跟传统关系型数据库对比一下。
传统的关系型数据库的表结构如图1-8所示。
图1-8
其中每个行都是不可分割的,也就是说三个列必须在一起,而且要被存储在同一台机器上,甚至是同一个文件里面。
HBase的表结构如图1-9所示。
图1-9
HBase中的每一个行都是离散的。因为有列族的存在,所以一个行里面的不同列甚至被分 配到了不同的服务器上。行的概念被减弱到只有一个抽象的存在。在实体上,把多个列标定为一个行的关键词是rowkey,这也是行这个概念在HBase中的唯一体现。
在HBase中,每一个存储语句都必须精确地写出数据是要被存储到哪个单元格,而单元格是由表:列族:行:列来定义的。翻译过来就是你要精确地写出数据要被存储到哪个表的哪个列族的哪个行的哪个列。如果一行有10列,那存储一行的数据得写10行的语句;而在传统数据库中存储语句(insert语句)可以把整个行的数据一次性写在行语句里面。