往kudu写数据一般有两种方式:impala+kudu,kudu api
- impala+kudu: 更删改查很方便,但是效率不高
- kudu api: 相对来说会快很多,官方推荐使用 Java API和Python API完成数据写入工作
kudu API 有三种FlushMode:
- AUTO_FLUSH_SYNC(默认):所有的写入都只有在自动刷新到服务器后才会返回。不会发生批处理,在这种模式下,Flush()函数不会产生任何效果,因为每个应用程序在返回之前已经刷新了缓冲区
- AUTO_FLUSH_BACKGROUND:每一个应用的KuduSession.apply()函数都会返回的非常快,但是写操作会被发送到后台进程,可能与来自同一会话的其他写入一起进行批处理。如果没有足够的缓冲空间,KuduSession.apply()会阻塞,缓冲空间不可用。因为写入操作是在后台应用进行的的,因此任何错误都将存储在一个会话本地缓冲区中。注意:这个模式可能会导致数据插入是乱序的,这是因为在这种模式下,多个写操作可以并发地发送到服务器。这个模式有个bug
- MANUAL_FLUSH:每一个应用的KuduSession.apply()函数都会返回的非常快,但是写操作不会发送,直到用户使用flush()函数,如果缓冲区超过了配置的空间限制,KuduSession.apply()函数会返回一个错误
看了网上一些效率测试,相对来说,MANUAL_FLUSH的效率是最高的,而且高很多。
MANUAL_FLUSH
def upsertBatchRows(tableName: String, datas: List[Array[(String, String, String)]], client: KuduClient = this.client): Unit = {
val table = this.getTable(tableName, client)
val session = client.newSession()
session.setFlushMode(SessionConfiguration.FlushMode.MANUAL_FLUSH)
session.setMutationBufferSpace(DATA_COMMIT_SIZE)
var commitCount = 0
datas.foreach(data => {
val upsert = table.newUpsert()
val row = upsert.getRow
data.foreach(f => {
f._2.toLowerCase match {
case "bigint" => if (f._3.isEmpty) 0 else row.addLong(f._1.toLowerCase, f._3.toLong)
case "decimal" => if (f._3.isEmpty) 0 else row.addDecimal(f._1.toLowerCase, java.math.BigDecimal.valueOf(f._3.n2e.toDouble))
case "double" => if (f._3.isEmpty) 0.0 else row.addDouble(f._1.toLowerCase, f._3.toDouble)
case "float" => if (f._3.isEmpty) 0.0 else row.addFloat(f._1.toLowerCase, f._3.toFloat)
case "int" => if (f._3.isEmpty) 0 else row.addInt(f._1.toLowerCase, f._3.toInt)
case "long" => if (f._3.isEmpty) 0 else row.addLong(f._1.toLowerCase, f._3.toLong)
case "smallint" => if (f._3.isEmpty) 0 else row.addShort(f._1.toLowerCase, f._3.toShort)
case "timestamp" => row.addTimestamp(f._1.toLowerCase, Timestamp.valueOf(f._3.n2e))
case "tinyint" => if (f._3.isEmpty) 0 else row.addByte(f._1.toLowerCase, f._3.toByte)
case "n" => row.setNull(f._1.toLowerCase)
case _ => row.addString(f._1.toLowerCase, f._3.n2e)
}
})
session.apply(upsert)
//对于手工提交, 需要buffer在未满的时候flush
commitCount += 1
if (commitCount > DATA_COMMIT_SIZE / 2) {
session.flush()
commitCount = 0
}
})
session.flush()
session.close()
}
需要设置buffer大小,测试了50,1000,10000,发现设置为10000时,效率最高