部署架构
从Hasee的部署架构来说,Hbase有两种服务器:Master服务器和RegionServer服务器。
一般一个Hbase集群中一个Master服务器和几个RegionServer服务器。
Master服务器负责维护表结构信息;RegionServer服务器负责存储实际的数据,RegionServer保存的表数据直接存储在Hadoopde HDFS上
RegionServer完全依赖zookeeper服务,没有zookeeper就没有hbase。zookeeper在hbase中充当管家的角色。zookeeper管理了hbase所有RegionServer的信息,包括具体的数据段存放在哪个RegionServer上,因为读取数据所需要的元数据表hbase:meata的位置存储在zookeeper的/hbase/meta-region-server节点上。
hbase中自带了一个zookeeper,而且默认会启动自己的zookeeper,如果hbase用的是自己的zookeeper,那么在jps中看到的zookeeper名字是HquorumPeer,使用外部的zookeeper,名字叫QuorumPeerMain。
是否使用自带的zookeeper由conf/hbase-env.sh中定义的HBASE_MANAGES_ZK变量定义,默认为true。即开启自带的zookeeper。
Hbase有一点很特殊,客户端获取数据由客户端直连RegionServer,因此当Master挂掉之后,依然可以查询数据,但是不能新建表。
客户端每次与hbase连接,其实都是先与zookeeper通信,查询出哪个RegionServer需要连接,然后再连接RegionServer。
Region
Region是一段数据的集合,每个region都有起始rowkey和结束rowkey。当数据表大小超过一定的阀值就会“水平切分”。Region是集群负载均衡的基本单位。Hbase中的表一般拥有一到多个Region。
单个region内部结构如下如所示:
每个region内都包含多个store实例。一个store对应表中一个列族的数据,如果一个表有两个列族,那么在一个region里面就有两个store。
store内部有MemStore和HFile两部分组成。
每个store中有一个MemStore实例。数据在写入WAL之后就会被放入到MemStore。MemStore是内存的存储对象。
在Store中有多个HFile。当MemStore满了之后Hbase就会在HDFS上生成一个新的HFile,然后把MemStore中内容写到这个HFile中。HFile直接跟HDFS打交道,它是数据的存储实体。
Region有以下特性:
- Region不能跨服务器,一个RegionServer上有一个或者多个Region。
- 数据量小的时候,一个Region足以存储所有数据。但是当数据量大的时候,Hbase会拆分Region。
- 当hbase在进行负载均衡的时候,也有可能会从一台RegionServer上把Region移动到另一台RegionServer上。
- Region是基于HDFS的,它的所有数据存取操作都是调用HDFS的客户端接口来实现的。
RegionServer
RegionServer就是存放Region的容器,主要用于响应来自用户的IO请求。直观上说就是服务器上的一个服务。
一个RegionServer包含一个BlockCache、一个或多个WAL(默认只有1个,在HBase1.0可以开启MultiWAL功能,开始支持多个WAL。参见HBASE-5699)和多个Region。
一般来说,一个服务器只会安装一个RegionServer服务。
当客户端从zookeeper获取RegionServer的地址后,它会直接从RegionServer获取数据。
Region以及RegionServer的关系图如下
Master
在hbase中Master并不充当领导的角色,更像是打杂的。主要负责Hbase系统各种管理工作:
- 处理用户各种管理请求,包括建表、修改表、权限操作、切分表、合并和分隔Region以及Compaction等;
- 管理集群中所有的RegionServer,包括RegionServer中Region的负载均衡、RegionServer的宕机恢复以及Region的迁移等;
- 清理过期日志以及文件,Master会每个一段时间检查HDFS中WAL(Hlog)是否过期、HFile文件是否已经被删除,并在过期之后将其删除。
它们的共性就是需要跨RegionServer,这些操作由哪个RegionServer来执行都不合适,因此这些操作由Master进行。
读取、写入、删除等所有的数据操作都是直接操作RegionServer ,而不需要经过Master。
这种结构的好处就是大大降低了集群对Master的依赖。Master节点一般只有一到两个。在Hbase中即使Master宕机了,集群依然可以正常的运行,可以存储和删除数据。
存储架构
最基本的存储单位是列(column),一个列或者多个列形成一行(row)。
每个行都拥有唯一的行键(rowkey)来标定这个行的唯一性。每个列可以有多个版本,多个版本的值存储在单元格(cell)中,cell就是数据存储的最小单元。
传统关系型数据库是严格的列行对齐,即每行必须有相同的列。在Hbase中,行与行之间的列可以完全不同。一行的数据可以跟另外一行的数据存储在不同的机器上,甚至一行内的列也可以存储在完全不同的机器上。
关系型数据表中每个行都是不可分割的,也就是说列必须都在一起,而且要被存储在同一台机器上,甚至是同一个文件中。
hbase中的每一个行都是离散的。因为有列族的存在,所以一个行中不同列甚至可以被分配到不同的服务器上。行的概念被减弱到只有一个抽象的存在。在实体上,把多个列标定为一个行的关键字是rowkey,这也是行这个概念在hbase中的唯一体现。
rowkey
rowkey完全是由用户指定的一串不重复的字符串,但是rowkey会直接决定对应row存储的位置。
hbase中永远是根据rowkey来排序的,因此rowkey就是决定row存储顺序的唯一凭证。排序规格就是根据rowkey的字典序进行。
如果插入hbase时,使用相同的rowkey,那么把之前存在的row更新掉。之前存在的值会被放到cell的历史记录里面,需要带上版本参数才可以找到对应的值。
column family
在hbase中,若干列可以组成列族(column family)。
建表的时候是不需要指定column的,因为column是灵活可变的,唯一需要确定的就是column family。
此外,表的属性,比如过期时间、数据块缓冲以及是否压缩等都是定义在column family上,而不是定义在表上或者column上。因此同一个表的不同列族可以有完全不同的属性配置,但是同一个列族内的所有列都会有相同的属性。
列必须依赖列族而存在。在Hbase中一个列的名称前面总是带着它的所属列族。列名称的规范是列族:列名
列族存在的意义是:Hbase会把相同列族的列尽量放在同一台机器上。
官方建议一个表设置的列族越少越好,因为hbase虽然是分布式数据库,但是数据在同一台物理机上依然会加速数据的查询过程。列族太多会极大从程度地降低数据库性能。而且根据目前hbase实现,列族定义太多,容易出bug。
cell
虽然列是hbase中最基本的存储单位,但是一个列可以存储多个版本的值,多个版本的值就被存储在多个单元格(cell)中,多个版本之间用版本号(Version)来区分。
所以唯一确定一条结果的表达式应该是行键:列族:列:版本号(rowkey:column family:column:version)。不过版本号可以省略,如果省略,hbase默认获取最后一个版本的数据。
每个列或者单元格的值都被赋予一个Timestamp(时间戳/版本号),既可以称之为时间戳,也可以称之为版本号,因为它是用来标定同一个列中多个单元格的版本号的。系统会自动采用当前的时间戳来作为Timestamp,也可以由用户手动指定一个数字当作Timestamp
region和row的关系
一个region就是多个行的集合,在region中行的排序是按照rowkey字典排序
Namespace
表命名空间的作用是把多个属于相同业务的表分到一个组。
表命名空间不是强制,当想把多个表分到一个组去统一管理的时候才会用到表命名空间。一个表可以自由选择是否有命名空间,如果创建表的时候加上了命名空间后,这个表名字就变成了<Namespace>:<Table>
表命名空间目前开放出来的方法只有ser和unset两种,并且只能在shell下调用
alter_namespace 'ns1', {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}
alter_namespace 'ns1', {METHOD => 'unset', NAME=>'PROPERTY_NAME'}
表命名空间主要是用于对表分组,命令空间可以填补hbase无法在一个实例上分库的缺憾。通过命名空间可以像关系型数据库一样将表分组,对于不同的组进行不同的环境设定,比如配额管理、安全管理等。
hbase中有两个保留表命名空间是预先定义好的:
hbase:系统表空间,用于Hbase内部表
default:没有定义表空间的表都被自动分配到这个表空间下
《Hbase不睡觉书》笔记