写数据

流程图

hbase读性能怎么样 hbase的读写流程图_java

  1. Client 先访问 zookeeper。
  2. 获取 hbase:meta 表位于哪个 Region Server。
  3. 访问对应的 Region Server。
  4. 获取 hbase:meta 表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。
  5. 将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
  6. 与目标 Region Server 进行通讯。
  7. 将数据顺序写入(追加)到 WAL。
  8. 将数据写入对应的 MemStore,数据会在 MemStore 进行排序。
  9. 向客户端发送 ack。
  10. 等达到 MemStore 的刷写时机后,将数据刷写到 HFile

Flush

刷写:当数据写入到内存中后,需要持久化到HDFS中,这个步骤叫做Flush
刷写机制:

  1. 配置单个memstore最大值
<!-- 单个region里memstore的缓存大小,超过那么整个HRegion就会flush,默认128M -->  
    <property>  
        <name>hbase.hregion.memstore.flush.size</name>  
        <value>134217728</value>  
        <description>  
            Memstore will be flushed to disk if size of the memstore  
            exceeds this number of bytes. Value is checked by a thread that runs  
            every hbase.server.thread.wakefrequency.  
        </description>  
    </property> 
    <!-- 当一个HRegion上的memstore的大小满足hbase.hregion.memstore.block.multiplier *   
        hbase.hregion.memstore.flush.size, 这个HRegion会执行flush操作并阻塞对该HRegion的写入 -->  
    <property>  
        <name>hbase.hregion.memstore.block.multiplier</name> 
        <value>4</value>  
        <description>  
            Block updates if memstore has hbase.hregion.memstore.block.multiplier  
            times hbase.hregion.memstore.flush.size bytes. Useful preventing  
            runaway memstore during spikes in update traffic. Without an  
            upper-bound, memstore fills such that when it flushes the  
            resultant flush files take a long time to compact or split, or  
            worse, we OOME.  
        </description>  
    </property>
  1. 配置所有memstore最大值
<!-- regionServer的全局memstore的大小,超过该大小会触发flush到磁盘的操作,默认是堆大小的40%,而且regionserver级别的   
        flush会阻塞客户端读写 -->  
    <property>  
        <name>hbase.regionserver.global.memstore.size</name>  
        <value></value>  
        <description>Maximum size of all memstores in a region server before  
            new  
            updates are blocked and flushes are forced. Defaults to 40% of heap (0.4).  
            Updates are blocked and flushes are forced until size of all  
            memstores  
            in a region server hits  
            hbase.regionserver.global.memstore.size.lower.limit.  
            The default value in this configuration has been intentionally left  
            emtpy in order to  
            honor the old hbase.regionserver.global.memstore.upperLimit property if  
            present.  
        </description>  
    </property>  
    <!--可以理解为一个安全的设置,有时候集群的“写负载”非常高,写入量一直超过flush的量,这时,我们就希望memstore不要超过一定的安全设置。   
        在这种情况下,写操作就要被阻塞一直到memstore恢复到一个“可管理”的大小, 这个大小就是默认值是堆大小 * 0.4 * 0.95,也就是当regionserver级别   
        的flush操作发送后,会阻塞客户端写,一直阻塞到整个regionserver级别的memstore的大小为 堆大小 * 0.4 *0.95为止 -->  
    <property>  
        <name>hbase.regionserver.global.memstore.size.lower.limit</name>  
        <value></value>  
        <description>Maximum size of all memstores in a region server before  
            flushes are forced.  
            Defaults to 95% of hbase.regionserver.global.memstore.size (0.95).  
            A 100% value for this value causes the minimum possible flushing to  
            occur when updates are  
            blocked due to memstore limiting.  
            The default value in this configuration has been intentionally left  
            emtpy in order to  
            honor the old hbase.regionserver.global.memstore.lowerLimit property if  
            present.  
        </description>  
    </property>
  1. 配置自动刷写的时间
<!-- 内存中的文件在自动刷新之前能够存活的最长时间,默认是1h -->  
    <property>  
        <name>hbase.regionserver.optionalcacheflushinterval</name>  
        <value>3600000</value>  
        <description>  
            Maximum amount of time an edit lives in memory before being automatically  
            flushed.  
            Default 1 hour. Set it to 0 to disable automatic flushing.  
        </description>  
    </property>
  1. WAL文件的最大值,默认为32(新版本已移除属性值配置,不可修改)。

读数据

hbase读性能怎么样 hbase的读写流程图_hbase_02

  1. Client 先访问 zookeeper。
  2. 获取 hbase:meta 表位于哪个 Region Server。
  3. 访问对应的 Region Server。
  4. 获取 hbase:meta 表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个 Region Server 中的哪个 Region 中。
  5. 将该 table 的 region 信息以及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
  6. 分别在 Block Cache(读缓存),MemStore 和 Store File(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。返回时间戳最大的值。
  7. 将从文件中查询到的数据块(Block,HFile 数据存储单元,默认大小为 64KB)缓存到
    Block Cache。
  8. 将合并后的最终结果返回给客户端。

HFile 合并

由于memstore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile。为了减少 HFile 的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction。

Compaction 分为两种,分别是 Minor Compaction 和 Major Compaction。

Minor Compaction会将临近的若干个较小的 HFile 合并成一个较大的 HFile,但不会清理过期和删除的数据。
Major Compaction 会将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且会清理掉过期和删除的数据。
配置

<!-- 一个region进行 major compaction合并的周期,在这个点的时候, 这个region下的所有hfile会进行合并,默认是7天,major   
        compaction非常耗资源,建议生产关闭(设置为0),在应用空闲时间手动触发 -->  
    <property>  
        <name>hbase.hregion.majorcompaction</name>  
        <value>604800000</value>  
        <description>The time (in miliseconds) between 'major' compactions of  
            all  
            HStoreFiles in a region. Default: Set to 7 days. Major compactions tend to  
            happen exactly when you need them least so enable them such that they  
            run at  
            off-peak for your deploy; or, since this setting is on a periodicity that is  
            unlikely to match your loading, run the compactions via an external  
            invocation out of a cron job or some such.  
        </description>  
    </property>  
    <!-- 一个store里面允许存的hfile的个数,超过这个个数会被写到新的一个hfile里面 也即是每个region的每个列族对应的memstore在fulsh为hfile的时候,默认情况下当达到3个hfile的时候就会   
        对这些文件进行合并重写为一个新文件,设置个数越大可以减少触发合并的时间,但是每次合并的时间就会越长 -->  
    <property>  
        <name>hbase.hstore.compactionThreshold</name>  
        <value>3</value>  
        <description>  
            If more than this number of HStoreFiles in any one HStore  
            (one HStoreFile is written per flush of memstore) then a compaction  
            is run to rewrite all HStoreFiles files as one. Larger numbers  
            put off compaction but when it runs, it takes longer to complete.  
        </description>  
    </property>

Region 拆分

默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进行拆分。刚拆分时,两个子 Region 都位于当前的 Region Server,但处于负载均衡的考虑,HMaster 有可能会将某个 Region 转移给其他的 Region Server。

拆分流程

  1. 将需要拆分的 Region下线,阻止所有对该 Region 的客户端请求,Master 会检测到 Region 的状态为 SPLITTING。
  2. 将一个 Region 拆分成两个子 Region,先在父 Region下建立两个引用文件,分别指向 Region 的首行和末行,这时两个引用文件并不会从父 Region 中复制数据。
  3. 之后在 HDFS 上建立两个子 Region 的目录,分别复制上一步建立的引用文件,每个子 Region 分别占父 Region 的一半数据。复制登录完成后删除两个引用文件。
  4. 完成子 Region 创建后,向 Meta 表发送新产生的 Region 的元数据信息。
  5. 将 Region 的拆分信息更新到 HMaster,并且每个 Region 进入可用状态。

拆分策略

hbase读性能怎么样 hbase的读写流程图_java_03

Region 合并

从 Region 的拆分过程中可以看到,随着表的增大,Region 的数量也越来越大。如果有很多 Region,它们中 MemStore 也过多,会频繁出现数据从内存被刷新到 HFile 的操作,从而会对用户请求产生较大的影响,可能阻塞该 Region 服务器上的更新操作。过多的 Region 会增加 ZooKeeper 的负担。

因此,当 Region 服务器中的 Region 数量到达阈值时,Region 服务器就会发起 Region 合并,其合并过程如下。

  1. 客户端发起 Region 合并处理,并发送 Region 合并请求给 Master。
  2. Master 在 Region 服务器上把 Region 移到一起,并发起一个 Region 合并操作的请求。
  3. Region 服务器将准备合并的 Region下线,然后进行合并。
  4. 从 Meta 表删除被合并的 Region 元数据,新的合并了的 Region 的元数据被更新写入 Meta 表中。
  5. 合并的 Region 被设置为上线状态并接受访问,同时更新 Region 信息到 Master。

Region 负载均衡

当 Region 分裂之后,Region 服务器之间的 Region 数量差距变大时,Master 便会执行负载均衡来调整部分 Region 的位置,使每个 Region 服务器的 Region 数量保持在合理范围之内,负载均衡会引起 Region 的重新定位,使涉及的 Region 不具备数据本地性。

Region 的负载均衡由 Master 来完成,Master 有一个内置的负载均衡器,在默认情况下,均衡器每 5 分钟运行一次,用户可以配置。负载均衡操作分为两步进行:首先生成负载均衡计划表, 然后按照计划表执行 Region 的分配。

hbase读性能怎么样 hbase的读写流程图_hbase读性能怎么样_04