目次
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