史上最简单的spark教程第七章-spark的数据读取与保存Java案例实践


Spark数据读取与保存

史上最简单的spark教程
所有代码示例地址:https://github.com/Mydreamandreality/sparkResearch

(提前声明:文章由作者:张耀峰 结合自己生产中的使用经验整理,最终形成简单易懂的文章,写作不易,转载请注明)
(文章参考:Elasticsearch权威指南,Spark快速大数据分析文档,Elasticsearch官方文档,实际项目中的应用场景)
(帮到到您请点点关注,文章持续更新中!)
Git主页 https://github.com/Mydreamandreality

  • 大数据大数据,我们搞了这么久,还不知道数据是如何来的,流向哪里,在这里我把这个具体的流程给大家说一下

  • 数据的来源:
    • 数据的源头有很多,如果您是做电商系统的,那么用户的信息,商品信息,浏览记录等等数据,
    • 如果您和我一样是从事安全行业的,那么数据的来源就是流量,日志,漏洞,引擎等等数据
  • 在对这些数据进行分析之前
    • 首先我们需要进行数据清洗,通过一系列的ETL(ETL是英文Extract-Transform-Load的缩写,用来描述将数据从来源端经过萃取(extract)、转置(transform)、加载(load)至目的端的过程。ETL一词较常用在数据仓库,但其对象并不限于数据仓库,来源百度百科-------),把数据清洗,整理,把脏数据抛弃掉,把数据的格式转换成我们想要的
  • 到现在数据是有了,但是数据该存到哪?
    • 如果你选择的是hadoop生态系统,那么你可以存储到HDFS,基于磁盘进行运算,而spark是基于内存运算的,这也是它速度快的原因之一,spark不负责持久层操作,所以这个时候我们就需要借助外部的存储系统,比如HDFS,HBASE等
  • 那么到这里持久层是解决了,那么数据的存储格式应该是什么
    • 那其实这个倒没有业内的标准,你可以选择你最合适的方式
    • 比如文本文件,JSON,列式存储技术等等 [我们的选择是列式存储parquet]
  • 最后问题又来了,那么处理完的数据应该流向哪里
    • 这个也很简单,看业务场景
    • 可以直接给前端做展示,也可以保存到kafka等队列中,或者HDFS,elasticsearch

这里的流程是批处理的场景,就是对数据计算实时性要求不高的场景

之后写到流处理 spark streaming的时候会和这个做个对比


这里有个列表,有spark支持的主流的文件格式
格式名称 结构化 备注
文本文件 普通的文本文件,一行一条记录
JSON 半结构化 半结构化的数据
CSV 基于文本的,电子表格
SequenceFiles 键值对数据,常见的Hadoop文件格式
paquet 半结构化 列式存储,效率更高
  • 在这里补充一哈,Spark对不同文件格式的读取和保存方式都很简单
  • spark会根据文件扩展名称选择对应的处理方式,这个过程是spark程序封装好的,开发人员无需关心这些
我在这写一下具体操作这些文件的案例

文本文件
  • 读取文本文件太简单了,之前我们的案例中已经写了N次了
  • 我们读取文本文件为RDD时,每一行都会成为RDD的一个元素
  • 也可以读取成pairRDD,文件名称isKey,内容isVaalue
代码案例
//读文本文件
JavaRDD input = sc.textFile("file:///home/holden/repos/spark/README.md")
//写文本文件
input.saveAsTextFile("/usr/local/data1");


如果我们输入的参数是一个目录,那么整个目录下的文件都会被读取到RDD中
也可以使用通配符 * 读取通配文件 比如 *.pqrauet
我们也可以使用sparkContext.wholeTextFiles(),返回一个pairRDD,键是文件名

JSON文件的读写
  • JSON就不多做解释了,在我们平常开发后台接口的时候大部分的数据都是以JSON的格式传递给前端去做渲染工作的.
  • 读取JSON最简单的方式就是将数据当做文本文件读取,然后使用JSON解析器对RDD中的元素进行解析
  • spark-sql也可以读取json文件,这个如果后续写到的话再说
代码案例
    //先定义一个读取JSON的解析器
    static class ParseJson implements FlatMapFunction, Person> {
        @Override
        public Iterator call(Iterator lines) {
            List arrayList = new ArrayList<>();
            ObjectMapper objectMapper = new ObjectMapper();
            while (lines.hasNext()) {
                try {
                    String line = lines.next();
                    arrayList.add(objectMapper.readValue(line, Person.class));
                } catch (IOException e) {
                    e.printStackTrace();
                    //此处失败跳过
                }
            }
            return arrayList.iterator();
        }
    }
    
    
     //写的JSON解析器
    static class wirteJson implements FlatMapFunction, String> {
        @Override
        public Iterator call(Iterator personIterator) {
            List arrayList = new ArrayList<>();
            ObjectMapper objectMapper = new ObjectMapper();
            while (personIterator.hasNext()) {
                try {
                    arrayList.add(objectMapper.writeValueAsString(personIterator));
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
            }
            return arrayList.iterator();
        }
    }
        //条件过滤
        static class filterData implements Function {
        @Override
        public Boolean call(Person v1) {
            return v1.get("filed").toString().contains("测试");
        }
    }
    
    //把解析器传递到函数中
        public static void jsonFile(JavaSparkContext sparkContext) {
        //json文件的读
        JavaRDD rdd = sparkContext.textFile("url");
        JavaRDD result = rdd.mapPartitions(new ParseJson()).filter(new filterData());
          //写
        result.saveAsTextFile("url");
    }
mapPartitions使用这个是因为在解析中它的效率要比,map高,
至于原理我也不太清楚,后续再进行一个补充把 [TODO]
Person这个是二进制解析的函数

CSV文件的读写(逗号分隔值与制表符分隔值)
  • CSV文件应该和JSON文件差不多,都是把这些文件当做普通文本读取再进行解析,
  • 主要的差别就是CSV的数据记录没有关联的字段名称,只有对应的序号,需要手动分解和组合特定的字段
  • 我们用opencsv这个库进行解析
代码案例
static class readCsv implements Function{
        @Override
        public String[] call(String v1) throws Exception {
            CsvReader reader = new CsvReader(new StringReader(v1));
            return reader.getValues();
        }
    }

    protected static void run(JavaSparkContext sparkContext){
        JavaRDD csvFile1 = sparkContext.textFile("url");
        JavaRDD csvData = csvFile1.map(new readCsv());
    }

SequenceFile
  • sequenceFile是由没有相对关系的键值对文件组成的常用Hadoop格式
  • sequenceFIle有同步标记,通过这个可以定位到某个点,然后和记录的边界对齐
  • 这样spark可以多个节点高效的并行读取sequence文件
  • sequence提供了单独的数据类型,需要转换成java的数据类型
  • 单独的数据类型可以理解为是Hadoop因为JDK自带的基础数据类型序列化时效率较低,所以基于JDK的数据类型做了一定的优化
数据对应关系为:

Long ===== LongWritable
String ===== Text
Integer ===== IntWritable
Null ===== NullWritable

代码案例
    //读
    protected static void run(JavaSparkContext sparkContext) {
        JavaPairRDD javaPairRDD = sparkContext.sequenceFile("url", Text.class, IntWritable.class);
        //转换成java的数据类型
        JavaPairRDD PairRDD = javaPairRDD.mapToPair(new sequenceToConvert());
        //写pairRDD.saveAsHadoopFile("url",Text.class,IntWritable.class,SequenceFileOutputFormat.class);

    }


    static class sequenceToConvert implements PairFunction, String, Integer> {

        @Override
        public Tuple2 call(Tuple2 textIntWritableTuple2) {
            return new Tuple2(textIntWritableTuple2._1.toString(), textIntWritableTuple2._2.get());
        }
    }

上面的案例只是一部分,但是思想都是统一的,暂时先写到这里吧.
当然除了我们上面用的到从文件系统读写,还可以操作非文件系统数据源
非文件系统数据源:HBase,Elasticsearch,Redis,kafka等
比如:
  • hadoopDataSet()函数
  • saveHadoopDataSet()函数
  • newApiHadoopDataSet()函数
  • saveAsNewAPIhadoopDataSet()函数

非文件系统数据源后续单独写一章节把,涉及的东西太多了


文件压缩
  • 在大数据的开发中,我们一般都需要对数据进行压缩,优化数据的存储空间以及网络的传输速率,
  • 对于大多数的hadoop输出格式都支持压缩
  • 而spark原生的输入方式 textFIle() sequenceFile 可以自动处理一些压缩的类型,读取时压缩解码器会自动推测压缩类型
  • 这个操作只适用于我们写出到文件系统,如果想把压缩文件写入数据库可能需要基于数据库进行配置
数据读取与保存的核心案例基本都写的差不多了,后续更新文件系统相关的教程
有任何问题欢迎留言交流–

你可能感兴趣的:(#,spark,#,大数据,拥抱大数据)