Hbase写路径

来源:互联网 发布:java是脚本语言 编辑:程序博客网 时间:2024/06/14 21:10

转自 http://ptsolmyr.com/index.php/2012/07/21/hbase-write-path/


 Apache HBase是基于Hadoop的数据库,底层依赖的是Hadoop DFS。尽管HDFS只支持追加写(append)操作,而且数据一旦被创建,就是不可变(immutable)的,但是HBase却能够支持随机访问,并且可以更新存储在HDFS上的数据。你可能会好奇了,那HBase是凭什么提供低延时的读写操作的?本文通过分析HBase是更新数据的过程来解释这一点。

这里提到的HBase write path指的是HBase是怎么完成写操作(put)和删除操作(delete)的。这个过程是有client发起的,请求被发送到Region Server,最后数据被写入HBase data file, 也就是HFile中,整个过程结束。这个过程的设计理念就包含了HBase的一个重要特性: 在Region Server失效的情况下避免数据的丢失。所以理解了写过程,也就能够更深入的了解HBase防止本地数据丢失的机制了。

每个HBase的table都是由一下三组server来维护和管理的:

  1. 一个有效的master server
  2. 一个或多个备用的master server
  3. 许多region servers

Region Server用来管理HBase table。因为HBase table可能会很大,所以它们会被写分为若干个partition,称作Region。每个Region Server都管理这一个或多个这些Region。因为只有Region Server存储这HBase table的数据,所以master server挂了,并不会导致数据丢失。

HBase数据的组织形式和sorted map很相似,其中key排序后被切分至不同的shard或者说regions。HBase client通过put和delete操作来更新table。当client发起一个更新时,请求立刻被发送到一个默认的region server上。如果把auto flush开关关闭,那么client可以在自己这边把更新请求cache住,直到调用了flush-commit或者cache buffer满了,再成批的发送给region server。Cache buffer的大小可以通过”hbase.client.write.buffer”参数来修改。

因为row key是排过序的,很容易根据key找到管理该行的region server。每个写请求都是针对特定的一行。每个row key又属于一个特定的region,这个region又被一个region server管理着。所以,根据put或者delete请求针对的key,HBase client可以把请求定位到一个正确的region server上。首先,它从ZooKeeper集群中找到维护-ROOT- region的region server,然后从ROOT region server上找到-META- region的region server,最后,从META region server找到真正管理row key的region server。这是一个三步跳转,消耗比较大,HBase会把region server的地址缓存起来,如果缓存的地址失效了,那么就要重定位region server并更新缓存。

当请求被正确的region server接收之后,修改的数据并不会立刻被写入HFile,因为HFile里的数据都是根据row key排序的。排序的好处就是读请求可以很快的找到随机访问的位置。由于HDFS的特性,data并不能被随机的插入HFile。所以HBase的策略是把修改写入一个新文件。但是如果每个写请求都写入一个新文件的话,会创建过多的小文件。这样不仅效率低,也不利于扩展。所以,写请求并不会立即被写入新的HFile。

所以,每个修改动作,都会被存在内存中,叫做memstore,在内存中做随机写是很高效的。memstore中的数据按HFile相同的方式排序。当memstore中积累了一定的数据,将整个排好序的数据集写入一个新的HFile。对于HDFS来说,执行一个大量的写任务,效率是比较高的。

Write Path

尽管向memstore中写数据比较高效,但是这也引入了风险。在memstore中存储的信息随时可能由于系统崩溃而丢失。为了消除这个风险,HBase在写入memstore中之前,先把每个更新操作都存在了一个write-ahead-log(WAL)中。这样的话,如果region server挂了,memstore中的信息可以通过WAL恢复。

注意,默认情况下,WAL是打开的,但是往硬盘上写WAL的过程的确会消耗一定的资源。所以WAL是可以关闭的,当然,关闭了WAL自然就有数据丢失的隐患。如果要关闭WAL,那么就需要自己实现灾难恢复机制,或者可以接受数据的丢失。

WAL里面的数据组织方式和HFile不同。WAL里面包含了修改操作的列表,每一个条目都代表一个单独的put或者delete操作。条目包括修改操作的信息和修改发生在哪个region。修改条目都是以时序追加写入WAL文件的,所以这个过程中没有随机访问的需求。

随着WAL文件的逐步变大,他们最终都会被关闭,并且创建一个新的WAL文件,并且接受新的写操作。这个被称作rolling。当一个WAL文件被roll之后,不会再有新的修改记录被追加到旧的WAL文件里。

默认情况下,当WAL文件的大小达到HDFS的block大小的95%的时候,这个WAL文件就被roll掉。这个比例可以通过”hbase.regionserver.logroll.multiplier”来配置。WAL也会根据时间段来roll,默认一个小时,即使WAL文件的大小没有超过限制,也会roll。

限制WAL文件的大小的意义在于,当数据丢失的时候,恢复的速度会比较快。这是非常重要的,因为当根据WAL文件恢复数据时,相应的region是不可用的。这就是说,再WAL中的写操作没有全部被恢复并写入HFile之前,相应的region无法提供服务。在恢复动作完成之后,就可以归档这些WAL文件,它们最终会被后台线程LogCleaner删除。注意,WAL文件是用作保护数据的。WAL文件只用于恢复那些丢失了的更新操作。

一个Region Server管理着许多个region,但是并不是一个region都有一个相应的WAL文件,而是Region Server上的一个有效的WAL文件被这个server上的所有的region共享。因为WAL文件会周期性的被roll掉,所以一个region server上会有很多个WAL文件。注意再某个具体时刻,每个region server上只会有一个有效的WAL文件。

假设HBase的根节点是”/hbase”,同一个region server的所有的WAL文件都会被存在同一个根目录下,这是一个目录的规则:

/hbase/.logs/<host>,<port>,<startcode>

比如:

/hbase/.logs/srv.example.com,60020,1254173957298

WAL文件的命名规则如下:

/hbase/.logs/<host>,<port>,<startcode>/<host>%2C<port>%2C<startcode>.<timestamp>

例如:

/hbase/.logs/srv.example.com,60020,1254173957298/srv.example.com%2C60020%2C1254173957298.1254173957495

WAL文件里的每个条目都有一个唯一的序列id。序列id会按照条目的顺序递增。当Log文件roll掉之后,下一个序列id和老的文件名会被存于内存中的一个map结构中,用来记录每个WAL文件的最大序列id,所以当memstore中的内容被flush到disk里之后,可以很方便的得出某个WAL文件是否可以被归档。

每当一个写操作被写入了WAL文件,这个操作的序列ID也就被记录为最新的序列ID。当memstore被flush到磁盘上之后,那么这个region的最新的序列ID也会被清除。如果写入磁盘的最新的序列ID和WAL文件中最大的序列ID相同,那么就能够得到结论:WAL文件中关于这个region的所有写操作都被写入磁盘了。如果一个WAL中所有region的所有写操作都被写入磁盘了,那么很显然也就没有split和复现的需求了,这个WAL文件也就可以被归档了。

WAL文件的rolling和memstore的flush是两个独立的行为,不一定要一起发生。但是,一般不要保存太多的WAL文件,这样一旦某个region server挂掉了,恢复的时间不会过长。因此,当一个WAL文件被roll了之后,HBase会检查当前的WAL文件是不是过多了,并且计算一下哪个region需要flush掉,以似的一些WAL文件可以被归档。

本文解释了HBase的write path,也就是HBase是如何创建和更新数据的。重点是:

  1. Client是怎么定位region server的,
  2. Memstore, 用来支持快速的随机写访问,
  3. WAL, write ahead log, 避免数据丢失

 


原创粉丝点击