spark_数据读取与保存

182 阅读5分钟

1. 文件类数据读取与保存

1. Text文件

  • 1)数据读取:textFile(String)
  • 2)数据保存:saveAsTextFile(String)
  • 3)注意:如果是集群路径:hdfs://hadoop102:9000/input/1.txt
  • 4)代码实现
object Operate_Text {

    def main(args: Array[String]): Unit = {

        //1.创建SparkConf并设置App名称
        val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[1]")

        //2.创建SparkContext,该对象是提交Spark App的入口
        val sc: SparkContext = new SparkContext(conf)

        //3.1 读取输入文件
        val inputRDD: RDD[String] = sc.textFile("input/1.txt")

        //3.2 保存数据
        inputRDD.saveAsTextFile("output")

        //4.关闭连接
        sc.stop()
    }
}

2. Json文件

如果JSON文件中每一行就是一个JSON记录,那么可以通过将JSON文件当做文本文件来读取,然后利用相关的JSON库对每一条数据进行JSON解析。

  1. 数据准备
{"username": "zhangsan","age": 20}
{"username": "lisi","age": 18}
{"username": "wangwu","age": 16}
  1. 实例
//1 读取Json输入文件
val jsonRDD: RDD[String] = sc.textFile("input/user.json")
//2 导入解析Json所需的包并解析Json
import scala.util.parsing.json.JSON
val resultRDD: RDD[Option[Any]] = jsonRDD.map(JSON.parseFull)
//3 打印结果
resultRDD.collect().foreach(println)
  1. 修改刚刚数据格式
[{"username": "zhangsan","age": 20},
{"username": "lisi","age": 18},
{"username": "wangwu","age": 16}
]

再次执行程序,发现解析失败。原因是一行一行的读取文件。

注意:使用RDD读取JSON文件处理很复杂,同时SparkSQL集成了很好的处理JSON文件的方式,所以应用中多是采用SparkSQL处理JSON文件。

3. sequence文件

SequenceFile文件是Hadoop用来存储二进制形式的key-value对而设计的一种平面文件(Flat File)。在SparkContext中,可以调用sequenceFile[keyClass, valueClass](path)。

注意:SequenceFile文件只针对PairRDD

//1 创建rdd
val dataRDD: RDD[(Int, Int)] = sc.makeRDD(Array((1,2),(3,4),(5,6)))
//2 保存数据为SequenceFile
dataRDD.saveAsSequenceFile("output")
//3 读取SequenceFile文件
sc.sequenceFile[Int,Int]("output").collect().foreach(println)

4. object对象文件

对象文件是将对象序列化后保存的文件,采用Java的序列化机制。可以通过objectFile【k,v】(path)函数接收一个路径,读取对象文件,返回对应的RDD,也可以通过调用saveAsObjectFile()实现对对象文件的输出。因为是序列化所以要指定类型。

//1 创建RDD
val dataRDD: RDD[Int] = sc.makeRDD(Array(1,2,3,4))
//2 保存数据
dataRDD.saveAsObjectFile("output")
//3 读取数据
sc.objectFile[(Int)]("output").collect().foreach(println)

2. 文件系统类数据读取与保存

1. HDFS

Spark的整个生态系统与Hadoop是完全兼容的,所以对于Hadoop所支持的文件类型或者数据库类型,Spark也同样支持。另外,由于Hadoop的API有新旧两个版本,所以Spark为了能够兼容Hadoop所有的版本,也提供了两套创建操作接口。对于外部存储创建操作而言,hadoopRDD和newHadoopRDD是最为抽象的两个函数接口

2. MySql

支持通过Java JDBC访问关系型数据库。需要通过JdbcRDD进行,示例如下:

  1. 添加依赖
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.27</version>
</dependency>
  1. 实例
  • 读数据
/**
	从MySQL数据库中读取数据
    
    sc: SparkContext,   Spark程序执行的入口,上下文对象
    getConnection: () => Connection,  获取数据库连接
    sql: String,  执行SQL语句
    lowerBound: Long, 查询的其实位置
    upperBound: Long, 查询的结束位置
    numPartitions: Int,分区数
    mapRow: (ResultSet) => T   对结果集的处理

    注册驱动
    获取连接
    创建数据库操作对象PrepareStatement
    执行SQL
    处理结果集
    关闭连接
  */
object Spark04_MySQL_read {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交Spark App的入口 
    val sc: SparkContext = new SparkContext(conf)

    //数据库连接4要素
    var driver = "com.mysql.jdbc.Driver"
    var url = "jdbc:mysql://mayi101:3306/test"
    var username = "root"
    var password = "root"

    var sql:String = "select * from user where id >= ? and id <= ?"
    val resRDD = new JdbcRDD(
      sc,
      () => {
        //注册驱动
        Class.forName(driver)
        //获取连接
        DriverManager.getConnection(url, username, password)
      },
      sql,
      1,
      20,
      2,
      rs => (rs.getInt(1), rs.getString(2), rs.getInt(3))
    )

    resRDD.collect().foreach(println)

    // 关闭连接
    sc.stop()
  }
}
  • 写数据
/**
  * Author: Felix
  * Date: 2020/5/15
  * Desc: 向MySQL数据库中写入数据
    注册驱动
    获取连接
    创建数据库操作对象PrepareStatement
    执行SQL
    处理结果集
    关闭连接
  */
object Spark05_MySQL_write {
  def main(args: Array[String]): Unit = {
    //创建SparkConf并设置App名称
    val conf: SparkConf = new SparkConf().setAppName("SparkCoreTest").setMaster("local[*]")

    //创建SparkContext,该对象是提交Spark App的入口 
    val sc: SparkContext = new SparkContext(conf)

    //数据库连接4要素
    var driver = "com.mysql.jdbc.Driver"
    var url = "jdbc:mysql://hadoop202:3306/test"
    var username = "root"
    var password = "123456"

    val rdd: RDD[(String, Int)] = sc.makeRDD(List(("xingda",30),("ruihao",18)))

    /*
    //在循环体中创建连接对象,每次遍历出RDD中的一个元素,都要创建一个连接对象,效率低,不推荐使用
    rdd.foreach{
      case (name,age)=>{
        //注册驱动
        Class.forName(driver)
        //获取连接
        val conn: Connection = DriverManager.getConnection(url,username,password)
        //声明数据库操作的SQL语句
        var sql:String = "insert into user(name,age) values(?,?)"
        //创建数据库操作对象PrepareStatement
        val ps: PreparedStatement = conn.prepareStatement(sql)
        //给参数赋值
        ps.setString(1,name)
        ps.setInt(2,age)
        //执行SQL
        ps.executeUpdate()
        //关闭连接
        ps.close()
        conn.close()
      }
    }
    */

    /*
    下面这段代码,需要让Ps实现序列化。但是Ps不是我们自己定义的类型,没有办法实现
    //注册驱动
    Class.forName(driver)
    //获取连接
    val conn: Connection = DriverManager.getConnection(url,username,password)
    //声明数据库操作的SQL语句
    var sql:String = "insert into user(name,age) values(?,?)"
    //创建数据库操作对象PrepareStatement
    val ps: PreparedStatement = conn.prepareStatement(sql)

    rdd.foreach{
      case (name,age)=>{
        //给参数赋值
        ps.setString(1,name)
        ps.setInt(2,age)
        //执行SQL
        ps.executeUpdate()
      }
    }
    //关闭连接
    ps.close()
    conn.close()
    */

    //map==>mapPartitions
    rdd.foreachPartition{
      //datas是RDD的一个分区的数据
      datas=>{
        //注册驱动
        Class.forName(driver)
        //获取连接
        val conn: Connection = DriverManager.getConnection(url,username,password)
        //声明数据库操作的SQL语句
        var sql:String = "insert into user(name,age) values(?,?)"
        //创建数据库操作对象PrepareStatement
        val ps: PreparedStatement = conn.prepareStatement(sql)

        //对当前分区内的数据,进行遍历
        //注意:这个foreach不是算子了,是集合的方法
        datas.foreach{
          case (name,age)=>{
            //给参数赋值
            ps.setString(1,name)
            ps.setInt(2,age)
            //执行SQL
            ps.executeUpdate()
          }
        }

        //关闭连接
        ps.close()
        conn.close()
      }
    }

    // 关闭连接
    sc.stop()
  }
}