HBaseテーブルのデータの読み取りと分析は、HBaseAPIを学習するための要件です。

目次

1.HBaseAPIの重要な概念

1.スキャン

 2.結果 

3.フィルターのスキャン

2.ケース分析--- HBaseAPI学習

1.需要

2.考える

 3.コード


1.HBaseAPIの重要な概念

1.スキャン

HBaseのデータテーブルは、リージョンに分割することでデータの断片化を実現します。各リージョンはRowKeyの範囲に関連付けられ、各リージョンのデータはRowKeyの辞書式順序で編成されます。

HBaseがこのタイプのクエリ「RowKey範囲を指定してその範囲内のすべてのレコードを取得する」を簡単に処理できるのはこの設計に基づいています。このタイプのクエリはHBaseでスキャンと呼ばれます。もちろん、指定しない場合は、それはいっぱいになります。テーブルスキャン、以下はクエリがRPCアクセスであり、結果セットをクライアントに返します。

1.ビルドスキャン、startRowおよびstopRowを指定します。指定されていない場合、全表スキャンが実行されます

2.ResultScannerを入手する

3.クエリ結果をトラバースします

4.ResultScannerを閉じます

 2.結果 

ScanをResultオブジェクトとしてカプセル化し、クライアントに返します。

3.フィルターのスキャン

フィルタは、スキャンの結果セットに基づいて、返されたレコードに対してより多くの条件値を設定できます。これらの条件は、RowKey、列名、または列値に関連付けることができ、複数のフィルタ条件をTogetherで組み合わせることができます。通常、一緒に組み合わせると、 FilterListである必要がありますが、一般的には推奨されません。データ漏洩のリスクがある可能性があります。

  • クライアントがRegionServerにスキャン要求を送信するたびに、データのバッチが返され(毎回取得される結果の数はキャッシュによって決定されます)、今度はそれを結果キャッシュに入れます。
  • アプリケーションがデータを読み取るたびに、ローカルの結果キャッシュからデータが取得されます。結果キャッシュ内のデータが読み取られた場合、クライアントはスキャン要求を再度RegionServerに送信して、さらにデータを取得します。

2.ケース分析--- HBaseAPI学習

1.需要

次の表のデータを分析します。定期的に報告されるデータは分単位で報告されます。minutes= 2#1#0の後の値は、分析および要約されるデータです。次に、この日の20200706のデータを要約します。データ分析を読み取るとき、タイムスタンプはLastJobTimeテーブルに保持されます。テーブルのmodifyTimeが、各分析後に記録されたタイムスタンプよりも大きい場合、新しいデータが読み取られます。

2.考える

  • 結果の結果セットをトラバースし、CFと列(c、ci、ct、および上記の他のフィールドなど)に従って、結果から決定されたフィールド値を直接取り出します
  • 不確実なフィールドは、時間に応じて動的にテーブルに書き込まれるフィールドd:1300、d:1305などです。まず、セルをトラバースし、セルに応じてQualifyを取り出し、すべてのフィールドを読み取り、解析します。 4以上の長さまで、これまでのところ、時間列に動的に書き込まれた値データを取得できます 

   幸運にもここを読むことができれば、私の考えを理解してください。上記のビジネスシナリオに入る必要はありません。

 3.コード

​
​
package com.kangll.hbaseapi

import java.util
import com.winner.utils.KerberosUtil
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.hbase.{Cell, CellUtil, CompareOperator, HBaseConfiguration, TableName}
import org.apache.hadoop.hbase.client.{Connection, ConnectionFactory, Get, Result, Scan, Table}
import org.apache.hadoop.hbase.filter.{SingleColumnValueFilter, SubstringComparator}
import org.apache.hadoop.hbase.util.Bytes
import org.apache.spark.SparkContext
import org.apache.spark.sql.SparkSession

import scala.collection.mutable.ListBuffer

/** ******************************************
 *
 * @AUTHOR kangll
 * @DATE 2020/8/11 14:47
 * @DESC:
 * *******************************************
 */
// 将表中解析出的数据封装为样例类
case class InOutDataHBaseTest(rowkey: String, channel: String, counterid: String, countertype: String, devicesn: String,
                              datatype: String, hostname: String, modifytime: String, datatime: String, inNum: Int, outNum: Int)

object HBaseAPI_Test_One {

  // Kerberos认证
  KerberosUtil.kerberosAuth()

  private val spark: SparkSession = SparkSession
    .builder()
    .master("local[2]")
    .appName("spark-hbase-read")
    .getOrCreate()

  private val sc: SparkContext = spark.sparkContext

  private val hbaseConf: Configuration = HBaseConfiguration.create()
  hbaseConf.set("hbase.zookeeper.quorum", "hdp301")
  hbaseConf.set("hbase.zookeeper.property.clientPort", "2181")

  def getOriginalData() = {
    import spark.implicits._
    import collection.mutable._

    // HBase 源数据库表
    val HBASE_TAG_TABLE = "trafficData"
    // 维护的时间戳,增量读取解析数据
    val HBASE_LAST_JOBTIME = "LastJobTime"
    // 创建连接对象
    val conn: Connection = ConnectionFactory.createConnection()
    val tag_table: Table = conn.getTable(TableName.valueOf(HBASE_TAG_TABLE))
    val time_table: Table = conn.getTable(TableName.valueOf(HBASE_LAST_JOBTIME))

    // 通过 rowkey 查询 HBase 表的 lastjobtime
    val get = new Get("TrafficDateTime".getBytes())
    val mdResult: Result = time_table.get(get)
    // get 直接拿到 时间戳
    val modifyTime: String = Bytes.toString(mdResult.getValue(Bytes.toBytes("t"), Bytes.toBytes("m")))

    // 查询原始数据
    val scan = new Scan()
    // 单列值过滤器 当 表中的 modifyTime 大于时间戳时 增量读取解析
    val mdValueFilter = new SingleColumnValueFilter(
      "d".getBytes(),
      "t".getBytes(),
      CompareOperator.GREATER_OR_EQUAL,
      new SubstringComparator(modifyTime) // 大于等于增量的时间戳
    )

    // scan 的条数,默认为100 扫描100 返给 客户端 Result 缓存读取
    scan.setCaching(200)
    // 设置过滤,下推到服务器 ,减少返回给客户端的数据量和 rowkey 指定范围结合更佳
    scan.setFilter(mdValueFilter)
    import collection.JavaConversions._
    val iter: util.Iterator[Result] = tag_table.getScanner(scan).iterator()
    // 存放定义的样例类
    val basicListTmp = new ListBuffer[InOutDataHBaseTest]()

    while (iter.hasNext) {
      var rowkey = ""
      var datatime = ""
      var inNum = 0
      var outNum = 0
      val result: Result = iter.next()
      val channel = Bytes.toString(result.getValue(Bytes.toBytes("d"), Bytes.toBytes("c")))
      val counterid = Bytes.toString(result.getValue(Bytes.toBytes("d"), Bytes.toBytes("ci")))
      val countertype = Bytes.toString(result.getValue(Bytes.toBytes("d"), Bytes.toBytes("ct")))
      val devicesn = Bytes.toString(result.getValue(Bytes.toBytes("d"), Bytes.toBytes("ds")))
      val datatype = Bytes.toString(result.getValue(Bytes.toBytes("d"), Bytes.toBytes("dt")))
      val hostname = Bytes.toString(result.getValue(Bytes.toBytes("d"), Bytes.toBytes("h")))
      val modifytime = Bytes.toString(result.getValue(Bytes.toBytes("t"), Bytes.toBytes("md")))

      rowkey = Bytes.toString(result.getRow)
      // 拿到 Result 的cell ,遍历 cell 拿到 columnName后判断列名取出 3#2#1 value 值 
      val cells = result.listCells()
      for (cell <- cells) {
        //  Cell工具类 获取到 列名
        var  cname = Bytes.toString(CellUtil.cloneQualifier(cell))
        if (cname.length >= 4) {
          datatime = rowkey.split("#")(1)+cname
          val cvalue = Bytes.toString(CellUtil.cloneValue(cell))
          val arr = cvalue.split("#")
          inNum = arr(0).toInt
          outNum = arr(1).toInt
          println(datatime + "--------" + inNum + "------" + outNum)
          // 将解析出的 cell 数据 放到 List 的样例类中
          basicListTmp += InOutDataHBaseTest(rowkey, channel, counterid, countertype,
            devicesn, datatype, hostname, modifytime, datatime, inNum, outNum)
        }
      }
    }
    // 获取 返回的 basicListTmp 并且返回
    val basicList: ListBuffer[InOutDataHBaseTest] = basicListTmp.map(x => InOutDataHBaseTest(x.rowkey, x.channel, x.counterid, x.countertype,
      x.devicesn, x.datatype, x.hostname, x.modifytime, x.datatime ,x.inNum, x.outNum))
    basicList.toSet
  }

  def main(args: Array[String]): Unit = {
    getOriginalData().foreach(println(_))
  }
}

 

上記は会社のテーブルデータの読み取りと分析の例です。もちろん、行キーはカスタムデザインであり、ホスト名+チャネルmd5は暗号化およびハッシュ化されているため、行キーに応じて読み取りを最適化することもできます。行キーのスキャン範囲に応じて指定---- withStartRow()およびwithStopRow()は、増分データ分析速度と相まって完璧です。

 参照:https//www.sohu.com/a/284932698_100109711

 

おすすめ

転載: blog.csdn.net/qq_35995514/article/details/108110553