HBase(九)集成MapReduce

HBase表中的数据最终都是存储在HDFS上,HBase天生的支持MR的操作,我们可以通过MR直接处理HBase表中的数据,并且MR可以将处理后的结果直接存储到HBase表中。

参考: http://hbase.apache.org/book.html#mapreduce

实战一:HBase表到HBase表

需求:读取HBase当中myuser这张表的f1:name、f1:age数据,将数据写入到另外一张myuser2表的f1列族里面去

第一步:创建myuser2这张hbase表

注意:列族的名字要与myuser表的列族名字相同

create 'myuser2','f1'

第二步:创建maven工程并导入jar包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>XZK</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>HbaseMrDdemo</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>3.1.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-auth</artifactId>
            <version>3.1.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-client -->
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-mapreduce</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.14.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <!--    <verbal>true</verbal>-->
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*/RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

第三步:开发MR程序实现功能

自定义map类

package com.kkb.hbase.mr;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapper;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.Text;

import java.io.IOException;

public class HBaseReadMapper extends TableMapper<Text, Put> {
    /**
     * @param key     rowKey
     * @param value   rowKey此行的数据 Result类型
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {
        // 获得rowKey的字节数组
        byte[] rowKeyBytes = key.get();
        String rowKeyStr = Bytes.toString(rowKeyBytes);
        Text text = new Text(rowKeyStr);

        // 输出数据 -> 写数据 -> Put 构建Put对象
        Put put = new Put(rowKeyBytes);
        // 获取一行中所有的Cell对象
        Cell[] cells = value.rawCells();
        // 将f1 : name& age输出
        for (Cell cell : cells) {
            //当前cell是否是f1
            //列族
            byte[] familyBytes = CellUtil.cloneFamily(cell);
            String familyStr = Bytes.toString(familyBytes);
            if ("f1".equals(familyStr)) {
                //在判断是否是name | age
                byte[] qualifier_bytes = CellUtil.cloneQualifier(cell);
                String qualifierStr = Bytes.toString(qualifier_bytes);
                if ("name".equals(qualifierStr)) {
                    put.add(cell);
                }
                if ("age".equals(qualifierStr)) {
                    put.add(cell);
                }
            }
        }

        // 判断是否为空;不为空,才输出
        if (!put.isEmpty()) {
            context.write(text, put);
        }
    }
}

自定义reduce类

import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.io.Text;
import java.io.IOException;

/**
 * TableReducer第三个泛型包含rowkey信息
 */
public class HBaseWriteReducer extends TableReducer<Text, Put, ImmutableBytesWritable> {
    //将map传输过来的数据,写入到hbase表
    @Override
    protected void reduce(Text key, Iterable<Put> values, Context context) throws IOException, InterruptedException {
        //rowkey
        ImmutableBytesWritable immutableBytesWritable = new ImmutableBytesWritable();
        immutableBytesWritable.set(key.toString().getBytes());

        //遍历put对象,并输出
        for(Put put: values) {
            context.write(immutableBytesWritable, put);
        }
    }
}

main入口类

package com.kkb.hbase.mr;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class Main extends Configured implements Tool {
    public static void main(String[] args) throws Exception {
        Configuration configuration = HBaseConfiguration.create();
        // 设定绑定的zk集群
        configuration.set("hbase.zookeeper.quorum", "hadoop100:2181,hadoop101:2181,hadoop102:2181");

        int run = ToolRunner.run(configuration, new Main(), args);
        System.exit(run);
    }

    @Override
    public int run(String[] args) throws Exception {
        Job job = Job.getInstance(super.getConf());
        job.setJarByClass(Main.class);

        // mapper
        TableMapReduceUtil.initTableMapperJob(TableName.valueOf("myuser"), new Scan(), HBaseReadMapper.class, Text.class, Put.class, job);
        // reducer
        TableMapReduceUtil.initTable ReducerJob("myuser2", HBaseWriteReducer.class, job);

        boolean b = job.waitForCompletion(true);
        return b ? 0 : 1;
    }
}

实战二: HDFS到HBase表

需求读取hdfs上面的数据,写入到hbase表里面去

hadoop102执行以下命令准备数据文件,并将数据文件上传到HDFS上面去

在/opt/data目录,创建user.txt文件

cd /opt/data
vim user.txt

内容如下:

0007    zhangsan    18
0008    lisi    25
0009    wangwu  20

将文件上传到hdfs的路径下面去

hdfs dfs -mkdir -p /hbase/input
hdfs dfs -put /opt/pkg/user.txt /hbase/input/

代码开发

package com.kkb.hbase.hdfs2hbase;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;

import java.io.IOException;

/**
 * 将HDFS上文件/hbase/input/user.txt数据,导入到HBase的myuser2表
 */
public class HDFS2HBase {
    public static class HdfsMapper extends Mapper<LongWritable, Text, Text, NullWritable> {

        // 数据原样输出
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            context.write(value, NullWritable.get());
        }
    }

    public static class HBaseReducer extends TableReducer<Text, NullWritable, ImmutableBytesWritable> {

        protected void reduce(Text key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
            /**
             * key -> 一行数据
             * 样例数据:
             * 0007 zhangsan    18
             * 0008 lisi    25
             * 0009 wangwu  20
             */
            String[] split = key.toString().split("\t");

            Put put = new Put(Bytes.toBytes(split[0]));
            put.addColumn("f1".getBytes(), "name".getBytes(), split[1].getBytes());
            put.addColumn("f1".getBytes(), "age".getBytes(), split[2].getBytes());

            context.write(new ImmutableBytesWritable(Bytes.toBytes(split[0])), put);
        }
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf = HBaseConfiguration.create();
        // 设定zk集群
        conf.set("hbase.zookeeper.quorum", "hadoop100:2181,hadoop101:2181,hadoop102:2181");
        Job job = Job.getInstance(conf);

        job.setJarByClass(HDFS2HBase.class);

        // 可省略
        // job.setInputFormatClass(TextInputFormat.class);
        // 输入文件路径
        FileInputFormat.addInputPath(job, new Path("hdfs://hadoop100:8020/hbase/input"));

        job.setMapperClass(HdfsMapper.class);
        // map端的输出的key value 类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(NullWritable.class);

        // 指定输出到hbase的表名
        TableMapReduceUtil.initTableReducerJob("myuser2", HBaseReducer.class, job);

        // 设置reduce个数
        job.setNumReduceTasks(1);

        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

实战三:HDFS到HBase表(批量加载)

需求

通过bulkload的方式批量加载数据到HBase表中

将我们hdfs上面的这个路径/hbase/input/user.txt的数据文件,转换成HFile格式,然后load到myuser2这张表里面去

知识点描述:

  • 加载数据到HBase当中去的方式多种多样,我们可以使用HBase的javaAPI或者使用sqoop将我们的数据写入或者导入到HBase当中去,但是这些方式不是最佳的,因为在导入的过程中占用Region资源导致效率低下

  • 我们也可以通过MR的程序,将我们的数据直接转换成HBase的最终存储格式HFile,然后直接load数据到HBase当中去即可

HBase数据正常写流程回顾

bulkload方式的处理示意图

好处

  • 导入过程不占用Region资源
  • 能快速导入海量的数据
  • 节省内存
  1. 开发生成HFile文件的代码

自定义map类

import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

// 四个泛型中后两个,分别对应rowkey及put
public class BulkLoadMapper extends Mapper<LongWritable, Text, ImmutableBytesWritable, Put> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] split = value.toString().split("\t");
        // 封装输出的rowkey类型
        ImmutableBytesWritable immutableBytesWritable = new ImmutableBytesWritable(split[0].getBytes());

        // 构建put对象
        Put put = new Put(split[0].getBytes());
        put.addColumn("f1".getBytes(), "name".getBytes(), split[1].getBytes());
        put.addColumn("f1".getBytes(), "age".getBytes(), split[2].getBytes());

        context.write(immutableBytesWritable, put);
    }
}

程序main

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class HBaseBulkLoad extends Configured implements Tool {
    public static void main(String[] args) throws Exception {
        Configuration configuration = HBaseConfiguration.create();
        //设定zk集群
        configuration.set("hbase.zookeeper.quorum", "hadoop100:2181,hadoop101:2181,hadoop102:2181");

        int run = ToolRunner.run(configuration, new HBaseBulkLoad(), args);
        System.exit(run);
    }
    @Override
    public int run(String[] args) throws Exception {
        Configuration conf = super.getConf();
        Job job = Job.getInstance(conf);
        job.setJarByClass(HBaseBulkLoad.class);

        FileInputFormat.addInputPath(job, new Path("hdfs://hadoop100:8020/hbase/input"));
        job.setMapperClass(BulkLoadMapper.class);
        job.setMapOutputKeyClass(ImmutableBytesWritable.class);
        job.setMapOutputValueClass(Put.class);

        Connection connection = ConnectionFactory.createConnection(conf);
        Table table = connection.getTable(TableName.valueOf("myuser2"));

        //使MR可以向myuser2表中,增量增加数据
        HFileOutputFormat2.configureIncrementalLoad(job, table, connection.getRegionLocator(TableName.valueOf("myuser2")));
        //数据写回到HDFS,写成HFile -> 所以指定输出格式为HFileOutputFormat2
        job.setOutputFormatClass(HFileOutputFormat2.class);
        HFileOutputFormat2.setOutputPath(job, new Path("hdfs://hadoop100:8020/hbase/out_hfile"));

        //开始执行
        boolean b = job.waitForCompletion(true);

        return b? 0: 1;
    }
}

3、观察HDFS上输出的结果

4、加载HFile文件到hbase表中

方式一:代码加载

  package kkk.hbase.demo3;

  import org.apache.hadoop.conf.Configuration;
  import org.apache.hadoop.fs.Path;
  import org.apache.hadoop.hbase.HBaseConfiguration;
  import org.apache.hadoop.hbase.TableName;
  import org.apache.hadoop.hbase.client.Admin;
  import org.apache.hadoop.hbase.client.Connection;
  import org.apache.hadoop.hbase.client.ConnectionFactory;
  import org.apache.hadoop.hbase.client.Table;
  import org.apache.hadoop.hbase.tool.BulkLoadHFiles;

  public class LoadData {

      public static void main(String[] args) throws Exception {
          Configuration configuration = HBaseConfiguration.create();
          configuration.set("hbase.zookeeper.quorum", "hadoop100,hadoop101,hadoop102");
          // 获取数据库连接
          Connection connection = ConnectionFactory.createConnection(configuration);
          // 获取表的管理器对象
          Admin admin = connection.getAdmin();
          // 获取table对象
          TableName tableName = TableName.valueOf("myuser2");
          Table table = connection.getTable(tableName);
          // 构建BulkLoadHFiles加载HFile文件 hbase2.0 api
          BulkLoadHFiles load = BulkLoadHFiles.create(configuration);
          load.bulkLoad(tableName, new Path("hdfs://hadoop100:8020/hbase/out_hfile"));
      }

Views: 29

HBase(八)Region 合并操作

Region的合并不是为了性能, 而是出于便于运维的目的 .

比如删除了大量的数据 ,这个时候每个Region都变得很小 ,存储多个Region就浪费了 ,这个时候可以把Region合并起来,进而可以减少一些Region服务器节点。

下面来看一下如何进行region合并:

通过Merge类冷合并Region

执行冷合并前,需要先关闭hbase集群

创建一张hbase表:

create 'test','info1',SPLITS => ['1000','2000','3000']

查看表region

file

需求:

通过查看UI界面,需要把test表中的前2个region数据进行合并,分别是:

  1. test,,1620148534743.8222cc22ab4acb18b726b3c6be1cb082.
  2. test,1000,1620148534743.b2e07e8922d0ffa6477ef2d7ff368bba.

这里通过org.apache.hadoop.hbase.util.Merge类来实现,不需要进入hbase shell,直接执行(需要先关闭hbase集群):

hbase org.apache.hadoop.hbase.util.Merge test test,,1620148534743.8222cc22ab4acb18b726b3c6be1cb082. test,1000,1620148534743.b2e07e8922d0ffa6477ef2d7ff368bba.

成功后可以界面观察分区变化,但是我这里运行找不到主类org.apache.hadoop.hbase.util.Merge。实际上冷合并不常用,因为需要停止集群损耗巨大,工作中更常使用热合并。

通过online_merge热合并Region

不需要关闭hbase集群==,在线进行合并

与冷合并不同的是,online_merge的传参是Region的hash值,而Region的hash值就是Region名称的最后那段在两个.之间的字符串部分。

需求:需要把test表中的2个region数据进行合并:

  1. test,2000,1620148534743.68216f0389235352165d3b884db24b95.
  2. test,3000,1620148534743.c85c37595f74abb8eb0276c50bf700e6.

需要进入hbase shell:

hbase(main):001:0> merge_region '68216f0389235352165d3b884db24b95','c85c37595f74abb8eb0276c50bf700e6'

Took 11.3953 seconds

成功后观察界面

file

Views: 45

HBase(七)表的预分区

HBase表的预分区

  • 当一个table刚被创建的时候,Hbase默认的分配一个region给table。也就是说这个时候,所有的读写请求都会访问到同一个regionServer的同一个region中,这个时候就达不到负载均衡的效果了,集群中的其他regionServer就可能会处于比较空闲的状态。
  • 解决这个问题可以用pre-splitting,在创建table的时候就配置好,生成多个region。

1 为何要预分区?

  • 增加数据读写效率
  • 负载均衡,防止数据倾斜
  • 方便集群容灾调度region
  • 优化Map数量

2 预分区原理

  • 每一个region维护着startRowKey与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。

手动指定预分区

  • 三种方式

  • 方式一

create 'person','info1','info2',SPLITS => ['1000','2000','3000','4000']

可以在UI界面查看这个表的每个分区的起始和结束rowKey。

file

  • 方式二:也可以把分区规则创建于文件中

    cd /opt/data
    
    vim split.txt
    • 文件内容
    aaa
    bbb
    ccc
    ddd
    • hbase shell中,执行命令
    create 'student','info',SPLITS_FILE => '/opt/data/split.txt'
    • 成功后查看web界面

file

  • 方式三: HexStringSplit 算法

    • HexStringSplit会将数据从“00000000”到“FFFFFFFF”之间的数据长度按照n等分之后算出每一段的起始rowkey和结束rowkey,以此作为拆分点。

    • 例如:

    create 'mytable', 'base_info',' extra_info', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}

file

Views: 27

HBase(六)flush、compact机制

Flush 触发条件

memstore 级别限制

  • Region中任意一个MemStore的大小达到了上限(hbase.hregion.memstore.flush.size,默认128MB),会触发Memstore刷新(flush)。
<property>
    <name>hbase.hregion.memstore.flush.size</name>
    <value>134217728</value>
</property>

region 级别限制

  • Region中所有Memstore的大小总和达到了上限(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2 * 128M = 256M),会触发memstore刷新。
<property>
    <name>hbase.hregion.memstore.flush.size</name>
    <value>134217728</value>
</property>
<property>
    <name>hbase.hregion.memstore.block.multiplier</name>
    <value>4</value>
</property>

说明:

  • 上限值 = 134217728 x 4*

Region Server级别限制

  • 当一个Region Server中所有Memstore的大小总和超过低水位阈值hbase.regionserver.global.memstore.size.lower.limit * hbase.regionserver.global.memstore.size(前者默认值0.95),RegionServer开始强制flush
  • Flush Memstore最大的Region,再执行次大的,依次执行;
  • 如写入速度大于flush写出的速度,导致总MemStore大小超过高水位阈值hbase.regionserver.global.memstore.size(默认为JVM内存的40%),此时RegionServer会阻塞更新并强制执行flush,直到总MemStore大小低于低水位阈值
<property>
    <name>hbase.regionserver.global.memstore.size.lower.limit</name>
    <value>0.95</value>
</property>
<property>
    <name>hbase.regionserver.global.memstore.size</name>
    <value>0.4</value>
</property>

file

file

HLog数量上限

  • 当一个Region ServerHLog数量达到上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个HLog对应的一个或多个Region进行flush

定期刷新 Memstore

  • 默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。

file

手动 flush

  • 用户可以通过shell命令flush <tablename>或者flush <region name>分别对一个表或者一个Region进行flush

flush的流程

为了减少flush过程对读写的影响,将整个flush过程分为三个阶段:

  1. prepare阶段:遍历当前Region中所有的Memstore,将Memstore中当前数据集CellSkipListSet做一个快照snapshot;然后再新建一个CellSkipListSet。后期写入的数据都会写入新的CellSkipListSet中。prepare阶段需要加一把updateLock写请求阻塞,结束之后会释放该锁。因为此阶段没有任何费时操作,因此持锁时间很短。

  2. flush阶段:遍历所有Memstore,将prepare阶段生成的snapshot持久化为临时文件,临时文件会统一放到目录.tmp下。这个过程因为涉及到磁盘IO操作,因此相对比较耗时。

  3. commit阶段:遍历所有Memstore,将flush阶段生成的临时文件移到指定的ColumnFamily目录下,针对HFile生成对应的storefileReader,把storefile添加到HStorestorefiles列表中,最后再清空prepare阶段生成的snapshot

file

Compact合并机制

hbase为了防止小文件过多,以保证查询效率,hbase需要在必要的时候将这些小的store file合并成相对较大的store file,这个过程就称之为compaction。

在hbase中主要存在两种类型的compaction合并

  1. minor compaction 小合并
  2. major compaction 大合并

HBase 2.x 还可以基于内存合并

minor compaction 小合并

  • 在将Store中多个HFile合并为一个HFile

    在这个过程中会选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,这种合并的触发频率很高。

  • minor compaction触发条件由以下几个参数共同决定:

<!--默认值3;表示一个store中至少有4个store file时,会触发minor compaction-->
<property>

<name>hbase.hstore.compactionThreshold</name>
    <value>3</value>
</property>

<!--默认值10;表示一次minor compaction中最多合并10个store file-->
<property>

<name>hbase.hstore.compaction.max</name>
    <value>10</value>
</property>

<!--默认值为128m;表示store file文件大小小于该值时,一定会加入到minor compaction的-->
<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中所有的HFile为一个HFile

    将所有的StoreFile合并成一个StoreFile,这个过程还会清理三类无意义数据:被删除的数据、TTL过期数据、版本号超过设定版本号的数据。合并频率比较低,默认7天执行一次,并且性能消耗非常大,建议生产关闭(设置为0),在应用空闲时间手动触发。一般可以是手动控制进行合并,防止出现在业务高峰期。

  • major compaction触发时间条件

    <!--默认值为7天进行一次大合并,-->
    <property>
    <name>hbase.hregion.majorcompaction</name>
    <value>604800000</value>
    </property>
  • 手动触发

    ##使用major_compact命令
    major_compact tableName

Views: 38

HBase(五)数据存储原理

  1. 一个HRegionServer会负责管理很多个region
  2. 一个region包含很多个store
    • 一个列族就划分成一个store
    • 如果一个表中只有1个列族,那么这个表的每一个region中只有一个store
    • 如果一个表中有N个列族,那么这个表的每一个region中有Nstore
  3. 一个store里面只有一个memstore
    • memstore是一块内存区域,写入的数据会先写入memstore进行缓冲,然后再把数据刷到磁盘
  4. 一个store里面有很多个StoreFile, 最后数据是以很多个HFile这种数据结构的文件保存在HDFS上
    • StoreFileHFile的抽象对象,如果说到StoreFile就等于HFile
    • 每次memstore刷写数据到磁盘,就生成对应的一个新的HFile文件出来

HBase读数据流程

说明:HBase集群,只有一张meta表,此表只有一个region,该region数据保存在一个HRegionServer

  1. 客户端首先与zookeeper进行连接;

    • zk找到meta表的region位置,即meta表的数据 一HRegionServer上;
    • 客户端与此HRegionServer建立连接,然后读取meta表中的数据;meta表中存储了所有用户表的region信息,我们可以通过scan 'hbase:meta'来查看meta表信息
  2. 根据要查询的namespace、表名和rowkey信息。找到写入数据对应的region信息

  3. 找到这个region对应的regionServer,然后发送请求

  4. 查找并定位到对应的region

  5. 先从memstore查找数据,如果没有,再从BlockCache上读取

    • HBaseRegionserver的内存分为两个部分
    • 一部分作为Memstore,主要用来写;
    • 另外一部分作为BlockCache,主要用于读数据;
  6. 如果BlockCache中也没有找到,再到StoreFile上进行读取

    • storeFile中读取到数据之后,不是直接把结果数据返回给客户端,而是把数据先写入到BlockCache中,目的是为了加快后续的查询;然后再返回结果给客户端。

file

HBase写数据流程

  1. 客户端首先从zk找到meta表的region位置,然后读取meta表中的数据,meta表中存储了用户表的region信息

  2. 根据namespace、表名和rowkey信息。找到写入数据对应的region信息

  3. 找到这个region对应的regionServer,然后发送请求

  4. 把数据分别写到HLog(write ahead log)和memstore各一份

  5. memstore达到阈值后把数据刷到磁盘,生成storeFile文件

file

补充:

HLog(write ahead log):
也称为WAL意为Write ahead log,类似mysql中的binlog,用来做灾难恢复时用,HLog记录数据的所有变更,一旦数据修改,就可以从log中进行恢复。

Views: 31