HBASE原理及使用

一、hbase架构简介

在这里插入图片描述

  • Zookeeper:作为分布式的协调。RegionServer也会把自己的信息写到ZooKeeper中。
  • HDFS是Hbase运行的底层文件系统。
  • RegionServer,理解为数据节点,存储数据的。
  • Master :RegionServer要实时的向Master报告信息。Master知道全局的RegionServer运行情况,可以控制RegionServer的故障转移和Region的切分。

架构的细分

在这里插入图片描述

  • HMaster是Master Server的实现,负责监控集群中的RegionServer实例,同时所有metadata改变的接口,在集群中,通常运行在NameNode上面。
    • HMasterInterface暴露的接口Table(createTable, modifyTable, removeTable, enable, disable),ColumnFamily (addColumn, modifyColumn, removeColumn),Region (move, assign, unassign)
    • Master运行的后台线程:LoadBalancer线程,控制region来平衡集群的负载。CatalogJanitor线程,周期性的检查hbase:meta表。
  • HRegionServer是RegionServer的实现,服务和管理Regions,集群中RegionServer运行在DataNode。
    • HRegionRegionInterface暴露接口Data (get, put, delete, next, etc.),Region (splitRegion, compactRegion, etc.)
    • RegionServer后台线程:CompactSplitThread,MajorCompactionChecker,MemStoreFlusher,LogRoller。
  • Regions,代表table,Region有多个Store(列簇),Store有一个Memstore和多个StoreFiles(HFiles),StoreFiles的底层是Block

HLog, 预写日志文件,也叫做WAL(write-ahead log)。

  • 通常情况,每个RegionServer只有一个WAL实例。
  • MultiWAL: 如果每个RegionServer只有一个WAL,由于HDFS必须是连续的,导致必须写WAL连续的,然后出现性能问题。MultiWAL可以让RegionServer同时写多个WAL并行的,通过HDFS底层的多管道,最终提升总的吞吐量,但是不会提升单个Region的吞吐量。
  • WAL的配置:
// 启用multiwal
<property>
  <name>hbase.wal.provider</name>
  <value>multiwal</value>
</property>

HFile 真实的数据存储文件。HFile的生成方式:

  • 起初,HFile中并没有任何Block,数据还存在于MemStore中。
  • Flush发生时,创建HFile Writer,第一个空的Data Block出现,初始化后的Data Block中为Header部分预留了空间,Header部分用来存放一个Data Block的元数据信息。
  • 而后,位于MemStore中的KeyValues被一个个append到位于内存中的第一个Data Block中:
    注:如果配置了Data Block Encoding,则会在Append KeyValue的时候进行同步编码,编码后的数据不再是单纯的KeyValue模式。Data Block Encoding是HBase为了降低KeyValue结构性膨胀而提供的内部编码机制。
    在这里插入图片描述

二、Hbase的读写数据流程

HBase用-ROOT-表记录.META.表的位置信息(即元数据信息),而.META.表记录了用户表Region的位置信息。

要搞清楚hbase的读写流程,首先来看hbase的几张系统表:
hbase:namespace 存储了hbase中的所有namespace的信息
在这里插入图片描述
hbase:meta 存储了hbase中所有的region的信息,包括rowkey范围,region所在的regionserver的地址
在这里插入图片描述
hbase:meta 在zookeeper中,进入zookeeper中查看
在这里插入图片描述

hbase的读流程:

  • 1、Client 先访问 zookeeper,找到 Meta 表,并获取 Meta 表元数据。。
  • 2、找到meta表所在的regionserver的地址。
  • 3、访问对应的regionserver,读meta表的信息。
  • 4、通过命令找到rowkey对应的region,得到region的名称。
  • 5、访问region所在的regionserver。
  • 6、Client到HRegion的中去查找数据,首先到MemStore中查找,查到直接返回;查不到就去BlockCache中查找,查到直接返回;再查不到就去StoreFile中读数据,把读到的数据存入BlockCache中再返回Client

一个 RegionServer 上有一个 BlockCache 和N个 Memstore它们的大小之和不能大于等于 heapsize * 0.8,否则 hbase 不能启动。默认 BlockCache 为 0.2,而 Memstore 为 0.4。对于注重读响应时间的系统,应该将 BlockCache 设大些,比如设置BlockCache =0.4,Memstore=0.39。这会加大缓存命中率。
在这里插入图片描述

hbase的写流程:

  • 1、Client通过Zookeeper调度获取表的元数据信息。
  • 2、Cilent通过rpc协议与RegionServer交互,通过-ROOT-表与.META.表找到对应的对应的Region。
  • 3、将数据写入HLog日志中,如出现意外可以通过HLog恢复信息。
  • 4、将数据写入Region的MemStore中,当MemStore达到阈值开始溢写,将其中的数据Flush成一个StoreFile。
  • 5、MemStore不断生成新的StoreFile,当StoreFile的数量到达阈值后会出发Compact合并操作,将多个StoreFile合并成一个StoreFile。
  • 6、StoreFile文件会不断增大,当达到阈值后会出发Split操作,把当前的Region且分为两个新的Region。**父Region会下线,**两个子Region会被HMaster分配到相应的RegionServer。

数据写到store以后是先缓存在memstore中,同一个region中存在多个列族则存在多个store,每个store都一个memstore,当其实memstore进行flush时,属于同一个region
的store中的memstore都会进行flush。

HBase的流程高性能:

  • 1、由读写数据的流程可以发现,Region中的内存分为两块:MemStore(负责写数据)、BlockCache(负责读数据),这是HBase的一大特点——读写分离,这也是HBase读写速度极快的原因之一。
  • 2、在HBase中,可以看出只有增添操作,所有的更新和删除都是在后续的Compact合并过程中进行的,这使得用户的写操作只有进入内存就可以立刻返回,实现了I/O的高性能。

Hbase的storeFile合并的两种方式minor 和 major

hbase中有两种类型的合并

  • minor compaction 小合并
  • major compaction 大合并

minor compaction 小合并

  • 多个StoreFile合并成一个更大的StoreFile
    这个过程是将多个小的、相近的StoreFile合并成一个更大的StoreFile,对于超过TTL(Time To Live)的数据、更新的数据和删除的数据仅仅做个了标记(即新增了一个带有标记的版本号),并没有进行物理删除,一次minor compaction的结果会得到更少、更大的StoreFile。这种合并的触发频率很高
  • minor compaction触发的条件:
    • 表示一次minor compaction中最多选取10个store file
    • 文件大小小于该值的store file (128MB)一定会加入到minor compaction的store file中
    • 默认值为LONG.MAX_VALUE,表示文件大小大于该值的store file 会被minor compaction排除
<!--表示至少需要三个满足条件的store file时,minor compaction才会启动-->
<property>
	<name>hbase.hstore.compactionThreshold</name>
	<value>3</value>
</property>

<!--表示一次minor compaction中最多选取10个store file-->
<property>
	<name>hbase.hstore.compaction.max</name>
	<value>10</value>
</property>

<!--默认值为128m,
表示文件大小小于该值的store file 一定会加入到minor compaction的store file中
-->
<property>
	<name>hbase.hstore.compaction.min.size</name>
	<value>134217728</value>
</property>

<!--默认值为LONG.MAX_VALUE,表示文件大小大于该值的store file 会被minor compaction排除-->
<property>
	<name>hbase.hstore.compaction.max.size</name>
	<value>9223372036854775807</value>
</property>  

major compaction 大合并

  • 将一个Store中的所有StoreFile合并成一个HFile
    这个过程会把已经删除的、TTL过期的数据,版本号超过设定版本号的数据(版本太多的老数据)。这种合并频率比较低,默认是7天执行一次,并且性能消耗很大,一般都是手动控制合并,防止自动合并出现在业务高峰期
  • major compactiom的触发条件:默认7天一次
<!--默认值为7天进行一次大合并,-->
<property>
	<name>hbase.hregion.majorcompaction</name>
	<value>604800000</value>
</property>
  • 手动触发:使用major_compact命令
##使用major_compact命令
major_compact tableName

三、Hbase的应用场景

在这里插入图片描述

  • 对象存储:我们知道不少的头条类、新闻类的的新闻、网页、图片存储在HBase之中,一些病毒公司的病毒库也是存储在HBase之中
  • 时序数据:HBase之上有OpenTSDB模块,可以满足时序类场景的需求
  • 推荐画像:特别是用户的画像,是一个比较大的稀疏矩阵,蚂蚁的风控就是构建在HBase之上
  • 时空数据:主要是轨迹、气象网格之类,滴滴打车的轨迹数据主要存在HBase之中,另外在技术所有大一点的数据量的车联网企业,数据都是存在HBase之中
  • CubeDB OLAP:Kylin一个cube分析工具,底层的数据就是存储在HBase之中,不少客户自己基于离线计算构建cube存储在hbase之中,满足在线报表查询的需求
  • 消息/订单:在电信领域、银行领域,不少的订单查询底层的存储,另外不少通信、消息同步的应用构建在HBase之上
  • Feeds流:典型的应用就是xx朋友圈类似的应用
  • NewSQL:之上有Phoenix的插件,可以满足二级索引、SQL的需求,对接传统数据需要SQL非事务的需求

四、Hbase的优化

1、zookeeper.session.timeout(默认时间是:3分钟(180000ms))

该值可视情况调大,防止假掉线引起的数据rebalance。

2、hbase.hregion.memstore.mslab.enabled

是否开启mslab方案,减少因内存碎片导致的Full GC,提高整体性能.
线上配置:true
默认配置:true

3、垃圾回收(GC)参数配置

在hbase做GC的时候,我们采用CMS方式做回收是没问题的,也是正确方式。但是这个CMS 这种标记回收默认是当老年代内存达到90%的时候,开始做回收;但是可以想象一下这样的场景:假如此时老年代内存达到了90%,老年代开始CMS的并发收集,此时年轻代还在不断的把对象晋升到老年代,那么这个时候就很可能出现,老年代的CMS还没完成标记,老年代的空间就满了,那么这个时候就会出现比较严重的full GC ,停掉所有正在运行的线程,然后以单线程的方式去做回收,这个时间是很漫长的;所以我们最好用:-XX:CMSInitiatingOccupancyFraction=N(默认90)这个参数去控制,不要把老年代的GC设置的这么高,调节到60%就可以

4、HBase内存管理

HBase上的Regionserver的内存主要分为两部分,一部分作为Memstore,主要用来写;一部分作为BlockCache,主要用于读。
写请求会先写入Memstore,Regionserver会给每个region提供一个Memstore,当Memstore满128M(hbase.hregion.memstore.flush.size)以后,会启动flush刷新到磁盘,当Memstore的总大小超过限制时(heapsizehbase.regionserver.global.memstore.upperLimit0.9),会强行启动flush进程,从最大的Memstore开始flush知道低于限制

读请求先到Memstore中查数据,查不到就到BlockCache中查,再查不到就会到磁盘上读,并把读的结果放入BlockCache。由于ClockCache采用的是LRU(最近最少使用)策略,因此BlockCache达到上限(heapsizehfile.block.cache.size0.85)后,会启动淘汰机制,淘汰掉最老的一批数据。
在注重读响应时间的应用场景下,可以将BlockCache设置大些,Memstore设置小些,以加大缓存命中率

5、启动SNAPPY压缩

HBase列存储,比较占用空间,所以一般需要采用压缩算法,(从mysql导入hbase数据时发现,原本在mysql中30G数据,在hbase中占用150G),其中snappy性能优异,而且CDH中,直接安装了snappy的库。

使用方式:
         1、创建时指定格式;
                    hbase> create 'test', { NAME => 'c', COMPRESSION => 'SNAPPY' }
         2、修改已经创建好的列簇的压缩格式
          disable 'test'
          alter 'test', NAME => 'c', COMPRESSION => 'snappy'
          enable 'test'
          major_compact 'test'   --修改之后,需要做一个major合并才能养压缩格式生效
          describe 'test'  --查看有没有生效
6、预创建Region

创建HBase时,就预先根据可能的RowKey划分出多个Region而不是默认的一个,从而可以将后续的读写操作负载均衡到不同的Region上,避免热点现象;
HBase表的预分区需哟啊紧密结合业务场景来选择分区的key值,每个region都有一个startKey和一个endKey来表示该region存储的rowKey范围;

7、避免Region热点

热点现象:某个小的时段内,对HBase的读写请求集中到极少数的Region上,导致这些Region所在的RegionServer处理请求量骤增,负载量明显偏大,而其他的RegionServer明显空闲;
出现的原因:主要是因为Hbase表设计时,rowKey设计不合理造成的;
解决办法:Rowkey的随机散列+创表预分区
RowKey设计原则
1、总的原则:避免热点现象,提高读写性能;
2、长度原则:最大长度64KB,开发通常10-16个字节,因为Hbase中每个单元格是以key-value进行存储的,因此每个value都会存储rowkey,所以rowkey越来越占空间;
3、散列原则:将时间上连续产生的rowkey散列化,以避免集中到极少数Region上
4、唯一原则:必须在设计上保证rowkey的唯一性
RowKey设计结合业务:
在满足rowkey设计原则的基础上,往往需要将经常用于查询的字段整合到rowkey上,以提高检索查询效率

8、手动进行major compact

实际应用中,可以考虑必要时手动进行major compact,将同一个row key的修改进行合并形成一个大的StoreFile。同时,可以将StoreFile设置大些,减少split的发生。
合并小的storeFile成大的storeFile。在hbase中,主要存在两种类型的compaction:minor compaction和major compaction。

设置major compaction禁止自动执行,将值设为0:

<property>
      <name>hbase.hregion.majorcompaction</name>
      <value>0</value>
</property>

五、Hbase为什么写快读慢

HBase采用LSM树存储结构
LSM树本质是在读写之间取得平衡,它首先在内存中构建一颗有序的小树,随着小树的逐渐增大,达到一定阈值时会flush到磁盘上。所以LSM树不像B+树一样是一棵完整的大树,一棵LSM树就是一个个B+树合起来
多次flush之后会形成多个数据存储文件,后台线程会按照配置自动将多个文件合并成一个,此时多颗小树就会被合并成一棵大树。
但是读取时,由于不知道数据在哪棵小树上,因此必须遍历所有小树(所以才说LSM牺牲了部分读的性能),每棵小树内部数据是有序的。查询是先查内存中的部分,再去查磁盘上的部分。

这也就解释了为什么要有WAL了:
因为数据是先存在内存中的,不会立即写到磁盘上,断点数据就丢失了,所以为了保护内存中的数据需要写在磁盘上记录LogFile。当内存中的数据flush到磁盘上时,就可以删除掉相应的LogFile了。

这也就解释了为什么需要compact了:
因为上面说了,读的时候是要遍历所有小树的,小树越多,读的性能就会越来越差,所以需要进行merge,将多颗小树变成一颗大树

LSM树和B+树相比,LSM树放弃了部分读性能,使用顺序写来大幅提高写性能,适合写多读少以及大规模数据读取;而B+树更加适合读多写少的场景
HBase本身设计(应当说是底层Hadoop的设计)就是更加偏向于写多读少的场景,所以使用LSM更加合适。

六、Hbase是cp还是ap架构

结论:hbase是CAP中的CP系统,即hbase是强一致性的。

  • hbase之所以是CP系统,实际和底层HDFS无关不大,它是CP系统,是因为对每一个region同时只有一台RegionServer为它服务,对一个region所有的操作请求,都由这一台region server来响应,自然是强一致性的。
  • 在这台RegionServer fail的时候,它管理的region会failover到其他Region Server时,需要根据WAL log来redo,这时候进行redo的region应该是unavailable(不可用)的,所以hbase降低了可用性,提高了一致性。
  • 设想一下,如果redo的region能够响应请求,那么可用性提高了,则必然会返回不一致的数据(因为redo可能还没完成),从这个角度来看hbase就是降低一致性来提高了可用性。

CAP 结构中的 cp(强一致性) 和 ap(高可用性)。

HBase上面regionserver宕机之后的处理
1.zookeeper上面没收到regionserver心跳,发现掉线
2.HLog进行拆分,分配到相应的region上面
3.region分发到相应的活着的regionserver上面
4.根据分给的region上面的HLog,持久化到新的HFile

七、Hbase 的scan

首先,对于scan来说,并不是所有的都会扫描全表。

scan的几种实现

scan是较为底层的也是用的最多的一种获取Hbase中数据的方式,在它的上面还封装了几种基于scan的获取方式的数据,例如snapshotScanMR,TableScanMR,scanMR。这几中方式都是基于scan,用MR的方式去获取数据,并且对于获取大批量的数据来说,效率都比scan好的多。

非全表scan的两种方式
  • 借助Rowkey:scan可以根据rowkey,cf,column,timestamp,filter来获取方式。在这里优先推荐根据rowkey的方式获取数据,即可以指定startrow和endrow,也可以指定具体的某个行键去获取数据。在源码中也指出了如果具体指定了rowkey,那么会只是扫描那一段的数据。众所周知,hbase底层存储数据是根据rowkey的字典顺序存储数据的,所以如果使用rowkey的方式去查找,scan就可以顺序查找,大大缩短了时间。
  • 第二种就是根据timestamp去查找,对于这个时间戳,特别注意的是指的是存入hbase中的时间在存入时,对应的cell中会记录下来。而不是客户端插入的数据的时间,这里肯定会有一定的使时间偏移,但相差不会很大。对于timestamp的方式来说,它也是只是去扫描对应范围或者对应某个时间戳去获取。

其他的方式,就是通过全表扫描去查询的。

在用scan查询的时候,底层有个next()方法,一次默认返回100条数据(或者不超过2m的数据),这不同于scan中的setBatch()方法,这里返回的是给hbase服务器的值而不是返回给客户端的值,怎么做是为了避免在全表扫描的时候
一次返回太多数据,导致OOM。

八、Hbase 的rowkey设计

访问hbase table中的行,只有三种方式:

  • 1 通过单个row key访问
  • 2 通过row key的range
  • 3 全表扫描
hbase rowkey如何设计才能散列到不同的节点上?

即时间上连续的数据。这些数据可能来自于某个传感器网络、证券交易或者一个监控系统。它们显著的特点就是rowkey中含有事件发生时间。带来的一个问题便是HBase对于row的不均衡分布,它们被存储在一个唯一的rowkey区间中,被称为region,区间的范围被称为Start Key和End Key。

对于单调递增的时间类型数据,很容易被散列到同一个Region中,这样它们会被存储在同一个服务器上,从而所有的访问和更新操作都会集中到这一台服务器上,从而在集群中形成一个hot spot,从而不能将集群的整体性能发挥出来。

要解决这个问题是非常容易的,只需要将所有的数据散列到全部的Region上即可。这是可以做到的,比如,在rowkey前面加上一个非线程序列,常常有如下选择:

Hash散列

可以使用一个Hash前缀来保证所有的行被分发到多个Region服务器上。例如:

byte prefix =
(byte) (Long.hashCode(timestamp) % <number of regionservers>);
byte[] rowkey =
Bytes.add(Bytes.toBytes(prefix), Bytes.toBytes(timestamp);

这个公式可以产生足够的数字,将数据散列到所有的Region服务器上。当然,公式里假定了Region服务器的数目。如果您打算后期扩容您的集群,那么您可以把它先设置为集群的整数倍。生成的rowkey类似下面:

0myrowkey-1,
1myrowkey-2, 2myrowkey-3, 0myrowkey-4, 1myrowkey-5, \
2myrowkey-6,

当他们将按如下顺序被发送到各个Region服务器上去:

0myrowkey-1
0myrowkey-4
1myrowkey-2
1myrowkey-5

换句话说,对于0myrowkey-1和0myrowkey-4的更新操作会被发送到同一个region服务器上去(假定它们没有被散列到两个region上去),1myrowkey-2和1myrowkey-5会被发送到同一台服务器上。

这种方式的缺点是rowkey的范围必须通过代码来控制,同时对数据的访问,可能要访问多台region服务器。当然,可以通过多个线程同时访问,来实现并行化的数据读取。这种类似于只有map的MapReduce任务,可以大大增加IO的性能。

翻转字段

比如将时间戳翻转,来防止rowkey热点问题。

查找时:可以通过scan.setStartRow 和 scan.setStopRow 来设置查找的范围,startrow 和 endrow 也要做相应的翻转。

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/*todo  查hbase数据条数
 */
public class HBaseLocalTest {
    public static Configuration conf;
    public static Connection conn;

    static {
        conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.property.clientPort", "2181");
        conf.set("hbase.zookeeper.quorum", "*");
        try {
            conn = ConnectionFactory.createConnection(conf);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //将传入的日期参数转换成rowkey的反转
    public static String Fdate(String s) {
        char[] array = s.toCharArray();
        String reverse = "";
        //倒叙拿出
        for (int i = array.length - 1; i >= 0; i--)
            reverse += array[i];
        return reverse;
    }
    //补齐传入的字符串
    public static String Complement(String busNo) {//预先定义一个6位0
        StringBuilder zero =new StringBuilder("000000");
        String result = zero.substring(0, zero.length() - busNo.length()) + busNo;
        return result;
    }

    /**
     * 根据startRowKey和endRowKey筛选出区间,然后根据regxKey正则匹配和num查出最终的结果
     * @param tableName 表名
     * @param startRowKey 开始的范围
     * @param endRowKey 结束的范围
     * @param regxKey 正则匹配
     * @param num 查询的条数
     * @return List<Result>
     */
    public static List<Result> getNumRegexRow(String tableName,String startRowKey,String endRowKey, String regxKey,int num) {
        HTableInterface table = null;
        List<Result> list = null;
        try {
            table = new HTable(conf, tableName);
            //创建一个过滤器容器,并设置其关系(AND/OR)
            FilterList fl = new FilterList(FilterList.Operator.MUST_PASS_ALL);
            //设置正则过滤器
            RegexStringComparator rc = new RegexStringComparator(regxKey);
            RowFilter rf = new RowFilter(CompareFilter.CompareOp.EQUAL, rc);
            //过滤获取的条数 
            if (num != 0) {
                Filter filterNum = new PageFilter(num);//展示条数
                fl.addFilter(filterNum);
            }
            //过滤器的添加
            fl.addFilter(rf);
            Scan scan = new Scan();
            //设置取值范围
            scan.setStartRow(startRowKey.getBytes());//开始的key
            scan.setStopRow(endRowKey.getBytes());//结束的key
            scan.setFilter(fl);//为查询设置过滤器的list
            ResultScanner scanner = table.getScanner(scan) ;
            list = new ArrayList<Result>() ;
            for (Result rs : scanner) {
                list.add(rs) ;
            }
        } catch (Exception e) {
            e.printStackTrace() ;
        }
        finally
        {
            try {
                table.close() ;

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return list;
    }
    public static void main(String[] args) throws Exception {
        String startDate= "181017";
        String endDate ="181017";
        String tablename = "Table";
        String zao="00";  //这里是根据一天24小时匹配查询
        String wan="23";
        String wei = "000000001";
        String wei2= "999999999";
        String startRowkey = Fdate(startDate)+zao+wei;
        String endRowkey = Fdate(endDate)+wan+wei2;
        List<Result> list = getNumRegexRow(tablename,startRowkey,endRowkey,"[^\\\\\\/\\^]",0);  //后面的0,是查询所有符合的。如果改为5,只会打印符合条件的前5条数据
        for (int j = 0; j < list.size(); j++) {
//            System.out.print("PROID:");  //下面是打印字段的值
//            System.out.print(Bytes.toString(list.get(j).getValue(Bytes.toBytes("YB"),Bytes.toBytes("PROID"))));
//            System.out.print("     ORIDATA:");
//            System.out.println(Bytes.toString(list.get(j).getValue(Bytes.toBytes("YB"),Bytes.toBytes("ORIDATA"))));
            System.out.print("     SITE_TIME:");
            System.out.print(Bytes.toString(list.get(j).getValue(Bytes.toBytes("f"),Bytes.toBytes("SITE_TIME"))));
            System.out.print("     DOWN:");
            System.out.print(Bytes.toString(list.get(j).getValue(Bytes.toBytes("f"),Bytes.toBytes("IS_UP+DOWN"))));
            System.out.print("     LAT:");
            System.out.print(Bytes.toString(list.get(j).getValue(Bytes.toBytes("f"),Bytes.toBytes("LAT"))));
            System.out.print("     LNG:");
            System.out.println(Bytes.toString(list.get(j).getValue(Bytes.toBytes("f"),Bytes.toBytes("LNG"))));
        }
        System.out.println("数据量:"+list.size());
        conn.close();
    }
}

九、请详细描述 Hbase 中一个 Cell 的结构

HBase 中通过 row 和 columns 确定的为一个存贮单元称为 cell。Cell:由{row key, column(= + ), version}是唯一确定的单元cell 中的数据是没有类型的,全部是字节码形式存贮。

十、简述 Hbase filter 的实现原理是什么?结合实际项目经验,写出几个使用filter 的场景。

HBase 为筛选数据提供了一组过滤器,通过这个过滤器可以在 HBase 中的数据的多个维度(行,列,数据版本)上进行对数据的筛选操作,也就是说过滤器最终能够筛选的数据能够细化到具体的一个存储单元格上(由行键, 列名,时间戳定位)。

RowFilter、PrefixFilter。hbase 的 filter 是通过 scan 设置的,所以是基于 scan 的查询结果进行过滤. 过滤器的类型很多,但是可以分为两大类——比较过滤器,专用过滤器。过滤器的作用是在服务端判断数据是否满足条件,然后只将满足条件的数据返回给客户端;如在进行订单开发的时候,我们使用 rowkeyfilter 过滤出某个用户的所有订单。

相关的过滤方法使用:

  • 提取rowkey以01结尾数据
    Filter filter = new RowFilter(CompareFilter.CompareOp.EQUAL,new RegexStringComparator(".*01$"));
  • 提取rowkey以包含201407的数据
    Filter filter = new RowFilter(CompareFilter.CompareOp.EQUAL,new SubstringComparator(“201407”));
  • 提取rowkey以123开头的数据
    Filter filter = new RowFilter(CompareFilter.CompareOp.EQUAL,new BinaryPrefixComparator(“123”.getBytes()));

十一、hbase 的存储结构?

Hbase 中的每张表都通过行键 (rowkey) 按照一定的范围被分割成多个子表(HRegion),默认一个 HRegion 超过 256M 就要被分割成两个,由 HRegionServer 管理,管理哪些 HRegion 由 Hmaster 分配。 HRegion 存取一个子表时,会创建一个 HRegion 对象然后对表的每个列族 (Column Family) 创建一个 store 实例, 每个 store 都会有 0个或多个 StoreFile 与之对应,每个 StoreFile 都会对应一个 HFile , HFile 就是实际的存储文件
HFile 是HBase 使用的底层存储格式。**HFile 对应于列族,一个列族可以有多个 HFile,但一个 HFile 不能存储多个列族的数据。**在集群的每个节点上,每个列族有一个MemStore

十二、HBase 宕机如何处理?

宕机分为 HMaster 宕机和 HRegisoner 宕机.

  • 如果是 HRegisoner 宕机,HMaster 会将其所管理的 region 重新分布到其他活动的 RegionServer 上,由于数据和日志都持久在 HDFS 中,该操作不会导致数据丢失,所以数据的一致性和安全性是有保障的。
  • 如果是 HMaster 宕机, HMaster 没有单点问题, HBase 中可以启动多个HMaster,通过 Zookeeper 的 Master Election 机制保证总有一个 Master 运行。即ZooKeeper 会保证总会有一个 HMaster 在对外提供服务。

十三、Hmaster高可用部署

  • 2.1 创建backup-masters文件
    在wyl01机器上hbase的conf目录下创建backup-masters文件,文件内容如下:
cd /opt/hbase/conf
vim  backup-masters
# 添加以下内容,# wyl01是之前的master节点,现在是让wyl02成为别用的master节点
wyl02
  • 2.2 分发到其他的两台机器
scp -r backup-masters wyl02:/opt/hbase/conf/
 scp -r backup-masters wyl03:/opt/hbase/conf/
  • 2.3 启动服务
#在wyl01机器上执行启动脚本
cd /opt/hbase/bin
./start-hbase.sh

十四、hbase之布隆过滤器

布隆过滤器(Bloom Filter)是1970由布隆提出的。通过一个很长的二进制向量与一系列随机哈希函数生成。
布隆过滤器是hbase中的高级功能,它能够减少特定访问模式(get/scan)下的查询时间。不过由于这种模式增加了内存和存储的负担,所以被默认为关闭状态。

hbase支持如下类型的布隆过滤器:

  • 1、NONE 不使用布隆过滤器
  • 2、ROW 行键使用布隆过滤器
  • 3、ROWCOL 列键使用布隆过滤器

其中ROWCOL是粒度更细的模式。

当我们随机读get数据时,如果采用hbase的块索引机制,hbase会加载很多块文件。如果采用布隆过滤器后,它能够准确判断该HFile的所有数据块中,是否含有我们查询的数据,从而大大减少不必要的块加载,从而增加hbase集群的吞吐率。

1、布隆过滤器的存储在哪
对于hbase而言,当我们选择采用布隆过滤器之后,HBase会在生成StoreFile(HFile)时包含一份布隆过滤器结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销。但是在大多数情况下,这些负担相对于布隆过滤器带来的好处是可以接受的。

2、采用布隆过滤器后,hbase如何get数据
在读取数据时,hbase会首先在布隆过滤器中查询,根据布隆过滤器的结果,再在MemStore中查询,blockcache中查找,最后再在对应的HFile中查询

3、采用ROW还是ROWCOL布隆过滤器
这取决于用户的使用模式。如果用户只做行扫描,使用更加细粒度的行加列布隆过滤器不会有任何的帮助,这种场景就应该使用行级布隆过滤器。当用户不能批量更新特定的一行,并且最后的使用存储文件都含有改行的一部分时,行加列级的布隆过滤器更加有用。

理论知识:
https://zhuanlan.zhihu.com/p/93853964
https://blog.csdn.net/qq_32445015/article/details/101926881

参考资料:
https://www.cnblogs.com/kaiwen03/p/9847877.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值