índice
1. Conceitos importantes de HBaseAPI
2. Análise de caso --- aprendizagem HBaseAPI
1. Conceitos importantes de HBaseAPI
1. Varredura
A tabela de dados no HBase realiza a fragmentação dos dados dividindo-os em regiões. Cada região é associada a um intervalo de RowKey e os dados em cada região são organizados na ordem lexicográfica de RowKey.
É com base neste design que o HBase pode facilmente lidar com este tipo de consulta: "Especifique um intervalo de RowKey e obtenha todos os registros nesse intervalo". Este tipo de consulta é chamado de Scan no HBase. Claro, se você não o especificar , ele estará cheio. Varredura da tabela, a seguir está uma consulta é um acesso RPC, retornar o conjunto de resultados para o cliente.
1. Construir varredura, especificar startRow e stopRow, se não for especificado, uma varredura completa da tabela será realizada
2. Obtenha ResultScanner
3. Percorra os resultados da consulta
4. Feche o ResultScanner
2. Resultado
Encapsular a varredura como um objeto de resultado e devolvê-la ao cliente.
3. Varredura do Filtro
O filtro pode definir mais valores de condição para os registros retornados com base no conjunto de resultados de Scan. Essas condições podem ser relacionadas a RowKey, nome da coluna ou valor da coluna, e várias condições de filtro podem ser combinadas em Juntos, geralmente combinados ser FilterList, mas geralmente não recomendado, pode haver o risco de vazamento de dados.
- Cada vez que o cliente envia uma solicitação de varredura para o RegionServer, ele receberá de volta um lote de dados (o número de resultados recuperados a cada vez é determinado pelo cache), e então o colocará no cache de resultados desta vez
- Cada vez que o aplicativo lê dados, eles são obtidos do cache de resultados local. Se os dados no cache de resultados foram lidos, o cliente enviará uma solicitação de varredura ao RegionServer novamente para obter mais dados
2. Análise de caso --- aprendizagem HBaseAPI
1. Exigir
Analise os dados na tabela a seguir. Os dados relatados regularmente são relatados em minutos. O valor após minutos = 2 # 1 # 0 são os dados a serem analisados e resumidos. Agora, quero resumir os dados de 20200706 deste dia, desde que é incremental Ao ler a análise de dados, o carimbo de hora será mantido na tabela LastJobTime.Se o modifyTime na tabela for maior que o carimbo de hora registrado após cada análise, novos dados serão lidos.
2. Pensando
- Percorra o conjunto de resultados do Resultado e retire diretamente os valores de campo determinados do Resultado de acordo com CF e coluna, como c, ci, ct e outros campos acima
- Os campos incertos são os campos d: 1300, d: 1305, etc., que são escritos dinamicamente na tabela de acordo com o tempo. Primeiro, atravesse a célula, retire o Qualify de acordo com a célula, leia e analise todos os campos de acordo com com comprimento de 4 ou mais, até agora podemos obter os dados de valor escritos dinamicamente na coluna de tempo
Se você tiver a sorte de ler aqui, apenas entenda meu pensamento e não precisará entrar nos cenários de negócios acima.
3. Código
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(_))
}
}
O exemplo acima é um exemplo de leitura e análise dos dados da tabela da empresa. Claro, a leitura também pode ser otimizada de acordo com a chave de linha, porque a chave de linha é um design personalizado e o nome do host + canal md5 é criptografado e com hash, e pode ser especificado de acordo com o intervalo de varredura da chave de linha ---- withStartRow () e withStopRow (), juntamente com a velocidade de análise de dados incremental, são perfeitos.
Referência: https://www.sohu.com/a/284932698_100109711