大数据之HBase总结(下)

HBase的数据读写过程


前一篇提到zookeeper记录着HBase集群的一些元信息,而在上面这两张图中可以看出,不管是写数据的过程还是读数据的过程,都需要通过zookeeper来完成。此外,需要注意的是,HBase存储引擎是基于LSM树实现。

在HBase的官方文档中,有这样一段话:

The hbase:meta table (previously called .META.) keeps a list of all regions in the system, and the location of hbase:meta is stored in ZooKeeper.
The HBase client finds the RegionServers that are serving the particular row range of interest. It does this by querying the hbase:meta table. See hbase:meta for details. After locating the required region(s), the client contacts the RegionServer serving that region, rather than going through the master, and issues the read or write request. This information is cached in the client so that subsequent requests need not go through the lookup process. Should a region be reassigned either by the master load balancer or because a RegionServer has died, the client will requery the catalog tables to determine the new location of the user region. 详见:http://hbase.apache.org/book.html#arch.catalog。

也就是说,HBase中的.META.表记录着用户创建的表的region及其RegionServer地址、访问端口、线程开始时间等信息。客户端要找到特定行范围的RegionServers,就要通过查询hbase:meta表(也就是前面所说的.META.)来完成此操作。在找到所需的区域之后,客户端直接联系服务该区域的RegionServer,而不是通过主区域,并发出读取或写入请求。此信息缓存在客户端中,以便后续请求无需经过查找过程。如果区域由主负载均衡器重新分配,或者因为RegionServer已经死亡,则客户端将重新查询目录表以确定用户区域的新位置。

写数据的过程

再次说明一下,HBase中有两张特殊的Table,-ROOT-和.META.表。Zookeeper中记录了-ROOT-表的location,而-ROOT-表记录了.META.表的Region信息。所以,在客户端发起写请求后的过程为:

  1. 客户端首先会根据配置文件中zookeeper地址访问zookeeper得到-ROOT-表的信息
  2. 再访问.META.表找到meta表的数据,即写入数据对应的region信息,并找到对应的regionserver
  3. hregionServer将数据先写入hlog中,以备恢复数据时使用。
  4. hregionServer将数据后写入memstore中
  5. 当memstore写满时,根据LRU算法将数据Flush到硬盘,生成一个storeFile文件,然后再把内存和HLog中的数据删除
  6. 将硬盘中数据通过HFile来序列化,再将数据传输到HDFS进行存储,并对Hlog做一个标记
  7. 当多个StoreFile文件达到一定的大小后,会触发Compact合并操作,合并为一个StoreFile,这里同时进行版本的合并和数据删除
  8. 当Compact后,逐步形成越来越大的StoreFile后,会触发Split操作,把当前的StoreFile分成两个,这里相当于把一个大的region分割成两个region(即region的分裂),新分裂出的2个Region会被HMaster分配到相应的HRegionServer上,以达到负载均衡。

读数据的过程

读数据的过程前面两步和写数据一样,都是要通过zookeeper找到对应的regionserver,再对数据进行操作。但是,由于HBase存储引擎是基于LSM树实现的,所以它的读效率会比写效率慢很多,过程也复杂的多,下面只是基本过程。

  1. 客户端发起读请求,首先根据配置文件中zookeeper地址访问zookeeper得到-ROOT-表的信息
  2. 再访问.META.表找到meta表的数据,即根据namespace、表名和rowkey等信息在meta表的数据找到写入数据对应的region信息,并找到对应的regionserver
  3. 寻找相应的region,然后首先从内存的memstore中寻找,如果没有找到,再从硬盘的storefile中查找,随后将找到的结果返回。

命名空间NameSpace

在HBase中,namespace命名空间指对一组表的逻辑分组,类似RDBMS中的database,方便对表在业务上划分。Apache HBase从0.98.0, 0.95.2两个版本开始支持namespace级别的授权操作,HBase全局管理员可以创建、修改和回收namespace的授权。命名空间是类似于关系数据库系统中的数据库的表的逻辑分组。这种抽象为即将推出的多租户相关功能奠定了基础:

  • 配额管理(Quota Management) - 限制命名空间可以使用的资源量(即区域,表)。
  • 命名空间安全管理(Namespace Security Administration) - 为租户提供另一级别的安全管理。
  • 区域服务器组(Region server groups) - 可以将命名空间/表固定到RegionServers的子集上,从而保证粗略的隔离级别。

命名空间管理(Namespace management)

命名空间可以被创建、移除、修改。表和命名空间的隶属关系 在在创建表时决定,通过以下格式指定: :

Example:hbase shell中相关命令:
//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'}	

预定义的命名空间(Predefined namespaces)

有两个系统内置的预定义命名空间:
hbase:系统命名空间,用于包含hbase的内部表
default:所有未指定命名空间的表都自动进入该命名空间(默认的)

Example:指定命名空间和默认命名空间
//namespace=foo and table qualifier=bar
create 'foo:bar', 'fam'

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

HBase的rowkey的设计原则和热点问题

HBase是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这个三个维度可以对HBase中的数据进行快速定位。

rowkey的设计原则

  1. rowkey长度原则:
    rowkey可以是任意字符串,最大长度64kb,实际应用中一般为10-100bytes,以byte[]形式保存,一般设计成定长。建议越短越好,不要超过16个字节,原因如下:

    • 数据的持久化文件HFile中是按照KeyValue存储的,如果rowkey过长,比如超过100字节,1000w行数据,光rowkey就要占用100*1000w=10亿个字节,将近1G数据,这样会极大影响HFile的存储效率;
    • 数据缓存在内存的memstore时,由于内存的空间有限,如果rowkey字段过长,那么缓存的数据相对越少,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。
    • 目前操作系统都是64位系统,内存8字节对齐,控制在16个字节,8字节的整数倍利用了操作系统的最佳特性。
  2. rowkey散列原则:
    不要把直接把时间戳作为rowkey,建议将随机生成的散列字段作为rowkey的高位,低位放时间字段,这样将提高数据均衡分布在每个RegionServer,以实现负载均衡。如果没有散列字段,首字段直接是时间信息,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。

  3. rowkey唯一原则:
    HBase中rowkey可以唯一标识一行记录,必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的,因此,设计rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

热点问题

检索habse的记录首先要通过rowkey来定位数据行,一般为了提高速率,会把连续的数据放在一起,可能就造成大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)来操作这些数据,而其他regionserver负载却很小,这就是“热点”问题。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。

所以,为了避免热点问题,就需要把原来大量连续的rowkey对应的记录均衡地分散到不同的region里去。针对rowkey的避免热点问题的常用方法有:

1、加盐(随机前缀)

这里所说的加盐是指在rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。

2、哈希

哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据。

3、反转

反转固定长度或者数字格式的rowkey以得到散列的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。如以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题。

4、时间戳反转

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp追加到key的末尾,例如[key][reverse_timestamp],[key]的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。

你可能感兴趣的:(大数据)