Spark通过读文件构建DataFrame数据模型(java版)--RDD弹性分布式数据集

操作RDD

Spark支持两种RDD操作:transformation和action。

transformation操作

transformation操作会针对已有的RDD创建一个新的RDD。transformation具有lazy特性,即transformation不会触发spark程序的执行,它们只是记录了对RDD所做的操作,不会自发的执行。只有执行了一个action,之前的所有transformation才会执行。

常用的transformation介绍:

map :将RDD中的每个元素传人自定义函数,获取一个新的元素,然后用新的元素组成新的RDD。

filter:对RDD中每个元素进行判断,如果返回true则保留,返回false则剔除。

flatMap:与map类似,但是对每个元素都可以返回一个或多个元素。

groupByKey:根据key进行分组,每个key对应一个Iterable<value>。

reduceByKey:对每个key对应的value进行reduce操作。

sortByKey:对每个key对应的value进行排序操作。

join:对两个包含<key,value>对的RDD进行join操作,每个keyjoin上的pair,都会传入自定义函数进行处理。

cogroup:同join,但是每个key对应的Iterable<value>都会传入自定义函数进行处理。

action操作

action操作主要对RDD进行最后的操作,比如遍历,reduce,保存到文件等,并可以返回结果给Driver程序。action操作执行,会触发一个spark job的运行,从而触发这个action之前所有的transformation的执行,这是action的特性。

常用的action介绍:

reduce:将RDD中的所有元素进行聚合操作。第一个和第二个元素聚合,值与第三个元素聚合,值与第四个元素聚合,以此类推。

collect:将RDD中所有元素获取到本地客户端(一般不建议使用)。

count:获取RDD元素总数。

take(n):获取RDD中前n个元素。

saveAsTextFile:将RDD元素保存到文件中,对每个元素调用toString方法。

countByKey:对每个key对应的值进行count计数。

foreach:遍历RDD中的每个元素。

RDD持久化

要持久化一个RDD,只要调用其cache()或者persist()方法即可。在该RDD第一次被计算出来时,就会直接缓存在每个节点中。但是cache()或者persist()的使用是有规则的,必须在transformation或者textFile等创建了一个RDD之后,直接连续调用cache()或persist()才可以。

如果你先创建一个RDD,然后单独另起一行执行cache()或persist()方法,是没有用的,而且会报错,大量的文件会丢失。

val lines = spark.read.textFile("hdfs://mycluster/spark.txt").persist()

Spark提供的多种持久化级别,主要是为了在CPU和内存消耗之间进行取舍。

通用的持久化级别的选择建议:

1、优先使用MEMORY_ONLY,如果可以缓存所有数据的话,那么就使用这种策略。因为纯内存速度最快,而且没有序列化,不需要消耗CPU进行反序列化操作。

2、如果MEMORY_ONLY策略,无法存储所有数据的话,那么使用MEMORY_ONLY_SER,将数据进行序列化进行存储,纯内存操作还是非常快,只是要消耗CPU进行反序列化。

3、如果需要进行快速的失败恢复,那么就选择带后缀为_2的策略,进行数据的备份,这样在失败时,就不需要重新计算了。

4、能不使用DISK相关的策略,就不用使用,有的时候,从磁盘读取数据,还不如重新计算一次。

共享变量

Spark提供了两种共享变量:Broadcast Variable(广播变量)和Accumulator(累加变量)。

BroadcastVariable会将使用到的变量,仅仅为每个节点拷贝一份,更大的用处是优化性能,减少网络传输以及内存消耗。广播变量是只读的。

  1. val factor = 3

  2. val broadcastVars = sc.broadcast(factor);

  3. val numberList = Array(1,2,3,4,5)

  4. val number = sc.parallelize(numberList).map( num => num * broadcastVars.value) //广播变量读值broadcastVars.value

Accumulator则可以让多个task共同操作一份变量,主要可以进行累加操作。task只能对Accumulator进行累加操作,不能读取它的值。只有Driver程序可以读取Accumulator的值。

  1. val numberList = Array(1,2,3,4,5)

  2. val numberRDD = sc.parallelize(numberList,1)

  3. val sum = sc.accumulator(0)

numberRDD.foreach{m => sum += m}

一:创建persons.txt测试数据文本文件

内容如下:

1001,张三,23
1002,李四,33
1003,王五,18
1004,张三4,23
1005,李四6,33
1006,王五7,18
1007,张三8,23
1008,李四9,33
1009,王五10,18
1010,张三11,13
1011,李四12,33
1012,王五13,18
1013,张三15,23
1014,李四14,29
1015,王五16,18

二:构建maven项目,引入对应依赖

<properties>
        <scala.version>2.11.8</scala.version>
    </properties>

    <repositories>
        <repository>
            <id>repos</id>
            <name>Repository</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
        </repository>
        <repository>
            <id>scala-tools.org</id>
            <name>Scala-Tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>repos</id>
            <name>Repository</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public</url>
        </pluginRepository>
        <pluginRepository>
            <id>scala-tools.org</id>
            <name>Scala-Tools Maven2 Repository</name>
            <url>http://scala-tools.org/repo-releases</url>
        </pluginRepository>
    </pluginRepositories>

    <dependencies>
        <dependency> <!-- Spark -->
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.11</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency> <!-- Spark -->
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.11</artifactId>
            <version>2.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>
    </dependencies>

    <build>
        <sourceDirectory>src/main/scala</sourceDirectory>
        <testSourceDirectory>src/test/scala</testSourceDirectory>
        <plugins>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <version>2.15.2</version>
                <executions>
                    <execution>
                        <id>scala-compile-first</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                        <configuration>
                            <includes>
                                <include>**/*.scala</include>
                            </includes>
                        </configuration>
                    </execution>
                    <execution>
                        <id>scala-test-compile</id>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <appendAssemblyId>false</appendAssemblyId>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>org.jy.data.yh.bigdata.drools.scala.sparkrdd.SparkRDDWordCountFrep</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>org.jy.data.yh.bigdata.drools.scala.sparkrdd.SparkRDDWordCountFrep</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

三,完整代码如下:

package org.jy.data.yh.bigdata.drools.scala.spark.dataframe;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.RowFactory;
import org.apache.spark.sql.SQLContext;
import org.apache.spark.sql.types.DataTypes;
import org.apache.spark.sql.types.StructField;
import org.apache.spark.sql.types.StructType;
import java.util.ArrayList;
import java.util.List;
/**
 * 通过RDD来构建DataFrame:
 * 主要步骤:
 *    读取文本文件,遍历全部行,创建RDD<Row>
 *    构造DataFrame的元数据(MetaData)
 *    基于MetaDat,RDD<Row>来构造持久化
 *    注册称为临时表,进行数据查询
 *    使用用上面的临时表,进行数据查询
 *    对结果进行处理:DataFrame转换成RDD<Row>或者持久化
 */
public class RDD2DataFrameByProgrammatically {

    public static void main(String[] args){ //spark://centoshadoop1:7077,centoshadoop2:7077  集群环境线上运行
        SparkConf sparkConf = new SparkConf().setMaster("local")
                .setAppName("RDD2DataFrameByProgrammatically");
        JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
        SQLContext sqlContext = new SQLContext(javaSparkContext);
        JavaRDD<String> lines = javaSparkContext.textFile("D://jar/persons.txt");// 线上需要改为spark物理地址

        // 第一步:在RDD的基础上创建类型为Row的RDD
        JavaRDD<Row>  personsRDD = lines.map(new Function<String, Row>() {
            @Override
            public Row call(String line) throws Exception {
                System.out.println("当前正在解析的文本行内容为: "+line);
                String[] splited = line.split(",");
                return RowFactory.create(Integer.valueOf(splited[0]),splited[1],Integer.valueOf(splited[2]));
            }
        });
        /**
         * 第二步:动态构造DataFrame的元数据,一般而言,有多少列及每列的具体类型可能来自于JSON文件,也可能来自与DB
         */
        List<StructField> structFields = new ArrayList<>();
        structFields.add(DataTypes.createStructField("id",DataTypes.IntegerType,true));
        structFields.add(DataTypes.createStructField("name",DataTypes.StringType,true));
        structFields.add(DataTypes.createStructField("age",DataTypes.IntegerType,true));
        // 构建StructType,用于最后DataFrame元数据的描述
        StructType structType = DataTypes.createStructType(structFields);
        // 第三步:基于MetaData及RDD<Row>来构造DataFrame
        Dataset<Row> personsDF = sqlContext.createDataFrame(personsRDD,structType);
        // 第四步:注册成为临时表以供后续的SQL查询操作
        personsDF.registerTempTable("persons");
        // 第五步: 进行数据的多维度分析
        Dataset<Row> result = sqlContext.sql("select * from persons where age > 20");
        // 第六步:对结果进行处理,包括有DataFrame转换为RDD<Row>,以及结果持久化
        List<Row> listRow = result.javaRDD().collect();
        for(Row row : listRow){
            System.out.println(row);
        }
        javaSparkContext.close();

    }
}

四,运行该程序,输出结果如下:

20/04/12 22:36:33 INFO LineRecordReader: Found UTF-8 BOM and skipped it
当前正在解析的文本行内容为: 1001,张三,23
当前正在解析的文本行内容为: 1002,李四,33
当前正在解析的文本行内容为: 1003,王五,18
当前正在解析的文本行内容为: 1004,张三4,23
当前正在解析的文本行内容为: 1005,李四6,33
20/04/12 22:36:33 INFO CodeGenerator: Code generated in 42.2667 ms
当前正在解析的文本行内容为: 1006,王五7,18
当前正在解析的文本行内容为: 1007,张三8,23
当前正在解析的文本行内容为: 1008,李四9,33
当前正在解析的文本行内容为: 1009,王五10,18
当前正在解析的文本行内容为: 1010,张三11,13
当前正在解析的文本行内容为: 1011,李四12,33
当前正在解析的文本行内容为: 1012,王五13,18
当前正在解析的文本行内容为: 1013,张三15,23
当前正在解析的文本行内容为: 1014,李四14,29
当前正在解析的文本行内容为: 1015,王五16,18
20/04/12 22:36:33 INFO Executor: Finished task 0.0 in stage 0.0 (TID 0). 2859 bytes result sent to driver
20/04/12 22:36:33 INFO TaskSetManager: Finished task 0.0 in stage 0.0 (TID 0) in 268 ms on localhost (executor driver) (1/1)
20/04/12 22:36:33 INFO TaskSchedulerImpl: Removed TaskSet 0.0, whose tasks have all completed, from pool 
20/04/12 22:36:33 INFO DAGScheduler: ResultStage 0 (collect at RDD2DataFrameByProgrammatically.java:60) finished in 0.344 s
20/04/12 22:36:33 INFO DAGScheduler: Job 0 finished: collect at RDD2DataFrameByProgrammatically.java:60, took 0.392485 s
[1001,张三,23]
[1002,李四,33]
[1004,张三4,23]
[1005,李四6,33]
[1007,张三8,23]
[1008,李四9,33]
[1011,李四12,33]
[1013,张三15,23]
[1014,李四14,29]
20/04/12 22:36:33 INFO SparkUI: Stopped Spark web UI at http://192.168.227.1:4040
20/04/12 22:36:33 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped!
20/04/12 22:36:33 INFO MemoryStore: MemoryStore cleared
20/04/12 22:36:33 INFO BlockManager: BlockManager stopped
20/04/12 22:36:33 INFO BlockManagerMaster: BlockManagerMaster stopped
20/04/12 22:36:33 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped!
20/04/12 22:36:33 INFO SparkContext: Successfully stopped SparkContext
20/04/12 22:36:33 INFO ShutdownHookManager: Shutdown hook called
20/04/12 22:36:33 INFO ShutdownHookManager: Deleting directory C:\Users\Administrator\AppData\Local\Temp\spark-b73dd7f4-1fe3-49b7-a06a-8c4982b8cedd

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值