Kudu——Spark操作Kudu

到目前为止,我们已经听说过几个上下文,例如 SparkContextSQLContextHiveContextSparkSession,现在,我们将使用 Kudu 引入一个KuduContext。这是可在 Spark 应用程序中广播的主要可序列化对象。此类代表在 Spark 执行程序中与 Kudu Java客户端进行交互。 KuduContext 提供执行DDL 操作所需的方法,与本机 Kudu RDD 的接口,对数据执行更新/插入/删除**,将数据类型从 Kudu 转换为 Spark 等。

1、引入依赖

<repositories>
    <repository>
    <id>cloudera</id>
    <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
    </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>org.apache.kudu</groupId>
        <artifactId>kudu-client-tools</artifactId>
        <version>1.6.0-cdh5.14.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.kudu</groupId>
        <artifactId>kudu-client</artifactId>
        <version>1.6.0-cdh5.14.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.kudu/kudu-spark2 -->
    <dependency>
        <groupId>org.apache.kudu</groupId>
        <artifactId>kudu-spark2_2.11</artifactId>
        <version>1.6.0-cdh5.14.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.spark/spark-sql -->
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-sql_2.11</artifactId>
        <version>2.1.0</version>
    </dependency>
</dependencies>

2、创建表

object SparkKuduTest {
    def main(args: Array[String]): Unit = {
        //构建 sparkConf 对象
        val sparkConf: SparkConf = 
            new SparkConf().setAppName("SparkKuduTest").setMaster("local[2]")
        //构建 SparkSession 对象
        val sparkSession: SparkSession =
            SparkSession.builder().config(sparkConf).getOrCreate()
        //获取 sparkContext 对象
        val sc: SparkContext = sparkSession.sparkContext
        sc.setLogLevel("warn")
        //构建 KuduContext 对象
        val kuduContext = 
            new KuduContext("node1:7051, node2:7051, node3:7051", sc)
        //1.创建表操作
        createTable(kuduContext)
        /**
         * 创建表
         * @param kuduContext
         * @return
         */
        private def createTable(kuduContext: KuduContext) = {
            //1.1 定义表名
            val tableName = "spark_kudu"
            //1.2 定义表的 schema
            val schema = StructType(
                StructField("userId", StringType, false) ::
                StructField("name", StringType, false) ::
                StructField("age", IntegerType, false) ::
                StructField("sex", StringType, false) :: Nil)
            //1.3 定义表的主键
            val primaryKey = Seq("userId")
            
            //1.4 定义分区的 schema
            val options = new CreateTableOptions
            //设置分区
            options.setRangePartitionColumns(List("userId").asJava)
            //设置副本
            options.setNumReplicas(1)
            //1.5 创建表
            if(!kuduContext.tableExists(tableName)){
                kuduContext.createTable(tableName, schema, primaryKey, options)
            }
        }
    }
}

定义表时要注意的是 Kudu 表选项值。你会注意到在指定组成范围分区列的列名列表时我们调用asJava方法。这是因为在这里,我们调用了 Kudu

Java 客户端本身,它需要 Java对象(即java.util.List)而不是 ScalaList 对象;(要使“asJava”方法可用,请记住导入 JavaConverters库。)

创建表后,通过将浏览器指向 http// master 主机名:8051/tables 来查看KuduUI可以找到创建的表,通过单击表 ID,能够看到表模式和分区信息。

在这里插入图片描述

3、dataFrame 操作 Kudu

Kudu 支持许多 DML类型的操作,其中一些操作包含在 Spark on Kudu 集成。包括:

  • INSERT: DataFrame 的行插入 Kudu 。请注意,虽然API完全支持INSERT,但不鼓励在Spark 中使用它。使用 INSERT 是有风险的,因为 Spark 任务可能需要重新执行,这意味着可能要求再次插入已插入的行。这样做会导致失败,因为如果行已经存在,INSERT 将不允许插入行(导致败)。相反,我们鼓励使用下面描述的 INSERT_IGNORE
  • INSERT-IGNORE:DataFrame 的行插入 Kudu表。如果表存在,则忽略插入动作;
  • DELETE:Kudu 表中删除 DataFrame 中的行;
  • UPSERT: 如果存在,则在 Kudu 表中更新 DataFrame 中的行,否则执行插入操作;
  • UPDATE: 更新 dataframe 中的行。

3.1、插入数据 insert 操作

先创建一张表,然后把数据插入到表中。

case class People(id:Int, name:String, age:Int)
object DataFrameKudu {
    def main(args: Array[String]): Unit = {
        //构建 SparkConf 对象
        val sparkConf: SparkConf = 
            new SparkConf().setAppName("DataFrameKudu").setMaster("local[2]")
        //构建 SparkSession 对象
        val sparkSession: SparkSession =
            SparkSession.builder().config(sparkConf).getOrCreate()
        //获取 SparkContext 对象
        val sc: SparkContext = sparkSession.sparkContext
        sc.setLogLevel("warn")
        //指定 kudu 的 master 地址
        val kuduMaster="node1:7051, node2:7051, node3:7051"
        //构建 KuduContext 对象
        val kuduContext = new KuduContext(kuduMaster,sc)
        //定义表名
        val tableName="people"
        //1、创建表
        createTable(kuduContext, tableName)
        //2、插入数据到表中
        insertData2table(sparkSession,sc, kuduContext, tableName)
    }
    
    /**
     * 创建表
     * @param kuduContext
     * @param tableName
     */
    private def createTable(kuduContext: KuduContext, tableName: String): Unit = {
        //定义表的 schema
        val schema = StructType(
            StructField("id", IntegerType, false) ::
            StructField("name", StringType, false) ::
            StructField("age", IntegerType, false) :: Nil)
        //定义表的主键
        val tablePrimaryKey = List("id")
        //定义表的选项配置
        val options = new CreateTableOptions
        options.setRangePartitionColumns(List("id").asJava)
        options.setNumReplicas(1)
        //创建表
        if (!kuduContext.tableExists(tableName)) {
            kuduContext.createTable(tableName, schema, tablePrimaryKey, options)
        }
    }
    
    /**
     * 插入数据到表中
     * @param sparkSession
     * @param sc
     * @param kuduContext
     * @param tableName
     */
    private def insertData2table(sparkSession:SparkSession, sc: SparkContext,
        kuduContext: KuduContext, tableName: String): Unit = {
        //准备数据
        val data = List(
            People(1, "zhangsan", 20), 
            People(2, "lisi", 30), 
            People(3, "wangwu", 40))
        val peopleRDD: RDD[People] = sc.parallelize(data)
        import sparkSession.implicits._
        val peopleDF: DataFrame = peopleRDD.toDF
        kuduContext.insertRows(peopleDF, tableName)
    }
}

3.2、删除数据delete操作

/**
 * 删除表的数据
 * @param sparkSession
 * @param sc
 * @param kuduMaster
 * @param kuduContext
 * @param tableName
 */
private def deleteData(sparkSession: SparkSession, sc: SparkContext, 
    kuduMaster: String,
    kuduContext: KuduContext, tableName: String): Unit = {
    //定义一个 map 集合,封装 kudu 的相关信息
    val options = Map(
    "kudu.master" -> kuduMaster,
    "kudu.table" -> tableName
    )
    import sparkSession.implicits._
    val data = List(
        People(1, "zhangsan", 20), 
        People(2, "lisi", 30), 
        People(3, "wangwu", 40))
    val dataFrame: DataFrame = sc.parallelize(data).toDF
    dataFrame.createTempView("temp")
    //获取年龄大于 30 的所有用户 id
    val result: DataFrame = sparkSession.sql("select id from temp where age >30")
    //删除对应的数据,这里必须要是主键字段
    kuduContext.deleteRows(result, tableName)
}

3.3、更新数据upsert操作

/**
 * 更新数据--添加数据
 *
 * @param sc
 * @param kuduMaster
 * @param kuduContext
 * @param tableName
 */
private def UpsertData(sparkSession: SparkSession,sc: SparkContext, 
    kuduMaster: String, kuduContext: KuduContext, tableName: String): Unit = {
    //更新表中的数据
    //定义一个 map 集合,封装 kudu 的相关信息
    val options = Map(
        "kudu.master" -> kuduMaster,
        "kudu.table" -> tableName)
    import sparkSession.implicits._
    val data = List(People(1, "zhangsan", 50), People(5, "tom", 30))
    val dataFrame: DataFrame = sc.parallelize(data).toDF
    //如果存在就是更新,否则就是插入
    kuduContext.upsertRows(dataFrame, tableName)
}

3.4、更新数据 update 操作

/**
 * 更新数据
 * @param sparkSession
 * @param sc
 * @param kuduMaster
 * @param kuduContext
 * @param tableName
 */
private def updateData(sparkSession: SparkSession,sc: SparkContext, kuduMaster: String,
    kuduContext: KuduContext, tableName: String): Unit = {
    //定义一个 map 集合,封装 kudu 的相关信息
    val options = Map(
    "kudu.master" -> kuduMaster,
    "kudu.table" -> tableName)
    import sparkSession.implicits._
    val data = List(People(1, "zhangsan", 60), People(6, "tom", 30))
    val dataFrame: DataFrame = sc.parallelize(data).toDF
    //如果存在就是更新,否则就是报错
    kuduContext.updateRows(dataFrame, tableName)
}

3.5、DataFrame API 读取 kudu 数据

虽然我们可以通过上面显示的 KuduContext 执行大量操作,但我们还可以直接从默认数据源本身调用读/写API。要设置读取,我们需要为 Kudu表指定选项,命名我们要读取的表以及为表提供服务的 Kudu 集群的 Kudu主服务器列表。

/**
 * 使用 DataFrameApi 读取 kudu 表中的数据
 * @param sparkSession
 * @param kuduMaster
 * @param tableName
 */
private def getTableData(sparkSession: SparkSession, kuduMaster: String, 
    tableName: String): Unit = {
    //定义 map 集合,封装 kudu 的 master 地址和要读取的表名
    val options = Map(
    "kudu.master" -> kuduMaster,
    "kudu.table" -> tableName
    )
    sparkSession.read.options(options).kudu.show()
}

3.6、DataFrame API写数据到 Kudu

在通过 DataFrame API编写时,目前只支持一种模式“append”。尚未实现的“覆盖”模式。

/**
 * DataFrame api 写数据到 kudu 表
 * @param sparkSession
 * @param sc
 * @param kuduMaster
 * @param tableName
 */
private def dataFrame2kudu(sparkSession: SparkSession, sc: SparkContext, 
    kuduMaster: String, tableName: String): Unit = {
    //定义 map 集合,封装 kudu 的 master 地址和要读取的表名
    val options = Map(
    "kudu.master" -> kuduMaster,
    "kudu.table" -> tableName
    )
    val data = List(People(7, "jim", 30), People(8, "xiaoming", 40))
    import sparkSession.implicits._
    val dataFrame: DataFrame = sc.parallelize(data).toDF
    //把 dataFrame 结果写入到 kudu 表中 ,目前只支持 append 追加
    dataFrame.write.options(options).mode("append").kudu
    //查看结果
    //导包
    import org.apache.kudu.spark.kudu._
    //加载表的数据,导包调用 kudu 方法,转换为 dataFrame,最后在使用 show 方法显示结果
    sparkSession.read.options(options).kudu.show()
}

3.7、用 SparkSQL 操作 Kudu

可以选择使用 Spark SQL 直接使用 INSERT 语句写入 Kudu 表;与'append'类似,INSERT 语句实际上将默认使用 UPSERT语义处理。

/**
 * 使用 sparksql 操作 kudu 表
 * @param sparkSession
 * @param sc
 * @param kuduMaster
 * @param tableName
 */
private def SparkSql2Kudu(sparkSession: SparkSession, 
    sc: SparkContext, kuduMaster: String, tableName: String): Unit = {
    //定义 map 集合,封装 kudu 的 master 地址和表名
    val options = Map(
    "kudu.master" -> kuduMaster,
    "kudu.table" -> tableName
    )
    val data = List(People(10, "小张", 30), People(11, "小王", 40))
    import sparkSession.implicits._
    val dataFrame: DataFrame = sc.parallelize(data).toDF
    //把 dataFrame 注册成一张表
    dataFrame.createTempView("temp1")
    //获取 kudu 表中的数据,然后注册成一张表
    sparkSession.read.options(options).kudu.createTempView("temp2")
    //使用 sparkSQL 的 insert 操作插入数据
    sparkSession.sql("insert into table temp2 select * from temp1")
    sparkSession.sql("select * from temp2 where age >30").show()
}

4、kudu native RDD

SparkKudu的集成同时提供了 Kudu RDD

//使用 kuduContext 对象调用 kuduRDD 方法,需要 sparkContext 对象,表名,想要的字段名称
val kuduRDD: RDD[Row] =
    kuduContext.kuduRDD(sc, tableName, Seq("name", "age"))
//操作该 rdd 打印输出
val result: RDD[(String, Int)] = kuduRDD.map {
    case Row(name: String, age: Int) => (name, age)
}
result.foreach(println)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值