Spark读取HBase的方法

读取

利用TableInputFormat读取

	Base64.Encoder base64Encoder = Base64.getEncoder();
    String sparkMaster = "local";
    String zkQuorum = "master";
    String zkClientPort = "2181";
    String tableName = "KITTI";
    byte[] startRowKey = Bytes.toBytes("0r");
    byte[] endRowKey = Bytes.toBytes("9r");
    
	SparkSession sparkSession = SparkSession.builder().master(sparkMaster).getOrCreate();

	Configuration hconf= HBaseConfiguration.create();
	hconf.set("hbase.zookeeper.property.clientPort", zkClientPort);
	hconf.set("hbase.zookeeper.quorum",zkQuorum);

//设置读取HBase表的名称
        hconf.set(TableInputFormat.INPUT_TABLE, tableName);

        //设置读取HBase表scan的数据范围
        Scan scan = new Scan();
        scan.withStartRow(startRowKey, true);
        scan.withStopRow(endRowKey, true);
        hconf.set(TableInputFormat.SCAN, base64Encoder.encodeToString(ProtobufUtil.toScan(scan).toByteArray()));

        JavaRDD<Tuple2<ImmutableBytesWritable, Result>> hbaseRDD = sparkSession.sparkContext()
                .newAPIHadoopRDD(hconf, TableInputFormat.class, ImmutableBytesWritable.class, Result.class).toJavaRDD();
TableInputFormat原理

TableInputFormat通过hbase scan获取数据,SparkRDD采用的分区策略就是根据region的数量,决定partition的数量,hbase一个region对应一个RDD partition。

1. 数据分区数目

在这里插入图片描述

  • oneInputSplitPerRegion()方法将每一个Region作为一个数据分区InputSplit。

在这里插入图片描述

将Region转换为InputSplit时会判断Region的startEndKey和Scan 的startEndRowKey是否有重合区域,如果没有,则舍弃此Region。并且利用Region的startEndKey和Scan 的startEndRowKey的交集作为TableSplit数据的splitStart, splitStop。

  • 如果用户定义了hbase.mapreduce.tableinput.mappers.per.region参数(nSplitsPerRegion),则会调用createNInputSplitsUniform,将上述生成的每个InputSplit平均切分成nSplitsPerRegion个InputSplit。

  • 如果用户定义了hbase.mapreduce.tif.input.autobalance参数, 则会调用calculateAutoBalancedSplits将上述生成的InputSplit进行动态调整,使得每个InputSize的大小接近hbase.mapreduce.tif.ave.regionsize参数。注意,这不会改变InputSplit的数目

  1. 分区数据读取

在这里插入图片描述

使用TableRecordReader读取分区数据,并设置InputSplit的splitStart和splitEnd作为TableRecordReader起终点。TableRecordReader利用TableRecordReaderImpl实现。

在这里插入图片描述

TableRecordReaderImpl利用传入的Scan和表名,获取Hbase数据表的ResultScanner,利用ResultScanner获取该分区的数据记录。

值得一提的是HBase在进行数据scan的时候,会调用StoreFileScanner和MemstoreScanner两个扫描器。其中StoreFileScanner可通过StoreFile的索引定位到待查找范围所在的block,从而直接读取StoreFile中对应的block,而不需要遍历整个StoreFile文件。

利用TableSnapshotInputFormat读取

首先必须要为读取的HBase的数据表创建快照:

hbase shell :> snapshot ‘tableName’,‘snapshotName’

		Base64.Encoder base64Encoder = Base64.getEncoder();
        String sparkMaster = "local";
        String zkQuorum = "master";
        String zkClientPort = "2181";
        //byte[] startRowKey = Bytes.toBytes("0r");
        //byte[] endRowKey = Bytes.toBytes("9r");
        String hbaseRootDir = "hdfs://master:9000/HBase_DB";
        String snapshotRestoreDir = "hdfs://master:9000/snapshot_restore";//spark读取snapshot时,会将snapshot被复制到该文件夹,文件夹必须和hbaseRootDir处于相同的文件系统
        String snapName ="KITTI_snapshot";

        SparkSession sparkSession = SparkSession.builder().master(sparkMaster).getOrCreate();

        Configuration hconf= HBaseConfiguration.create();
        hconf.set("hbase.rootdir",hbaseRootDir);
        hconf.set("hbase.zookeeper.property.clientPort", zkClientPort);
        hconf.set("hbase.zookeeper.quorum",zkQuorum);

        //设置读取Hfile的scan的数据范围
        Scan scan = new Scan();//必须要创建Scan
       /* scan.withStartRow(startRowKey, true);
        scan.withStopRow(endRowKey, true);*/
        hconf.set(TableInputFormat.SCAN, base64Encoder.encodeToString(ProtobufUtil.toScan(scan).toByteArray()));

        //设置要读取的hbase表快照
        Job job = Job.getInstance(hconf);
        Path path = new Path(snapshotRestoreDir);
        TableSnapshotInputFormat.setInput(job, snapName, path);

        JavaRDD<Tuple2<ImmutableBytesWritable, Result>> hbaseRDD = sparkSession.sparkContext()
                .newAPIHadoopRDD(job.getConfiguration(), TableSnapshotInputFormat.class, ImmutableBytesWritable.class, Result.class).toJavaRDD();

        System.out.println("分区数目:" + hbaseRDD.partitions().size());
TableSnapshotInputFormat原理

hbase快照原理:https://www.jianshu.com/p/8d091591d872

  1. 数据分区数目

    基本和TableInputFormat相似,都是一般情况下一个region对应一个数据分区,如果定义了hbase.mapreduce.tableinput.mappers.per.region参数,则会平均切分。同样也会使用Scan范围过滤无交集的Region,以及利用Region的startEndKey和Scan 的startEndRowKey的交集作为InputSplit数据的splitStart, splitStop。

在这里插入图片描述

  1. 分区数据读取
    在这里插入图片描述

    利用ClientSideRegionScanner实现分区数据读取。利用Hbase snapshot中各Region、HFile的位置,创建RegionScanner,直接读取HDFS上该Region的HFile文件获取数据,而不是再是借用HBase的scan API获取数据(scan API利用RegionServer进程读取HFile再传回Spark Task,这里是Spark Task直接读取Hfile)。

利用Spark-Hbase-connector读取

介绍:

https://blog.cloudera.com/spark-hbase-dataframe-based-hbase-connector/

使用方法:

https://hbase.apache.org/book.html#_sparksqldataframes

Spark-Hbase-connector原理

扩展了Spark DataSource API。

hbase-spark集成应用了关键技术,如分区剪枝、列剪枝、谓词下推和数据局部性

创建org.apache.hadoop.hbase.spark.DefaultSource和HBaseRelation。

在这里插入图片描述
在这里插入图片描述

DefaultSource继承自 RelationProvider接口,实现createRelation方法,创建返回HBaseRelation对象。

HBaseRelation继承 BaseRelation类,实现PrunedFilteredScan接口,实现buildScan方法创建返回RDD<Row>。

在这里插入图片描述
在这里插入图片描述

buildScan首选将过滤条件进行谓词下推。pushDownRowKeyFilter用于过滤不在查询范围内的region。创建HBaseTableScanRDD,并将pushDownRowKeyFilter的查询范围添加到HBaseTableScanRDD的ranges变量中。

  1. 数据分区数目

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0vE7itQM-1608442784206)(C:\Users\PZX\AppData\Roaming\Typora\typora-user-images\1608437487354.png)]

    HBaseTableScanRDD的getPartitions方法首先会获取HBase表的所有region。将每个region的范围(start/endKey)与ranges求交,最后获得HBaseScanPartition。HBase一个region对应RDD一个Partition。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

​ 如果region的范围(start/endKey)与ranges中的范围无交集,则返回null,否则返回交集。从而可以过滤掉无 关的region。

  1. 分区数据读取

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4htg688a-1608442784208)(C:\Users\PZX\AppData\Roaming\Typora\typora-user-images\1608438097665.png)]

    同样是利用HBase scan API获取读取分区数据。

总结

以上三种方式Spark读取HBase得到的RDD的分区均与Hbase的Region一一对应,且都可以使用scan或者SQL where的方式,传入rowkey scan范围,过滤无关的Hbase Region和Row,减少RDD的分区数量和Row的数量

第一种使用Spark core的API 和Hbase scan API读取HBsae数据。

第二种使用Spark core的API 和Hbase snapshot、Hfile,利用Spark Task直接读取Hfile文件,减轻了对Hbase region server的负担,一定程度上提高了读取效率,不需要通过RegionServer间接获取数据。但是读取Hbase之前,需要对相关表做snapshot。

e scan API读取HBsae数据。

第二种使用Spark core的API 和Hbase snapshot、Hfile,利用Spark Task直接读取Hfile文件,减轻了对Hbase region server的负担,一定程度上提高了读取效率,不需要通过RegionServer间接获取数据。但是读取Hbase之前,需要对相关表做snapshot。

第三种使用SparkSQL DataSource API和Hbase scan API,可以使用SparkSQL的各种优化措施,如谓词下推,二进制内存优化等,效率应该要好于第一种方式。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值