最近在写spark程序的时候遇到了hbase并发插入的问题,用sparkstreaming来接收kafka的数据,由于数据量较大,我用spark的executer端去并行插入hbase,结果发现有些数据没插进去,经过排查之后,发现是同一个批次中行键相同的数据,这些数据只能插入一部分或者都能插入,这里面存在一定的概率问题,原因就是不同的executer端会反复读写这些行键相同的数据行,导致数据不满足一致性。
后来用checkAndPut解决了这个问题,简单说来,就是在put数据之前先经过某些条件的验证,只有满足条件的put才会入库。
checkAndPut(byte[] row, byte[] family, byte[] qualifier, byte[] value, Put put) 第一个参数是行键,第二个参数是列族,第三个参数是列名,第四个参数是预期的列值,最后一个参数是put对象。
def putsumm(rowkey:String,htable:HTableInterface,obj:ExpressPointAlloSummary): Unit ={
var checkvalue :Array[Byte] = null
val get = new Get(rowkey.getBytes())
val orderd = htable.get(get)
if (recordExist(orderd)) {
checkvalue = getColumnValue(orderd, "brgew").getBytes()
}
var orderNumSuccess: Boolean = false
// 循环写入数据次数
var orderNumLoopNum: Int = 0
while (!orderNumSuccess && orderNumLoopNum < 20) {
val rltPut = new Put(rowkey.getBytes)
rltPut.add(Bytes.toBytes("f"), Bytes.toBytes("fc_time"), Bytes.toBytes(obj.fc_time))
rltPut.add(Bytes.toBytes("f"), Bytes.toBytes("site_cd"), Bytes.toBytes(obj.site_cd))
rltPut.add(Bytes.toBytes("f"), Bytes.toBytes("dest_site"), Bytes.toBytes(obj.dest_site))
rltPut.add(Bytes.toBytes("f"), Bytes.toBytes("brgew"), Bytes.toBytes(obj.brgew))
rltPut.add(Bytes.toBytes("f"), Bytes.toBytes("gewei"), Bytes.toBytes(obj.gewei))
orderNumSuccess = htable.checkAndPut(
Bytes.toBytes(rowkey),
Bytes.toBytes("f"),
Bytes.toBytes("brgew"),
checkvalue, rltPut)
logger.info("wyk")
orderNumLoopNum+=1
}
println(orderNumSuccess)
}
当然在并发超级高的时候,可能一直不满足条件,就会一直不入库,但这种概率是非常低的,可以忽略不计,为了避免陷入死循环或者循环时间太久,这里限制只循环20次,已经满足业务场景了。在这里插入代码片