使用Spark RDD實現hbase分佈式Scan
主要思路
利用Spark RDD的分佈式計算,將一個Scan任務按照自定義的範圍切分爲小的scan,使用這些RDD實現對scan的並行查詢,提高查詢效率。核心是手動實現RDD的compute方法與getPartitions方法。
關於Spark RDD
本文中使用到的關於Spark RDD方面的知識主要集中在RDD分區計算上,查看spark源碼即可知道,spark RDD中有兩個比較重要的方法,一個是compute 用於計算一個指定的分區給當前RDD,一個是getPartitions用於獲取當前RDD的一組分區。
主要代碼實現
下面是主要的代碼實現,具體關於分區範圍的值需要自己根據實際情況和自己的需求實現
class HBaseScanRDD(sc: SparkContext, val table: Table) //此處table值不應該出現,僅用於跳過編譯錯誤
extends RDD[Result](sc, Nil) {var ranges = Seq.empty[HRegionInfo]
var regions = new ArrayHRegionInfooverride protected def getPartitions: Array[Partition] = {
var idx = 0
val ps = regions.flatMap { _ =>
val rs = ranges
idx += 1
if (rs.nonEmpty)
Some(idx - 1, rs) //這裏的結果爲自定義的HBasePartition
else
None
}
ps.asInstanceOf[Array[Partition]]
}@DeveloperApi
override def compute(partition: Partition, context: TaskContext): >Iterator[Result] = {
val split = partition.asInstanceOf[HBasePartition]
//繼承Partition即可
val scans = split.scanRanges //構建小的Scan,推薦按照region的>startkey endkey進行切分
.map { range =>
//(start,end)的四種情況
(range.getStartKey, range.getEndKey) match {
case (a, b) => new Scan(a, b)
case (a, None) => new Scan(a)
case (None, b) => new Scan(ArrayByte, b)
case (None, None) => new Scan()
}
}
//並行scan
scans.par.map { scan =>
table.getScanner(scan)
}.map(toResultIterator)
.fold(Iterator.empty: Iterator[Result]) { case (x, y) =>
x ++ y
}
}//這裏用於獲取scanner的值
private def toResultIterator(scanner: ResultScanner): Iterator[Result] = {
val iterator = new Iterator[Result] {
var cur: Option[Result] = Noneoverride def hasNext: Boolean = { if (cur.isEmpty) { val r = scanner.next() if (r == null) { } else { cur = Some(r) } } cur.isDefined } override def next(): Result = { hasNext val ret = cur.get cur = None
ret
}
}
iterator
}
}case class HBasePartition(
override val index: Int,
scanRanges: Seq[HRegionInfo]) extends Partition
以上代碼只是用於描述思路,實際使用時還需要對代碼細節進行處理
注意:由於spark源碼定義RDD爲private[spark],所以該類只能放置與名稱爲org.apache.hadoop.hbase.spark的包或子包中,否則會出現編譯錯誤。
u
非tips: 由於hbase的scan在服務端查詢時,會分爲兩類scanner,一個查詢memstore,一個查詢hfile,
其中查找hfile的scanner會根據設置參數”hbase.storescanner.parallel.seek.enable”
決定是否使用多線程對文件進行查詢,所以建議將該參數設置爲true。