从零开始学习大数据平台(Episode 2)

(零)前言

这篇接着上一篇:《从零开始学习大数据平台(Episode 1)》

(一)为什么要用大数据平台

用大数据平台和自己开发分布式应用有啥区别呢?比如下图是一个自己开发的分布式稽核系统的测试。
我们有自己的Windows,RHEL下的调度平台,我们可以将数据分块分布式处理。那么为什么我们还需要学习和使用“通用”的大数据平台呢?
在这里插入图片描述

(1.1)开发/学习成本

一个普通程序员可以很快的熟练的掌握Spark开发,简短的代码就可以实现一种/多种业务场景。
同样开发水平的,自己开发一套,则需要考虑多线程,通信,容错,效率各种方面。开发周期很长bug也会很多,如果水平不够基本上是不可能成功完成的。

(1.2)分布式存储

分布式应用相对还算简单的话,开发一套分布式存储系统对于中小企业基本是不可能了。
考虑到人员流动,新开发人员学习它的成本也会很高。
没有分布式存储,数据处理量大而计算简单的业务场景,越分布式越慢。。。

(1.3)通用平台vs单一业务场景

使用大数据平台可以快速的开发出适合不同业务场景的业务模块。
自己开发的系统由于相对简单,实现的功能也比较单一。

(1.4)效率,灵活,扩展

自己开发的分布式应用因为没有分布式文件系统支撑,效率难以保证。
也无法像Spark,Mesos集群等灵活的指定分配资源。
自己开发的分布式节点难以做到实时的接入与下线(也是开发成本/难度的问题)。
最后由于自己开发不够强大,难以充分利用全部的CPU,内存,等资源。

(Spark集群/16core/7.7分钟)
在这里插入图片描述
(自己的平台,单线程,30分钟+)
在这里插入图片描述
只使用一台计算机上的全部虚拟机。
保证其它资源一致且不受网络条件影响。
通过调整分区参数,观察执行效率。
可以看到即便是使用同一台计算机上的全部虚拟机,效率也远高于自己开发的程序。
在这里插入图片描述
在这里插入图片描述

(二)准备测试环境

(2.1)CPU内存资源

正式生产环境可能是分配好的Linux服务器,可能是已有的集群。
但是我们测试只能用PC机,那么就需要注意资源分配的时候不要超过了物理极限。

  • 全部虚拟机的内存+物理PC机保留的内存得留有余量,不要内存不够导致使用交换文件。
  • 虚拟机内部比如Spark集群的Worker,Excutor,Driver分配也要留有余量,别OOM或者大量GC。
  • 注意CPU内核总数,不要用超线程的线程数来计算内核数。

(2.2)磁盘同样重要

正式生产环境通常是磁盘阵列,或者划分好的单独存储。
测试环境中我们的多个虚拟机最好是能放在不同的物理磁盘上,有条件的尽量用SSD。如果几个虚拟机放在同一个HDD上,那么瓶颈就卡在磁盘上,效率怎么都高不了。

(因为其它工作,这里停了几个月没写,都快全忘了T_T)

(2.3)虚拟机管理

如果我们用VMWare,并且有好几台实体的服务器,那么一定要用远程管理(共享虚拟机)。
就我实际情况来说,实体的服务器资源不够,当别的项目需要用到服务器的时候,需要关闭全部虚拟机。那么在一个界面开关全部虚拟机是最方便的。

同时远程管理虚拟机,也避免了SSH出现问题连不上,远程桌面需要和别人抢着用的尴尬情况。
在这里插入图片描述
同样用关闭虚拟机当然也是批量用脚本最方便:

#关机脚本文件
ssh -t shionlnx "echo \"密码\" | sudo -S shutdown -h now"
ssh -t shion1 "echo \"密码\" | sudo -S shutdown -h now"
ssh -t ac1 "echo \"密码\" | sudo -S shutdown -h now"
ssh -t ac2 "echo \"密码\" | sudo -S shutdown -h now"
ssh -t ad1 "echo \"密码\" | sudo -S shutdown -h now"
ssh -t ad2 "echo \"密码\" | sudo -S shutdown -h now"

(2.4)网络

用千兆有线局域网,不要用WIFI!!!
用千兆有线局域网,不要用WIFI!!!
用千兆有线局域网,不要用WIFI!!!

(2.5)主机时间

如果多台主机的时间相差较大,则HDFS和HBase会有问题。
我们可以用VMware Tool设置虚拟机时间与物理计算机同步(物理机用windows自动时间同步)。

也可以用ntp同步时间(或设置成启动就运行的服务)

$Shion@ad2 ~> sudo yum install ntp
$Shion@ad2 ~> sudo ntpdate ntp1.aliyun.com

或者临时手动设置一下时间,并和BIOS同步。

$Shion@ad2 ~> date -s 时间
$Shion@ad2 ~> sudo hwclock -w

(三)HBase

在这里插入图片描述
HBase™ :是基于Hadoop的数据库, 一个分布式可扩展的大数据存储。
以前我们把文件存储在磁盘上,需要管理和查询的数据存储在Oracle数据库中。
当数据达到一定规模的时候文件我们改放入HDFS,而数据库则需要使用非关系型数据库。

当然,如果传统关系型数据库比如Oracle,在容量和性能各方面要求都OK情况下,那就用Oracle吧。

(3.1)HBase安装/配置/启动

【1】下载安装包
从官网下载HBase 1.4的最新安装包。

【2】解压到你想放置的目录
……略……

【3】配置环境
先配置HADOOP_HOME:

$ sudo vim /etc/profile ——加入:

#HBase
export  HBASE_HOME=/home/Shion/hbase
export  PATH=$HBASE_HOME/bin:$PATH

进入{HBASE_HOME}/conf,配置hbase-site.xml:

<configuration>
        <property>
        <name>hbase.rootdir</name> <!-- hbase存放数据目录 -->
        <value>hdfs://vm201:9000/opt/hbase/hbase_db</value>
        </property>
        <property>
                <name>hbase.cluster.distributed</name> <!-- 是否分布式部署 -->
                <value>true</value>
        </property>
        <property>
            <name>hbase.zookeeper.quorum</name>
            <value>vm201,vm202</value>
        </property>    
        <property>
                <name>hbase.unsafe.stream.capability.enforce</name>
                <value>false</value>
        </property>
        <property>
        <name>hbase.zookeeper.property.dataDir</name>
        <value>/home/Shion/hbase/zookeeper</value>
        </property>
        <property>
                <name>hbase.replication</name>
                <value>false</value>
        </property>
        <property>
                <name>hbase.backup.enable</name>
                <value>false</value>
        </property>
</configuration>

以及配置regionservers,backup-masters,两个文件。

【4】从Master服务器整体拷贝hbase目录到各个RegionServer服务器
……略……

【5】在指定hbase.zookeeper.quorum的服务器上配置ZK

1)默认情况下HBASE使用内置的zookeeper,所以需要如下配置:
新建 {HBASE_HOME}/zookeeper/myid 文件。
文件内容,第一台服务器填写0,第二台填写1,如果有更多则依次填写不同的ID。

2)如果不想用内置的zookeeper则需要配置hbase-env.sh:

export HBASE_MANAGES_ZK=false

【6】启动/停止HBase
之前忘写了,还是需要先启动再使用的,呵呵哒……

#启动
$Shion@shionlnx ~> start-hbase.sh
#停止
$Shion@shionlnx ~> stop-hbase.sh

(3.2)HBase Shell

进入HBase Shell

$Shion@shionlnx ~> hbase shell
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
For Reference, please visit: http://hbase.apache.org/2.0/book.html#shell
Version 2.1.4, r5b7722f8551bca783adb36a920ca77e417ca99d1, Tue Mar 19 19:05:06 UTC 2019
Took 0.0020 seconds                                                                                                                                                                           
hbase(main):001:0> 

列举表:list

hbase(main):001:0> list
TABLE                                                                                                                   
ACTest                                                                                                                  
zg_crm                                                                                                                  
zk_cm                                                                                                                   
3 row(s)
Took 0.4271 seconds                                                                                                     
=> ["ACTest", "zg_crm", "zk_cm"]

创建表:create

#默认列簇属性
hbase(main):001:0> create 'ACTest', 'baseInfo', 'extrInfo'
Created table ACTest
Took 4.6956 seconds                                                                                                                                                                           
=> Hbase::Table - ACTest
#指定列簇属性
hbase(main):001:0> create 'ACTest', {
    
     NAME => 'baseInfo', COMPRESSION => 'GZ' }, {
    
     NAME => 'extrInfo', COMPRESSION => 'GZ'}
Created table ACTest
Took 4.4826 seconds                                                                                                                                                                           
=> Hbase::Table - ACTest

修改表:alter

#增加列簇
hbase(main):001:0> alter 'ACTest','moreInfo'
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 4.6656 seconds 
#修改列簇
hbase(main):001:0> alter 'ACTest',{
    
    NAME=>'moreInfo',COMPRESSION => 'GZ'}
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 3.8519 seconds 
#删除列簇
hbase(main):001:0> alter 'ACTest',{
    
    NAME=>'moreInfo',METHOD => 'delete'}
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 3.6241 seconds

查看表定义:describe

hbase(main):001:0> describe 'zg_crm'
Table zg_crm is ENABLED                                                                                                                                                                       
zg_crm                                                                                                                                                                                        
COLUMN FAMILIES DESCRIPTION                                                                                                                                                                   
{
    
    NAME => 'Info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NO
NE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_
BLOCKS_ON_OPEN => 'false', COMPRESSION => 'GZ', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}                                                                                                   
{
    
    NAME => 'Region', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => '
NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETC
H_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'GZ', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}                                                                                                 
2 row(s)
Took 1.2797 seconds 

启用表:enable(判断是否启用is_enabled)

hbase(main):001:0> enable 'ACTest'
Took 1.7209 seconds
hbase(main):002:0> is_disabled 'ACTest'
false                                                                                                                                                                                         
Took 0.0471 seconds                                                                                                                                                                           
=> 1
hbase(main):003:0> is_enabled 'ACTest'
true                                                                                                                                                                                          
Took 0.0239 seconds                                                                                                                                                                           
=> true

禁用表:disable(判断是否启用is_disabled)

hbase(main):001:0> disable 'ACTest'
Took 4.9442 seconds 
hbase(main):028:0> is_disabled 'ACTest'
true                                                                                                                                                                                          
Took 0.1571 seconds                                                                                                                                                                           
=> 1
hbase(main):029:0> is_enabled 'ACTest'
false                                                                                                                                                                                         
Took 1.5190 seconds                                                                                                                                                                           
=> false

删除表:drop(删除表之前需要禁用该表)

hbase(main):001:0> drop 'ACTest'
Took 0.4823 seconds 

表数据操作

#插入数据=put
hbase(main):001:0> put 'ACTest','rowkey000001','baseInfo:name','JimStone'
Took 0.6365 seconds
hbase(main):002:0> put 'ACTest','rowkey000001','baseInfo:age','42'
Took 0.0221 seconds
#取出数据=get
hbase(main):037:0> get 'ACTest','rowkey000001'
COLUMN                                           CELL                                                                                                                                         
 baseInfo:age                                    timestamp=1567581473419, value=42                                                                                                            
 baseInfo:name                                   timestamp=1567581335735, value=JimStone                                                                                                      
1 row(s)
Took 0.2473 seconds
#扫描数据=scan
hbase(main):006:0> scan 'ACTest'
ROW                             COLUMN+CELL                                                                             
 rowkey000001                   column=baseInfo:age, timestamp=1567581473419, value=42                                  
 rowkey000001                   column=baseInfo:name, timestamp=1567670052357, value=JimStone                           
 rowkey000002                   column=baseInfo:age, timestamp=1567670102703, value=18                                  
 rowkey000002                   column=baseInfo:name, timestamp=1567670091778, value=FDragon                            
2 row(s)
Took 0.0139 seconds
#删除数据=delete
#清空数据=truncate 

具体指令的各种参数关于还是通过HBase Shell里的help指令来了解吧。

查看快照:list_snapshots

hbase(main):001:0> list_snapshots
SNAPSHOT                                         TABLE + CREATION TIME                                                                                                                        
0 row(s)
Took 0.0349 seconds                                                                                                                                                                           
=> []

退出HBase Shell

hbase(main):005:0> quit
$Shion@shionlnx ~> 

(3.4)HBase on HDFS

查看HBase在HDFS中占用的空间:

$Shion@shionlnx ~> hdfs dfs -du -h /opt/hbase/hbase_db/
0       0        /opt/hbase/hbase_db/.hbck
0       0        /opt/hbase/hbase_db/.tmp
0       0        /opt/hbase/hbase_db/MasterProcWALs
0       2.3 G    /opt/hbase/hbase_db/WALs
22.1 K  66.4 K   /opt/hbase/hbase_db/archive
0       0        /opt/hbase/hbase_db/corrupt
24.2 K  70.3 K   /opt/hbase/hbase_db/data
0       0        /opt/hbase/hbase_db/default
0       0        /opt/hbase/hbase_db/hbase
42      84       /opt/hbase/hbase_db/hbase.id
7       14       /opt/hbase/hbase_db/hbase.version
0       0        /opt/hbase/hbase_db/mobdir
90.8 K  181.6 K  /opt/hbase/hbase_db/oldWALs
0       0        /opt/hbase/hbase_db/staging

查看HBase在HDFS中的机架节点块信息:

$Shion@shionlnx ~> hdfs fsck /opt/hbase/hbase_db/
Connecting to namenode via http://shionlnx:9870/fsck?ugi=Shion&path=%2Fopt%2Fhbase%2Fhbase_db
FSCK started by Shion (auth:SIMPLE) from /192.168.168.14 for path /opt/hbase/hbase_db at Tue Sep 17 15:09:12 CST 2019

Status: HEALTHY
 Number of data-nodes:  6
 Number of racks:               1
 Total dirs:                    347
 Total symlinks:                0

Replicated Blocks:
 Total size:    9320539815 B (Total open files size: 664 B)
 Total files:   171 (Files currently being written: 9)
 Total blocks (validated):      158 (avg. block size 58990758 B) (Total open file blocks (not validated): 8)
 Minimally replicated blocks:   158 (100.0 %)
 Over-replicated blocks:        0 (0.0 %)
 Under-replicated blocks:       0 (0.0 %)
 Mis-replicated blocks:         0 (0.0 %)
 Default replication factor:    2
 Average block replication:     2.0
 Missing blocks:                0
 Corrupt blocks:                0
 Missing replicas:              0 (0.0 %)

Erasure Coded Block Groups:
 Total size:    0 B
 Total files:   0
 Total block groups (validated):        0
 Minimally erasure-coded block groups:  0
 Over-erasure-coded block groups:       0
 Under-erasure-coded block groups:      0
 Unsatisfactory placement block groups: 0
 Average block group size:      0.0
 Missing block groups:          0
 Corrupt block groups:          0
 Missing internal blocks:       0
FSCK ended at Tue Sep 17 15:09:12 CST 2019 in 7 milliseconds

The filesystem under path '/opt/hbase/hbase_db' is HEALTHY

设置HBase在HDFS中的文件副本数量:

$Shion@shionlnx ~> hdfs dfs -setrep 2 /opt/hbase/hbase_db
Replication 2 set: /opt/hbase/hbase_db/MasterProcWALs/state-00000000000000000038.log
Replication 2 set: /opt/hbase/hbase_db/MasterProcWALs/state-00000000000000000039.log
Replication 2 set: /opt/hbase/hbase_db/WALs/ac1,16020,1568703613120/ac1%2C16020%2C1568703613120.1568703622640
Replication 2 set: /opt/hbase/hbase_db/WALs/ac1,16020,1568703613120/ac1%2C16020%2C1568703613120.meta.1568703621350.meta
Replication 2 set: /opt/hbase/hbase_db/WALs/ac2,16020,1568703612854/ac2%2C16020%2C1568703612854.1568703622746
Replication 2 set: /opt/hbase/hbase_db/WALs/ad1,16020,1568703609296/ad1%2C16020%2C1568703609296.1568703615447
Replication 2 set: /opt/hbase/hbase_db/WALs/ad1,16020,1568703609296/ad1%2C16020%2C1568703609296.meta.1568703925128.meta
Replication 2 set: /opt/hbase/hbase_db/WALs/ad2,16020,1568703609298/ad2%2C16020%2C1568703609298.1568703615526
Replication 2 set: /opt/hbase/hbase_db/WALs/shion1,16020,1568703611795/shion1%2C16020%2C1568703611795.1568703621964
Replication 2 set: /opt/hbase/hbase_db/WALs/shionlnx,16020,1568703612514/shionlnx%2C16020%2C1568703612514.1568703619021
Replication 2 set: /opt/hbase/hbase_db/archive/data/hbase/meta/1588230740/info/0d342d0b49454a4085c7c15a8116f78d
Replication 2 set: /opt/hbase/hbase_db/archive/data/hbase/meta/1588230740/info/3e6fdd587fe5417dbe020714088d4a30
Replication 2 set: /opt/hbase/hbase_db/archive/data/hbase/meta/1588230740/info/4af401e58a284d4f9e58113933a28816
Replication 2 set: /opt/hbase/hbase_db/data/default/SYSTEM.CATALOG/.tabledesc/.tableinfo.0000000001
......
......
......

(3.4)HBase with Java

首先我们用上一篇文章的数据:

JX_BOSS/CRM数据稽核,
文件格式为custID|custName|regNbr|regionCode
双方记录共270,000,000条,

按照这个格式,准备将数据导入HBase。
rowKey就是custID,建立两个列簇,Info中存放custName,而Region中存放regNbr|regionCode。
然后可以通过custID,对数据进行批量和单个用户的双表查询,并对比两张表用户是否一致。

入库时间比较长:
一个表大概1亿3千多万条,50多分钟才入完(回退到HBase1.4后居然只用了37分钟)。
在这里插入图片描述
单个查询示例:
在这里插入图片描述

批量查询示例:
这里如果一次查询1000条,速度也是毫秒级别的。
在这里插入图片描述

Java代码如下,首先按照这个格式定义一个User类:

//User.java
package com.ac;

public class User {
    
    
    private String CustID;
    private String CustName;
    private String RegionNBR;
    private String RegionCode;

    User(String CustID, String CustName, String RegionNBR, String RegionCode) {
    
    
        this.CustID = CustID;
        this.CustName = CustName;
        this.RegionNBR = RegionNBR;
        this.RegionCode = RegionCode;
    }

    User(){
    
    }

    String getCustID() {
    
    
        return CustID;
    }
    void setCustID(String CustID) {
    
    
        this.CustID = CustID;
    }

    String getCustName() {
    
    
        return CustName;
    }
    void setCustName(String CustName) {
    
    
        this.CustName = CustName;
    }

    String getRegionNBR() {
    
    
        return RegionNBR;
    }
    void setRegionNBR(String RegionNBR) {
    
    
        this.RegionNBR = RegionNBR;
    }

    String getRegionCode() {
    
    
        return RegionCode;
    }
    void setRegionCode(String RegionCode) {
    
    this.RegionCode = RegionCode;
    }

    @Override
    public String toString() {
    
    
        return "Cust{" +
                "CustID='" + CustID + '\'' +
                ", CustName='" + CustName + '\'' +
                ", RegionNBR='" + RegionNBR + '\'' +
                ", RegionCode='" + RegionCode + '\'' +
                '}';
    }
}

然后定义一个HBase的操作类,由于开始用的HBase2.1.6,后来用1.4.10所以代码有些变化注释掉了。

//HBaseCustAC.java
package com.ac;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.io.compress.Compression;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

class HBaseCustAC {
    
    

    private Admin admin;
    private Connection conn;

    HBaseCustAC(String HBMaster,String quorum,String ZKPort) throws Exception {
    
    
        conn=initHbase(HBMaster,quorum,ZKPort);
    }

    //连接集群
    private Connection initHbase(String HBMaster,String quorum,String ZKPort) throws IOException {
    
    
        Configuration configuration = HBaseConfiguration.create();
        configuration.set("hbase.zookeeper.property.clientPort", ZKPort);
        configuration.set("hbase.zookeeper.quorum", quorum);
        configuration.set("hbase.master", HBMaster);
        return ConnectionFactory.createConnection(configuration);
    }

    //创建表
    void createTable(String tableNmae, String[] cols) throws IOException {
    
    
        TableName tableName = TableName.valueOf(tableNmae);
        admin = conn.getAdmin();
        if (admin.tableExists(tableName)) {
    
    
            System.out.println("表已存在!");
        } else {
    
    
            //TableName tName = TableName.valueOf(tableNmae);//定义表名
            //TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(tName);
            HTableDescriptor tableDescriptor = new HTableDescriptor(tableName);
            for (String col : cols) {
    
    
                /*ColumnFamilyDescriptor family = ColumnFamilyDescriptorBuilder
                        .newBuilder(Bytes.toBytes(col))
                        .setCompressionType(Compression.Algorithm.GZ)//设置压缩格式
                        .build();//构建列族对象
                tableDescriptor.setColumnFamily(family);//设置列族*/
                HColumnDescriptor ColumnDescriptor = new HColumnDescriptor(col);
                ColumnDescriptor.setCompressionType(Compression.Algorithm.GZ);
                tableDescriptor.addFamily(ColumnDescriptor);
            }
            admin.createTable(tableDescriptor);//创建表
        }
    }
    //删除表
    void deleteTable(String tableName){
    
    
        try {
    
    
            TableName tablename = TableName.valueOf(tableName);
            admin = conn.getAdmin();
            admin.disableTable(tablename);
            admin.deleteTable(tablename);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    //判断表存在
    boolean isTableExists(String tableName) throws IOException {
    
    
        TableName tablename = TableName.valueOf(tableName);
        admin = conn.getAdmin();
        return admin.tableExists(tablename);
    }

    //插入数据(批量)
    void insertData(String tableName, List<User> users) throws IOException {
    
    
        TableName atablename = TableName.valueOf(tableName);
        Table table = conn.getTable(atablename);
        List<Put> puts= new ArrayList<>();
        for(User user: users) {
    
    
            Put put = new Put((user.getCustID()).getBytes());
            put.addColumn("INFO".getBytes(), "CUSTNAME".getBytes(), user.getCustName().getBytes());
            put.addColumn("REGION".getBytes(), "REGIONNBR".getBytes(), user.getRegionNBR().getBytes());
            put.addColumn("REGION".getBytes(), "REGIONCODE".getBytes(), user.getRegionCode().getBytes());
            puts.add(put);
        }
        table.put(puts);
    }
    //插入数据
    void insertData(String tableName, User user) throws IOException {
    
    
        TableName atablename = TableName.valueOf(tableName);
        Table table = conn.getTable(atablename);
        Put put = new Put((user.getCustID()).getBytes());
        //参数:1.列族名  2.列名  3.值
        put.addColumn("INFO".getBytes(), "CUSTNAME".getBytes(), user.getCustName().getBytes()) ;
        put.addColumn("REGION".getBytes(), "REGIONNBR".getBytes(), user.getRegionNBR().getBytes()) ;
        put.addColumn("REGION".getBytes(), "REGIONCODE".getBytes(), user.getRegionCode().getBytes()) ;
        table.put(put);
    }

    //根据rowKey前缀进行批量查询
    List<User> getDataBatch(String tableName, String filterStr, int limit){
    
    
        List<User> users= new ArrayList<>();
        try {
    
    
            Table table= conn.getTable(TableName.valueOf(tableName));
            Scan scan = new Scan();
            scan.setRowPrefixFilter(filterStr.getBytes());
            scan.setLimit(limit);
            ResultScanner resutScanner = table.getScanner(scan);
            for(Result result: resutScanner){
    
    
                User user = new User();
                user.setCustID(Bytes.toString(result.getRow()));
                for (Cell cell : result.rawCells()){
    
    
                    String colName = Bytes.toString(cell.getQualifierArray(),cell.getQualifierOffset(),cell.getQualifierLength());
                    String value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
                    switch (colName) {
    
    
                        case "CUSTNAME":
                            user.setCustName(value);
                            break;
                        case "REGIONNBR":
                            user.setRegionNBR(value);
                            break;
                        case "REGIONCODE":
                            user.setRegionCode(value);
                            break;
                    }
                }
                users.add(user);
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        return users;
    }

    //根据rowKey进行查询
    User getDataByRowKey(String tableName, String rowKey) throws IOException {
    
    
        Table table = conn.getTable(TableName.valueOf(tableName));
        Get get = new Get(rowKey.getBytes());
        User user = new User();
        user.setCustID(rowKey);
        //先判断是否有此条数据
        if(!get.isCheckExistenceOnly()){
    
    
            Result result = table.get(get);
            for (Cell cell : result.rawCells()){
    
    
                String colName = Bytes.toString(cell.getQualifierArray(),cell.getQualifierOffset(),cell.getQualifierLength());
                String value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
                switch (colName) {
    
    
                    case "CUSTNAME":
                        user.setCustName(value);
                        break;
                    case "REGIONNBR":
                        user.setRegionNBR(value);
                        break;
                    case "REGIONCODE":
                        user.setRegionCode(value);
                        break;
                }
            }
        }
        return user;
    }

    //其它的方法暂时省略
    //......
    //......
}

最后是主程序:
分别从zg.crm_customer.txt,zk.cm_customer.txt文件中(本来应该从HDFS中读取,无奈测试空间不够),
读取4个字段内容存入HBase的zg_crm表,和zk_cm表中。
以及单个用户和批量的查询两张表,并在一列中比较展示。

package com.ac;
import org.joda.time.DateTime;
import org.joda.time.Period;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class  HBaseTest2 {
    
    
    private static HBaseCustAC MyTestH;
    private static final int DBImpBatchSize=100000;

    public static void main(String[] args){
    
    
        try {
    
    
            System.out.println("\n********************* \033[01;36mHBase JX BOSSvsCRM Test 1.0\033[00m *********************");

            //MyTestH = new HBaseCustAC("Shionlnx:16000","Shionlnx,Shion1","2181");
            if ((args.length >= 4) && (args[3].toUpperCase().equals("IMP"))) {
    
    
                MyTestH = new HBaseCustAC(args[0],args[1],args[2]);
                MyImp();
            }
            else if ((args.length >= 5) && (args[3].toUpperCase().equals("GET"))) {
    
    
                MyTestH = new HBaseCustAC(args[0],args[1],args[2]);
                MyGet(args[4]);
            }
            else if ((args.length == 5) && (args[3].toUpperCase().equals("GETS"))) {
    
    
                MyTestH = new HBaseCustAC(args[0],args[1],args[2]);
                MyGets(args[4]);
            }
            else if ((args.length == 6) && (args[3].toUpperCase().equals("GETS"))) {
    
    
                MyTestH = new HBaseCustAC(args[0],args[1],args[2]);
                MyGets(args[4],Integer.parseInt(args[5]));
            }
            else{
    
    
                System.err.println("Usage: HBaseTest2 <HBMaster> <QuoruM> <ZKPort> <Method> [...]");
                System.err.println(" HBaseTest2 <HBMaster> <QuoruM> <ZKPort> <imp> : Import ZG_CRM and ZK_CM data to table");
                System.err.println(" HBaseTest2 <HBMaster> <QuoruM> <ZKPort> <get> <CustID> : Query one cust");
                System.err.println(" HBaseTest2 <HBMaster> <QuoruM> <ZKPort> <gets> <CustIDPrefix> [Limit] : Query custs by prefix and a number limit(Default 100,Max 1000).");
                System.exit(1);
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
    private static void MyGets(String CustID) {
    
    
        MyGets(CustID,100);
    }
    private static void MyGets(String CustID, int limit) {
    
    
        if (limit>1000) limit=1000;
        DateTime dt1=new DateTime();
        System.out.println(/*dt1.toString("[yyyy-MM-dd HH:mm:ss.SSS] ") +*/"批量前缀: "+CustID);
        List<User> usersg = MyTestH.getDataBatch("ZG_CRM",CustID,limit);
        DateTime dt2=new DateTime();
        List<User> usersk = MyTestH.getDataBatch("ZK_CM",CustID,limit);
        DateTime dt3=new DateTime();

        HashMap<String, User[]> CustMarkList = new HashMap<>();
        for (User user:usersg){
    
    
            CustMarkList.put(user.getCustID(),new User[]{
    
    user,null});
        }
        for (User user:usersk){
    
    
            if (CustMarkList.containsKey(user.getCustID())) {
    
    
                User[] entry;
                entry=CustMarkList.get(user.getCustID());
                entry[1]=user;
                CustMarkList.replace(user.getCustID(),entry);
            }
            else
                CustMarkList.put(user.getCustID(),new User[]{
    
    null,user});
        }
        System.out.println("*********************"+"\t"+String.format("%-20s", "***ZG_CRM***")+"\t*\t"+String.format("%-20s", "***ZK_CM***"));
        int aSeq=0;
        for (Map.Entry<String, User[]> entry : CustMarkList.entrySet()) {
    
    
            aSeq++;
            String key = entry.getKey();
            User[] Value = entry.getValue();
            String ColorCode;
            User user= new User();
            if (Value[0]!=null)
                user=Value[0];
            User user1= new User();
            if (Value[1]!=null)
                user1=Value[1];
            System.out.println("*********************");
            System.out.println("CustID:           "+"\t"+key+" <结果序号:"+aSeq+">");
            if (user.getCustName()==null || !user.getCustName().equals(user1.getCustName()))
                ColorCode="\033[01;31m";
            else
                ColorCode="\033[00m";
            System.out.println(ColorCode+"Info:CustName:    \033[00m"+"\t"+String.format("%-"+(20-GetChineseCount(user.getCustName()))+"s",user.getCustName())+"\t|\t"+user1.getCustName());
            if (user.getRegionNBR()==null || !user.getRegionNBR().equals(user1.getRegionNBR()))
                ColorCode="\033[01;31m";
            else
                ColorCode="\033[00m";
            System.out.println(ColorCode+"Region:RegionNBR: \033[00m"+"\t"+String.format("%-"+(20-GetChineseCount(user.getRegionNBR()))+"s",user.getRegionNBR())+"\t|\t"+user1.getRegionNBR());
            if (user.getRegionCode()==null || !user.getRegionCode().equals(user1.getRegionCode()))
                ColorCode="\033[01;31m";
            else
                ColorCode="\033[00m";
            System.out.println(ColorCode+"Region:RegionCode:\033[00m"+"\t"+String.format("%-"+(20-GetChineseCount(user.getRegionCode()))+"s",user.getRegionCode())+"\t|\t"+user1.getRegionCode());

        }
        Period period = new Period(dt1, dt2);
        Period period2 = new Period(dt2, dt3);
        System.out.println("*********************");
        System.out.println("耗时:               \t"+String.format("%-18s",period.toStandardDuration().getMillis()+"毫秒")+"\t|\t"+String.format("%-18s",period2.toStandardDuration().getMillis()+"毫秒"));
        System.out.println();
    }
    private static void MyGet(String CustID) throws Exception {
    
    
        DateTime dt1=new DateTime();
        System.out.println(/*dt1.toString("[yyyy-MM-dd HH:mm:ss.SSS] ") +*/"查询: "+CustID);
        User user = MyTestH.getDataByRowKey("ZG_CRM",CustID);
        DateTime dt2=new DateTime();
        User user1 = MyTestH.getDataByRowKey("ZK_CM",CustID);
        DateTime dt3=new DateTime();

        System.out.println("*********************"+"\t"+String.format("%-20s", "***ZG_CRM***")+"\t*\t"+String.format("%-20s", "***ZK_CM***"));
        String ColorCode;
        if (user.getCustName()==null || !user.getCustName().equals(user1.getCustName()))
            ColorCode="\033[01;31m";
        else
            ColorCode="\033[00m";
        System.out.println(ColorCode+"Info:CustName:    \033[00m"+"\t"+String.format("%-"+(20-GetChineseCount(user.getCustName()))+"s",user.getCustName())+"\t|\t"+user1.getCustName());
        if (user.getRegionNBR()==null || !user.getRegionNBR().equals(user1.getRegionNBR()))
            ColorCode="\033[01;31m";
        else
            ColorCode="\033[00m";
        System.out.println(ColorCode+"Region:RegionNBR: \033[00m"+"\t"+String.format("%-"+(20-GetChineseCount(user.getRegionNBR()))+"s",user.getRegionNBR())+"\t|\t"+user1.getRegionNBR());
        if (user.getRegionCode()==null || !user.getRegionCode().equals(user1.getRegionCode()))
            ColorCode="\033[01;31m";
        else
            ColorCode="\033[00m";
        System.out.println(ColorCode+"Region:RegionCode:\033[00m"+"\t"+String.format("%-"+(20-GetChineseCount(user.getRegionCode()))+"s",user.getRegionCode())+"\t|\t"+user1.getRegionCode());

        Period period = new Period(dt1, dt2);
        Period period2 = new Period(dt2, dt3);
        System.out.println("*********************");
        System.out.println("耗时:               \t"+String.format("%-18s",period.toStandardDuration().getMillis()+"毫秒")+"\t|\t"+String.format("%-18s",period2.toStandardDuration().getMillis()+"毫秒"));
        System.out.println();

    }

    private static int GetChineseCount(String str){
    
    
        int count=0;
        if (str!=null) {
    
    
            char[] c = str.toCharArray();
            for (char value : c) {
    
    
                String len = Integer.toBinaryString(value);
                if (len.length() > 8)
                    count++;
            }
        }
        return count;
    }

    private static void MyImp() throws Exception {
    
    
        String line;
        String[] arrs;
        int aCount = 0;
        List<User> users = new ArrayList<>();
        /*if(MyTestH.isTableExists("ZG_CRM")) {
            System.out.println((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] ") + "Deleting ZG_CRM...");
            MyTestH.deleteTable("ZG_CRM");
        }*/
        if (!MyTestH.isTableExists("ZG_CRM")) {
    
    
            System.out.println((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] ") + "Creating ZG_CRM...");
            MyTestH.createTable("ZG_CRM", new String[]{
    
    "INFO", "REGION"});
        }
        System.out.println((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] ") + "importing...");
        System.out.print((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] "));
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("/mnt/hgfs/ShareFolder/br1/zg.crm_customer.txt"), Charset.forName("GBK")));
        while ((line = br.readLine()) != null) {
    
    
            arrs = line.split("\\|", -1);
            if (arrs.length < 4) continue;
            if (arrs[0].trim().isEmpty()) continue;
            User user = new User(arrs[0], arrs[1], arrs[2], arrs[3]);
            users.add(user);
            aCount++;
            if ((aCount % DBImpBatchSize) == 0) {
    
    
                MyTestH.insertData("ZG_CRM", users);
                users.clear();
                if (aCount % 10000000 == 0) {
    
    
                    System.out.println("用户ZG_CRM:" + arrs[0] + " :" + aCount);
                    System.out.print((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] "));
                }
                System.out.print('.');
            }
        }
        if (users.size() > 0) {
    
    
            MyTestH.insertData("ZG_CRM", users);
            users.clear();
        }
        System.out.println("\nOK.记录数:" + aCount);
        br.close();

        aCount = 0;
        /*if(MyTestH.isTableExists("ZK_CM")) {
            System.out.println((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] ") + "Deleting ZK_CM...");
            MyTestH.deleteTable("ZK_CM");
        }*/
        if (!MyTestH.isTableExists("ZK_CM")) {
    
    
            System.out.println((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] ") + "Creating ZK_CM...");
            MyTestH.createTable("ZK_CM", new String[]{
    
    "INFO", "REGION"});
        }
        System.out.println((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] ") + "importing...");
        System.out.print((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] "));
        br = new BufferedReader(new InputStreamReader(new FileInputStream("/mnt/hgfs/ShareFolder/br1/zk.cm_customer.txt"), Charset.forName("GBK")));
        while ((line = br.readLine()) != null) {
    
    
            arrs = line.split("\\|", -1);
            if (arrs.length < 4) continue;
            if (arrs[0].trim().isEmpty()) continue;
            User user = new User(arrs[0], arrs[1], arrs[2], arrs[3]);
            users.add(user);
            aCount++;
            if ((aCount % DBImpBatchSize) == 0) {
    
    
                MyTestH.insertData("ZK_CM", users);
                users.clear();
                if (aCount % 10000000 == 0) {
    
    
                    System.out.println("用户ZK_CM:" + arrs[0] + " :" + aCount);
                    System.out.print((new DateTime()).toString("[yyyy-MM-dd HH:mm:ss] "));
                }
                System.out.print('.');
            }
        }
        if (users.size() > 0) {
    
    
            MyTestH.insertData("ZK_CM", users);
            users.clear();
        }
        System.out.println("\nOK.记录数:" + aCount);
        br.close();
    }
}

pom.xml
这里有个小插曲,hbase-server 2.1.4依赖的org.glassfish.web/javax.servlet.jsp,是一个非正式的版本没有pom无法自动下载。只好手动排除它,并且写明需要它的一个新版(mvn中央库中查到的)。
后来2.1.6貌似不需要这么做,最后又用了1.4.10版。(代码懒得改了)

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

    <groupId>com.ac</groupId>
    <artifactId>HBaseTest2</artifactId>
    <version>5.01</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>2.1.4</version>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish.web</groupId>
                    <artifactId>javax.servlet.jsp</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>javax.servlet.jsp</artifactId>
            <version>2.3.4</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-common</artifactId>
            <version>2.1.4</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.10.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.2</version>
                <configuration>
                    <archive>
                        <manifest>                               
                            <mainClass>com.ac.HBaseTest2</mainClass>                               
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.1.1</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.ac.HBaseTest2</mainClass>
                        </manifest>
                        <manifestEntries>
                            <Class-Path>.</Class-Path>
                        </manifestEntries>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

(四)Spark访问HBase

(4.1)sparkContext.newAPIHadoopRDD

上一篇文章:《从零开始学习大数据平台(Episode 1)》中,
我们用Spark从HDFS中读取文件的代码片段:

        JavaRDD<Row> dfboss = spark.read()
                .option("sep", "|")
                .option("encoding", "GBK")
                .csv(aPath + "zk.cm_customer*.txt")
                .javaRDD();
        JavaPairRDD<String, String[]> rddboss = dfboss
                .mapToPair(s ->
                        new Tuple2<>(s.getString(0), new String[]{
    
    s.getString(1), s.getString(2), s.getString(3)})
                )
                .reduceByKey((i1, i2) -> i1);

        JavaRDD<Row> dfhss = spark.read()
                .option("sep", "|")
                .option("encoding", "GBK")
                .csv(aPath + "zg.crm_customer*.txt")
                .javaRDD();
        JavaPairRDD<String, String[]> rddhss = dfhss
                .mapToPair(s ->
                        new Tuple2<>(s.getString(0), new String[]{
    
    s.getString(1), s.getString(2), s.getString(3)})
                )
                .reduceByKey((i1, i2) -> i1);

上面这段代码稍作修改就可以从HBase中读取表数据了:
不过读取表比读取文件慢了很多,主要是第一步读慢,后面对RDD的操作是完全一样的。

        Configuration confboss = HBaseConfiguration.create();
        confboss.set("hbase.master", args[0]/*"VM201:16000"*/);
        confboss.set("hbase.zookeeper.quorum", args[1]/*"VM201,VM202"*/);
        confboss.set("hbase.zookeeper.property.clientPort", args[2]/*"2181"*/);
        confboss.set(TableInputFormat.INPUT_TABLE, "ZK_CM");
        RDD<Tuple2<ImmutableBytesWritable, Result>> dfboss = spark.sparkContext().newAPIHadoopRDD(confboss, TableInputFormat.class, ImmutableBytesWritable.class, Result.class);
        JavaPairRDD<String, String[]> rddboss = dfboss.toJavaRDD()
                .mapToPair(s ->
                        new Tuple2<>(Bytes.toString(s._2.getRow()), new String[]{
    
    
                                Bytes.toString(s._2.getValue(Bytes.toBytes("INFO"),Bytes.toBytes("CUSTNAME"))),
                                Bytes.toString(s._2.getValue(Bytes.toBytes("REGION"),Bytes.toBytes("REGIONNBR"))),
                                Bytes.toString(s._2.getValue(Bytes.toBytes("REGION"),Bytes.toBytes("REGIONCODE")))}
                                )
                )
                .reduceByKey((i1, i2) -> i1);


        Configuration confhss = HBaseConfiguration.create();
        confhss.set("hbase.master", args[0]/*"VM201:16000"*/);
        confhss.set("hbase.zookeeper.quorum", args[1]/*"VM201,VM202"*/);
        confhss.set("hbase.zookeeper.property.clientPort", args[2]/*"2181"*/);
        confhss.set(TableInputFormat.INPUT_TABLE, "ZG_CRM");
        RDD<Tuple2<ImmutableBytesWritable, Result>> dfhss = spark.sparkContext().newAPIHadoopRDD(confhss, TableInputFormat.class, ImmutableBytesWritable.class, Result.class);
        JavaPairRDD<String, String[]> rddhss = dfhss.toJavaRDD()
                .mapToPair(s ->
                        new Tuple2<>(Bytes.toString(s._2.getRow()), new String[]{
    
    
                                Bytes.toString(s._2.getValue(Bytes.toBytes("INFO"),Bytes.toBytes("CUSTNAME"))),
                                Bytes.toString(s._2.getValue(Bytes.toBytes("REGION"),Bytes.toBytes("REGIONNBR"))),
                                Bytes.toString(s._2.getValue(Bytes.toBytes("REGION"),Bytes.toBytes("REGIONCODE")))}
                        )
                )
                .reduceByKey((i1, i2) -> i1);

(4.2)Jean Grey:Dark Phoenix

呃,我的意思是Apache Phoenix,也可以访问HBase。
在这里插入图片描述
Apache Phoenix :为Hadoop提供联机事务处理和操作与分析。

它可以提供标准的JDBC方式访问HBase,听起来非常的诱人。

下载很简单,只需要把包中phoenix-[version]-server.jar拷贝到HBase的lib目录,重启HBase就OK。
所以步骤也略过

最开始感觉这玩意儿有点麻烦……首先HBase的表是区分大小写的,而Phoenix默认全转为大写。所以为了方便只好把表和字段调整为全部大写,否则就需要在表名字段名上加双引号(Shell和程序中都需要)。理论上HBase表不能更名但是可以用快照的方式改。但是字段就没办法了,为了以后不总是打双引号,只好重建了表重新导入数据。

其次到目前为止它的最高版本5.0.0只支持到HBase2.0(而且很久没更新了),目前HBase是2.1.6。然后HBase的hbck工具呢,在2.0+版本上一堆fix命令都不支持。所以我也只好回退到了HBase1.4……不过Phoenix对HBase1.4的支持倒是最近都更新。

那么还是之前Spark从HDFS中读取文件的代码片段,再稍微修改为:

        JavaRDD<Row> dfboss = spark.read()
                .format("org.apache.phoenix.spark")
                .option("zkUrl", args[0]/*"VM201,VM202:2181"*/)
                .option("table","ZK_CM")
                .load().javaRDD();
        JavaPairRDD<String, String[]> rddboss = dfboss
                .mapToPair(s ->
                        new Tuple2<>(s.getString(0), new String[]{
    
    s.getString(1), s.getString(3), s.getString(2)})
                )
                .reduceByKey((i1, i2) -> i1);


        JavaRDD<Row> dfhss = spark.read()
                .format("org.apache.phoenix.spark")
                .option("zkUrl", args[0]/*"VM201,VM202:2181"*/)
                .option("table","ZG_CRM")
                .load().javaRDD();
        JavaPairRDD<String, String[]> rddhss = dfhss
                .mapToPair(s ->
                        new Tuple2<>(s.getString(0), new String[]{
    
    s.getString(1), s.getString(3), s.getString(2)})
                )
                .reduceByKey((i1, i2) -> i1);

我们可以通过phoenix,用SQL的形式在HBASE中创建和使用表。
但是phoenix不能直接读取hbase中已经创建的表,这时候需要创建关联。
第一个办法的建表(为什么大家都可以,到我这里就不行???),
第二个办法建视图(视图就只能查询了)。

1)在phoenix中添加同名表即可映射到hbase的表

--这个办法居然不行,试了几次。只能读到Rowkey,无法读取到表中其它字段的数据。
create table zg_crm(ROW varchar primary key, Info.CustName varchar , Region.RegionCode varchar, Region.RegionNBR varchar);

2)创建一个phoenix的视图

--测试OK
create view zg_crm(id varchar primary key, INFO.CUSTNAME varchar , REGION.REGIONCODE varchar, REGION.REGIONNBR varchar)as select * from zg_crm;

视图创建好后,上面的代码就可以用Phoenix访问我们之前创建好的HBase表ZG_CRM了。
同时由于HBase只能通过Rowkey索引,所以虽然可以用SQL但是非主键字段的条件会很慢。其实40多秒应该不算慢,毕竟记录总数有135,000,000条。而通过Rowkey查询则是30毫秒就搞定。

$Shion@vm201 bin> sqlline.py vm201,vm202:2181
Setting property: [incremental, false]
Setting property: [isolation, TRANSACTION_READ_COMMITTED]
issuing: !connect jdbc:phoenix:vm201,vm202:2181 none none org.apache.phoenix.jdbc.PhoenixDriver
Connecting to jdbc:phoenix:vm201,vm202:2181
Connected to: Phoenix (version 4.14)
Driver: PhoenixEmbeddedDriver (version 4.14)
Autocommit status: true
Transaction isolation: TRANSACTION_READ_COMMITTED
Building list of tables and columns for tab-completion (set fastconnect to true to skip)...
141/141 (100%) Done
Done
sqlline version 1.2.0
#----------------------分割线-----------------------
0: jdbc:phoenix:vm201,vm202:2181>  select * from ZG_CRM where id = '400083710838';
+---------------+-----------+-------------+---------------------+
|      ID       | CUSTNAME  | REGIONCODE  |      REGIONNBR      |
+---------------+-----------+-------------+---------------------+
| 400083710838  | 谢X娟      | 792         | 350721198909254923  |
+---------------+-----------+-------------+---------------------+
1 row selected (0.031 seconds)
0: jdbc:phoenix:vm201,vm202:2181>  select * from ZG_CRM where regionnbr = '350721198909254923';
+---------------+-----------+-------------+---------------------+
|      ID       | CUSTNAME  | REGIONCODE  |      REGIONNBR      |
+---------------+-----------+-------------+---------------------+
| 400038593889  | 谢X娟       | 792         | 350721198909254923  |
| 400083710838  | 谢X娟       | 792         | 350721198909254923  |
| 400111967614  | XX学院      | 792         | 350721198909254923  |
| 400111967617  | XX学院      | 792         | 350721198909254923  |
+---------------+-----------+-------------+---------------------+
4 rows selected (49.569 seconds)

又试了试count(1),到千万级别的时候,差不多半分钟才返回了。

0: jdbc:phoenix:vm201,vm202:2181> select count(1) from ZG_CRM where id like '400083710%';
+-----------+
| COUNT(1)  |
+-----------+
| 973       |
+-----------+
1 row selected (0.086 seconds)
0: jdbc:phoenix:vm201,vm202:2181> select count(1) from ZG_CRM where id like '40008371%';
+-----------+
| COUNT(1)  |
+-----------+
| 9805      |
+-----------+
1 row selected (0.077 seconds)
0: jdbc:phoenix:vm201,vm202:2181> select count(1) from ZG_CRM where id like '4000837%';
+-----------+
| COUNT(1)  |
+-----------+
| 98218     |
+-----------+
1 row selected (0.209 seconds)
0: jdbc:phoenix:vm201,vm202:2181> select count(1) from ZG_CRM where id like '400083%';
+-----------+
| COUNT(1)  |
+-----------+
| 991234    |
+-----------+
1 row selected (1.655 seconds)
0: jdbc:phoenix:vm201,vm202:2181> select count(1) from ZG_CRM where id like '40008%';
+-----------+
| COUNT(1)  |
+-----------+
| 9957254   |
+-----------+
1 row selected (27.923 seconds)

(4.3)性能对比

用新的机器和虚拟机集群测试了一下:
双方1.35亿x2数据条数,输出两个存在稽核项,三个差异稽核项。
下面是HBase用GZ方式压缩数据的结果,相对比较满,但是数据表占用空间最小。后来用LZ4压缩就快了很多,再测试Snappy压缩率比LZ4更高一点,速度也差不了太多(没有明显规律,可能是其他因素干扰)。
在这里插入图片描述

程序类型 处理时间 单位
Windows 单机单进程 30+ 分钟
Spark + HDFS 8.2 分钟
Spark via newAPIHadoopRDD + HBase 25(GZ),15(LZ4),17(Snappy) 分钟
Spark via Phoenix + HBase 21(GZ),17(LZ4),16(Snappy) 分钟

从HDFS读文件到DataFrame的最开始阶段非常快,基本一闪而过,而从HBase读就慢了很多。

#sparkContext.newAPIHadoopRDD方式,最开始job的两个stage,划分的tasks很少。2+4=6个RegionServer(应该不是)
$Shion@shionlnx ~> spark-submit --class com.ac.Word2AuditTest --master spark://shionlnx:7077 --deploy-mode client --conf spark.master.rest.enabled=true hdfs://shionlnx:9000/bin/Word2AuditTest-1.3-jar-with-dependencies.jar shionlnx:16000 shionlnx,shion1 2181 hdfs://shionlnx:9000 /output/ 600
*** AuditJava JX BOSSvsCRM HBASE 1.3 ***
[Stage 0:>                  (0 + 4) / 4][Stage 1:>                  (0 + 2) / 2]
#通过Phoenix,则最开始job的两个stage,划分的tasks多一些。可以10个worker同时跑。
#但是中途容易随机的出错,失去某个worker的通信。
$Shion@shionlnx ~> spark-submit --class com.ac.Word2AuditTest --master spark://shionlnx:7077 --deploy-mode client --conf spark.master.rest.enabled=true hdfs://shionlnx:9000/bin/Word2AuditTest-1.4-jar-with-dependencies.jar shionlnx,shion1:2181 hdfs://shionlnx:9000 /output/ 600
*** AuditJava JX BOSSvsCRM HBASE 1.4 ***
[Stage 0:>                (0 + 10) / 23][Stage 1:>                 (0 + 0) / 21]

当然,稽核结果也是一致的。

$Shion@vm201 ~> spark-submit --class com.ac.Word2AuditTest --master spark://vm201:7077 --deploy-mode client --conf spark.master.rest.enabled=true hdfs://vm201:9000/bin/Word2AuditTest-1.2.jar hdfs://vm201:9000 /dir1full/ /output/ 600
*** AuditJava JX BOSSvsCRM 1.2 ***
001: 370042
002: 114
存在并不一致: 399004
003: 189745
004: 216903
005: 54
*** Task done ***
$Shion@vm201 ~>
$Shion@vm201 ~> spark-submit --class com.ac.Word2AuditTest --master spark://vm201:7077 --deploy-mode client --conf spark.master.rest.enabled=true hdfs://vm201:9000/bin/Word2AuditTest-1.3-jar-with-dependencies.jar vm201:16000 vm201,vm202 2181 hdfs://vm201:9000 /output/ 600
*** AuditJava JX BOSSvsCRM HBASE 1.3 ***
001: 370042
002: 114
存在并不一致: 399004
003: 189745
004: 216903
005: 54
*** Task done ***
$Shion@vm201 ~>
$Shion@vm201 ~> spark-submit --class com.ac.Word2AuditTest --master spark://vm201:7077 --deploy-mode client --conf spark.master.rest.enabled=true hdfs://vm201:9000/bin/Word2AuditTest-1.4-jar-with-dependencies.jar vm201,vm202:2181 hdfs://vm201:9000 /output/ 600
*** AuditJava JX BOSSvsCRM HBASE 1.4 ***
001: 370042
002: 114
存在并不一致: 399004
003: 189745
004: 216903
005: 54
*** Task done ***
$Shion@vm201 ~>

(五)其它相关项目

(5.1)Trafodion

在这里插入图片描述
Apache Trafodion :为Hadoop数据库提供传统SQL操作。

Trafodion本是HP公司资助的一个开源项目,它提供了SQL on HBase解决方案。后来咋就送给了Apache(蛤?)。在网上讨论这个的比较少,感觉更多的人是用Phoenix。但是这个项目并没有停止,至少2019.2提供了2.3.0版本。

(5.2)Thrift

Apache Thrift 跨语言服务开发的软件框架。
HBase Thrift,提供多种语言绑定,主要是方便其它语言比如C++的开发项目访问HBase,但本质上还是Java在访问HBase。
在这里插入图片描述

(六)出现问题和解决的记录

(6.1)HBase设置压缩

由于HBase占用的空间比较厉害,所以可以对数据采用压缩。
自己测试不压缩/GZ/snappy看不出什么区别-__-:

$ hbase org.apache.hadoop.hbase.util.LoadTestTool -write 3:15:30 -num_keys 100000 -read 100:30 -num_tables 1 -data_block_encoding NONE -tn TestTable -compression XXX

下面是Google很久以前发布测试结果:

算法 压缩比例 编码速度 解码速度
GZIP 13.4% 21 MB/s 118 MB/s
LZO 20.5% 135 MB/s 410 MB/s
Zippy/Snappy 22.2% 172 MB/s 409 MB/s

自己测试用GZ没问题,用Snappy则无法Enable表。RegionServer的日志中会报错。

native snappy library not available: this version of libhadoop was built without snappy support.
…以及…
Compression algorithm ‘snappy’ previously failed test.

网上查了一下,别人是没装snappy,我这里yum install 看了下已经装了。
用官方的方法看看库,也都有。

$Shion@shionlnx ~> hbase --config ~/conf_hbase org.apache.hadoop.util.NativeLibraryChecker
2019-09-19 11:25:23,207 INFO bzip2.Bzip2Factory: Successfully loaded & initialized native-bzip2 library system-native
2019-09-19 11:25:23,210 INFO zlib.ZlibFactory: Successfully loaded & initialized native-zlib library
Native library checking:
hadoop:  true /home/Shion/hadoop/lib/native/libhadoop.so.1.0.0
zlib:    true /lib64/libz.so.1
snappy:  true /lib64/libsnappy.so.1
lz4:     true revision:10301
bzip2:   true /lib64/libbz2.so.1
openssl: true /lib64/libcrypto.so

再压缩测试一下snappy也是显示成功:

$Shion@shionlnx ~> hbase org.apache.hadoop.hbase.util.CompressionTest hdfs://shionlnx:9000/hbase snappy
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/Shion/hbase/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/Shion/hadoop/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
2019-09-19 12:07:38,227 INFO  [main] metrics.MetricRegistries: Loaded MetricRegistries class org.apache.hadoop.hbase.metrics.impl.MetricRegistriesImpl
2019-09-19 12:07:38,234 INFO  [main] hfile.CacheConfig: Created cacheConfig: CacheConfig:disabled
2019-09-19 12:07:38,383 INFO  [main] compress.CodecPool: Got brand-new compressor [.snappy]
2019-09-19 12:07:38,384 INFO  [main] compress.CodecPool: Got brand-new compressor [.snappy]
2019-09-19 12:07:38,631 INFO  [main] hfile.CacheConfig: Created cacheConfig: CacheConfig:disabled
2019-09-19 12:07:38,670 INFO  [main] compress.CodecPool: Got brand-new decompressor [.snappy]
SUCCESS

最后在官方文档中发现需要设置HBASE_LIBRARY_PATH环境变量,包含libsnappy.so库文件,和libhadoop.so库文件。
当然,最简单的办法是把这俩文件拷贝到{HBASE_HOME}/lib/native/Linux-amd64-64/目录中。然后每个RegionServer都复制一份这个目录结构。

如果不想运行时才报错,那么可以将必须支持的压缩编码写入hbase-site.xml中。

<property>
	<name>hbase.regionserver.codecs</name>
	<value>lz4,gz,snappy</value>
</property>

这样一来,RegionServer启动时就会检查这些编码方式是否支持。


在这里插入图片描述
本文为工作内容记录,会不定期的修改,
不要着急,更多的内容,待继续补充……

猜你喜欢

转载自blog.csdn.net/ddrfan/article/details/90900024