hbase源码编译、配置安装、测试

一、 hbase架构简介

HBase 是一个开源的、分布式的、数据多版本的,列式存储的nosql数据库。依托 Hadoop 的分布式文件系统 HDFS 作为底层存储, 能够为数十亿行数百万列的海量数据表提供随机、实时的读写访问。 HBase 集群服务包含:HBase 数据库服务、HDFS 分布式文件系统、Phoenix 查询引擎。压缩格式方面支持 GZIP、BZIP2、LZO、SNAPPY,可自行在应用中指定。 关于 HBase 更多的详细信息,可参阅 HBase 官方文档,关于 Phoenix 查询引擎的详细信息,可参阅 Phoenix 官方网站 。

与 Hadoop 一样,HBase 集群采用的是 master/slave 架构。 如下图所示,HBase 集群分三种节点类型:主节点 (HBase Master 和 HDFS NameNode),从节点 (HBase RegionServer 和 HDFS DataNode) 和客户端节点 (HBase Client)。 用户在HBase 客户端可通过HBase Shell、Java API(本地或MapReduce)、Rest API 或其他工具来访问HBase。 若需要使用除java外的其他语言时,可在客户端节点 (HBase Client)自行启动 Thrift Server 以供支持。


二、 hbase源码编译

1. 下载源码

$ git://git.apache.org/hbase.git
2. hbase编译

$ mvn clean package -DskipTests assembly:single
$ ls hbase-assembly/target/

三、 hbase配置安装

1. 配置JAVA_HOME

$ vim ./hbase/conf/hbase-env.sh
export JAVA_HOME=/usr/lib/java
export HBASE_MANAGES_ZK=true

2. 配置HDFS

$ vim ./hbase/conf/hbase-site.xml

  
    hbase.rootdir
    hdfs://master:9000/hbase
  
  
    hbase.cluster.distributed
    true
  
  
    hbase.master
    master:60000
  
  
    hbase.master.port
    60000
   
    
        hbase.master.info.port
        60001
    
  
    hbase.zookeeper.quorum
    master,slave1,slave2
  
  
    hbase.zookeeper.property.dataDir
    /home/spark/workspace/zookeeper/data
  

其中第一个属性指定本机的hbase的存储目录,必须与Hadoop集群的core-site.xml文件配置保持一致;第二个属性指定hbase的运行模式,true代表全分布模式;第三第四个属性指定hbase master的ip和port;第五个属性指定 Zookeeper 管控机器,一般为奇数个;第六个属性是数据存放的路径。这里我使用的默认的 HBase 自带的 Zookeeper。

3. 配置regionservers

$ vim ./hbase/conf/regionservers
slave1
slave2

4. hbase安装运行

$ pscp -r -h all_iplist hbase /home/hbase/
$ ./bin/start-hbase.sh
$ /home/hbase/bin/hbase shell
list

四、 hbase测试

1. hbase表创建插入查询删除操作

$ ./bin/hbase shell
hbase(main):001:0> create 'test', 'cf'
0 row(s) in 1.2130 seconds
=> Hbase::Table - test
hbase(main):002:0> list 'test'
TABLE
test
1 row(s) in 0.0180 seconds
=> ["test"]
hbase(main):003:0> put 'test', 'row1', 'cf:a', 'value1'
0 row(s) in 0.0850 seconds
hbase(main):004:0> put 'test', 'row2', 'cf:b', 'value2'
0 row(s) in 0.0110 seconds
hbase(main):005:0> put 'test', 'row3', 'cf:c', 'value3'
0 row(s) in 0.0100 seconds
hbase(main):006:0> scan 'test'
ROW                                      COLUMN+CELL
 row1                                    column=cf:a, timestamp=1469163844008, value=value1
 row2                                    column=cf:b, timestamp=1469163862005, value=value2
 row3                                    column=cf:c, timestamp=1469163899601, value=value3
3 row(s) in 0.0230 seconds
hbase(main):007:0> get 'test', 'row1'
COLUMN                                   CELL
 cf:a                                    timestamp=1469094709015, value=value1
1 row(s) in 0.0350 seconds
hbase(main):008:0> disable 'test'
0 row(s) in 1.1820 seconds
hbase(main):009:0> drop 'test'
0 row(s) in 0.1370 seconds
2. benchmark性能测试

# 测试随机写,预分区10个 region,使用多线程代替 MapReduce 的方式来并发随机写操作,10个线程,每个线程写10000行。
$ ./bin/hbase pe --nomapred --rows=10000 --presplit=10 randomWrite 10
# 测试顺序写,预分区10个 region,使用多线程代替 MapReduce 的方式来并发顺序写操作,10个线程,每个线程写10000行。
$ ./bin/hbase pe --nomapred --rows=10000 --presplit=10 sequentialWrite 10
# 测试基于 row    的自增操作,使用多线程代替 MapReduce 的方式来并发自增操作,10个线程,每个线程 increment 10000次。
$ ./bin/hbase pe --rows=10000 --nomapred increment 10
# 测试基于row的追加操作,使用多线程代替 MapReduce 的方式来并发追加操作,10个线程,每个线程 append 10000次。
$ ./bin/hbase pe --rows=10000 --nomapred append 10
# 测试随机读,使用多线程代替 MapReduce 的方式来并发随机读操作,10个线程,每个线程读10000行
$ ./bin/hbase pe --nomapred --rows=10000 randomRead 10
# 测试顺序读,使用多线程代替 MapReduce 的方式来并发顺序读操作,10个线程,每个线程读10000行
$ ./bin/hbase pe --nomapred --rows=10000 sequentialRead 10
# 测试范围scan操作,使用多线程代替 MapReduce 的方式来并发范围 scan 操作,10个线程,每个线程 scan 10000次,每次范围返回最大100行。
$ ./bin/hbase pe --rows=10000 --nomapred scanRange100 10
3. 其他测试,待整理

这个测试是通过 MapReduce 服务来批量导入 HDFS 中数据到 HBase

注解

 

已创建 Hadoop 集群并完成上文创建 HBase 客户端中使用 MapReduce 服务配置。

可通过 DistCp 命令来拷贝不同 HDFS 中的数据,关于 DistCp 更多的详细信息,可参阅 DistCp

使用 MapReduce 导入数据有三种方案:

一、直接书写 MapReduce 使用 HBase 提供的 JAVA API 从 HDFS 导入到 HBase 表。

二、书写 MapReduce 将 HDFS 中数据转化为 HFile 格式,再使用 HBase 的 BulkLoad 工具导入到 HBase 表。

三、使用 HBase ImportTsv 工具将格式化的 HDFS 数据导入到 HBase 表。

若要导入的数据已经是格式化的数据(有固定的分隔符),不需要自己实现 MapReduce 做进一步数据清洗,直接采用方案三;若数据并未格式化仍需规整则采用方案二。

以下方案中均使用 HBase 表 test_import,包含一个column family:content,可通过 HBase Shell 预先建好表

$ cd /usr/local/hbase

$ bin/hbase shell

hbase(main):001:0> create 'test_import', 'content'
0 row(s) in 1.2130 seconds

=> Hbase::Table - test_import

项目若使用mvn构建,pom.xml 中增加如下内容:


  1.2.2



  
    org.apache.hbase
    hbase-server
    ${hbase.version}
  

方案一 MapReduce 代码如下,先创建表,在 Map 中完成数据解析,在 Reduce 中完成入库。Reduce的个数相当于入库线程数。

package com.qingcloud.hbase

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.io.LongWritable;
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 org.apache.hadoop.util.GenericOptionsParser;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ImportByMR {

    private static String table = "test_import";

    private static class ImportByMRMapper extends Mapper {

        @Override
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String[] sp = value.toString().split(" ");
            if (sp.length < 2) {
                return;
            }
            context.write(new Text(sp[0]), new Text(sp[1]));
        }
    }

    private static class ImportByMRReducer extends TableReducer {

        @Override
        public void reduce(Text key, Iterable value, Context context) throws IOException, InterruptedException {
            byte[] bRowKey = key.toString().getBytes();
            ImmutableBytesWritable rowKey = new ImmutableBytesWritable(bRowKey);

            for (Text t : value) {
                Put p = new Put(bRowKey);
                p.setDurability(Durability.SKIP_WAL);
                p.addColumn("content".getBytes(), "a".getBytes(), t.toString().getBytes());
                context.write(rowKey, p);
            }
        }
    }

    private static void createTable(Configuration conf) throws IOException {
        TableName tableName = TableName.valueOf(table);
        Connection connection = ConnectionFactory.createConnection(conf);
        Admin admin = connection.getAdmin();
        if (admin.tableExists(tableName)) {
            System.out.println("table exists!recreating.......");
            admin.disableTable(tableName);
            admin.deleteTable(tableName);
        }
        HTableDescriptor htd = new HTableDescriptor(tableName);
        HColumnDescriptor tcd = new HColumnDescriptor("content");
        htd.addFamily(tcd);
        admin.createTable(htd);
    }

    public static void main(String[] argv) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf = HBaseConfiguration.create();
        File file = new File("/usr/local/hbase/conf/hbase-site.xml");
        FileInputStream in = new FileInputStream(file);
        conf.addResource(in);
        createTable(conf);
        GenericOptionsParser optionParser = new GenericOptionsParser(conf, argv);
        String[] remainingArgs = optionParser.getRemainingArgs();

        Job job = Job.getInstance(conf, ImportByMR.class.getSimpleName());
        job.setJarByClass(ImportByMR.class);
        job.setMapperClass(ImportByMRMapper.class);
        TableMapReduceUtil.initTableReducerJob(table, ImportByMRReducer.class, job);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);
        job.setOutputKeyClass(ImmutableBytesWritable.class);
        job.setOutputValueClass(Mutation.class);
        job.setNumReduceTasks(1);
        FileInputFormat.addInputPath(job, new Path(remainingArgs[0]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

hbase-tools-1.0.0.jar 是将上述代码打成的jar包,APP_HOME 是 jar 包所放置的目录,/user/inputPath 下是需要导入到HBase中的数据。 数据格式为 rowkey value,两列空格分隔。需自行准备后通过 bin/hdfs dfs -put 到 HDFS 的 /user/inputPath 目录。 依次执行下述命令:

$ cd /usr/local/hadoop

$ bin/hadoop jar $APP_HOME/hbase-tools-1.0.0.jar com.qingcloud.hbase.ImportByMR /user/inputPath

执行成功后可简单通过测试一中的 HBase Shell 来验证数据。

方案二 MapReduce 代码如下,Map 对数据做进一步处理,Reduce 无需指定,会根据 Map 的 outputValue 自动选择实现。

package com.qingcloud.hbase

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.HFileOutputFormat2;
import org.apache.hadoop.io.LongWritable;
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 org.apache.hadoop.util.GenericOptionsParser;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ImportByBulkLoad {

    private static String myTable = "test_import";

    private static class ImportByBulkLoadMapper extends Mapper {

        @Override
        public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String[] sp = value.toString().split(" ");
            if (sp.length < 2) {
                return;
            }
            byte[] bRowKey = sp[0].getBytes();
            ImmutableBytesWritable rowKey = new ImmutableBytesWritable(bRowKey);
            Put p = new Put(bRowKey);
            p.setDurability(Durability.SKIP_WAL);
            p.addColumn("content".getBytes(), "a".getBytes(), sp[1].getBytes());
            context.write(rowKey, p);
        }
    }

    public static void main(String[] argv) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf = HBaseConfiguration.create();
        File file = new File("/usr/local/hbase/conf/hbase-site.xml");
        FileInputStream in = new FileInputStream(file);
        conf.addResource(in);
        GenericOptionsParser optionParser = new GenericOptionsParser(conf, argv);
        String[] remainingArgs = optionParser.getRemainingArgs();
        Job job = Job.getInstance(conf, ImportByBulkLoad.class.getSimpleName());
        job.setJarByClass(ImportByBulkLoad.class);
        job.setMapperClass(ImportByBulkLoadMapper.class);
        job.setMapOutputKeyClass(ImmutableBytesWritable.class);
        job.setMapOutputValueClass(Put.class);
        FileInputFormat.addInputPath(job, new Path(remainingArgs[0]));
        HFileOutputFormat2.setOutputPath(job, new Path(remainingArgs[1]));
        TableName tableName = TableName.valueOf(myTable);
        Connection connection = ConnectionFactory.createConnection(conf);
        Table table = connection.getTable(tableName);
        RegionLocator regionLocator = connection.getRegionLocator(tableName);
        HFileOutputFormat2.configureIncrementalLoad(job, table, regionLocator);
        System.exit(job.waitForCompletion(true) ? 0 : 1);
    }
}

hbase-tools-1.0.0.jar 是将上述代码打成的 jar 包,APP_HOME 是 jar 包所放置的目录,/user/inputPath 下是需要导入到HBase中的数据。数据格式为 rowkey value,两列空格分隔。 需自行准备后通过 bin/hdfs dfs -put 到 HDFS 的 /user/inputPath 目录。 /user/outputPath 是 MapReduce 生成的 HFile 格式的结果。test_import 是 HBase 表名。依次执行下述命令:

$ cd /usr/local/hadoop

$ bin/hdfs dfs -rmr /user/outputPath

$ export HADOOP_CLASSPATH=`/usr/local/hbase/bin/hbase classpath`

$ bin/hadoop jar $APP_HOME/hbase-tools-1.0.0.jar com.qingcloud.hbase.ImportByBulkLoad /user/inputPath /user/outputPath

$ bin/hadoop jar /usr/local/hbase/lib/hbase-server-.jar completebulkload /user/outputPath test_import

执行成功后可简单通过测试一中的 HBase Shell 来验证数据。

方案三无需书写代码,/user/inputPath 下是需要导入到 HBase 中的数据。数据格式为 rowkey value,两列空格分隔。需自行准备后通过 bin/hdfs dfs -put 到 HDFS 的 /user/inputPath 目录。 /user/outputPath 是 HFile 格式的暂存结果。test_import是HBase表名。依次执行下述命令:

$ cd /usr/local/hadoop

$ bin/hdfs dfs -rmr /user/outputPath

$ export HADOOP_CLASSPATH=`/usr/local/hbase/bin/hbase classpath`

$ bin/hadoop jar /usr/local/hbase/lib/hbase-server-.jar importtsv -Dimporttsv.columns=HBASE_ROW_KEY,content:a -Dimporttsv.bulk.output=/user/outputPath test_import /user/inputPath

$ cd /usr/local/hadoop

$ bin/hdfs dfs -rmr /user/outputPath

$ cd /usr/local/hbase

$ bin/hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.columns=HBASE_ROW_KEY,content:a -Dimporttsv.bulk.output=/user/outputPath test_import /user/inputPath

执行成功后可简单通过测试一中的 HBase Shell 来验证数据。

你可能感兴趣的:(分布式数据库)