import org.apache.spark.sql._
import org.apache.spark.sql.types._
import org.apache.spark.sql.functions._
import org.apache.kudu.client._
import collection.JavaConverters._
import org.apache.kudu.spark.kudu.KuduContext
/**
* 将DataFrame中kudu不支持的列类型转为String类型
*/
def fmt_df(df: DataFrame) = {
val new_sch = fmt_schema(df.schema, Seq())
var d = df
new_sch.foreach {
case StructField(name: String, dataType: DataType, _, _) =>
d = d.withColumn(name, col(name).cast(dataType))
}
d
}
/**
* 将schema转为kudu可以建表的schema。具体做以下事情:
* 1. 主键不能为空
* 2. 把kudu不支持的数据类型转为String
* 3. 所有列名转为小写字母
*/
def fmt_schema(sch: StructType, pk: Seq[String]) = {
var arr = Array[StructField]()
sch.foreach {
case StructField(name: String, dataType: DataType, nullable: Boolean, metadata: Metadata) =>
val name_ = name.toLowerCase
val dataType_ = dataType match {
case DateType => StringType
case t: DecimalType => StringType
case _ => dataType
}
var nullable_ = nullable
pk.foreach(k => if (k.toLowerCase == name_) nullable_ = false)
arr = arr :+ new StructField(name_, dataType_, nullable_, metadata)
}
StructType(arr)
}
def createTableTest() {
val kudu_master = "master:7051,slave1:7051,slave2:7051"
val kuduContext = new KuduContext(kudu_master, spark.sparkContext)
val df = spark.table("hive_test")
val data = df.drop("columns_kudu_dont_supports")
val pks = Seq("primary", "key").map(_.toLowerCase)
val table_name = "kudu_test"
// 创建表结构,并不写入数据
kuduContext.createTable(
table_name, fmt_schema(data.schema, pks), pks,
new CreateTableOptions()
.setNumReplicas(3)
.addHashPartitions(pks.asJava, 10))
val res = fmt_df(data)
// 写入数据
kuduContext.upsertRows(res, table_name)
}
另一种写入数据的方法
// Data can also be inserted into the Kudu table using the data source, though the methods on
// KuduContext are preferred
// NB: The default is to upsert rows; to perform standard inserts instead, set operation = insert
// in the options map
// NB: Only mode Append is supported
df.write
.options(Map("kudu.master" -> kudu_master , "kudu.table" -> table_name))
.mode("append")
.format("kudu").save
val d = spark.read
.options(Map("kudu.master" -> kudu_master, "kudu.table" -> table_name))
.format("kudu").load
若语句报错java.lang.ClassNotFoundException: kudu.DefaultSource
则请确认引入的jar包版本(用1.9.0版本,更早版本会报错):
spark-shell --packages org.apache.kudu:kudu-spark2_2.11:1.9.0
# 或
spark-shell --jars kudu-spark2_2.11-1.9.0.jar
maven仓库地址:https://mvnrepository.com/artifact/org.apache.kudu/kudu-spark2_2.11/1.9.0
https://kudu.apache.org/docs/developing.html#_spark_integration_known_issues_and_limitations
注册为临时表时,必须为具有大写字母或非 ASCII 字符的名称的 Kudu 表分配备用名称。
具有包含大写字母或非 ASCII 字符的列名称的 Kudu 表, 不能与 SparkSQL 一起使用。 Columns 可能在 Kudu 更名为解决这个问题。
<>
和 OR
谓词不被推送到 Kudu ,而是将在 Spark 的evaluate过程中执行, 只有具有后缀通配符的 LIKE
谓词才被推送到Kudu执行,这意味着 LIKE "FOO%"
语句被下推到Kudu,而 LIKE "FOO%BAR"
这样的语句不会。
Kudu 不支持 Spark SQL 支持的所有类型,例如 Date , Decimal 和复杂类型。
Kudu 表只能在 SparkSQL 中注册为临时表。 不能使用 HiveContext 查询 Kudu 表。
https://github.com/apache/kudu/blob/master/examples/java/java-example/src/main/java/org/apache/kudu/examples/Example.java