Spark2.x学习笔记(二) - Spark SQL

Spark-Sql 介绍
1、spark 介绍

Hive是Shark的前身,Shark是SparkSQL的前身,SparkSQL产生的根本原因是其完全脱离了Hive的限制。
1> SparkSQL支持查询原生的RDD。 RDD是Spark平台的核心概念,是Spark能够高效的处理大数据的各种场景的基础。
2> 能够在Scala中写SQL语句。支持简单的SQL语法检查,能够在Scala中写Hive语句访问Hive数据,并将结果取回作为RDD使用
3> SparkSQL的数据源可以是JSON类型的字符串,JDBC,Parquent,Hive,HDFS等。

2、DataFrame

在这里插入图片描述
DataFrame也是一个分布式数据容器。与RDD类似,然而DataFrame更像传统数据库的二维表格,除了数据以外,还掌握数据的结构信息,即schema。同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。从API易用性的角度上 看, DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。
DataFrame的底层封装的是RDD,只不过RDD的泛型是Row类型。

代码调用
1、json 格式转换为sql 方式

JAVA

public class SparkSQLTest {
    public static void main(String[] args) {
        SparkConf sparkConf = new SparkConf().setMaster("local").setAppName("wc_2");
        JavaSparkContext sc = new JavaSparkContext(sparkConf);

        SQLContext sqlContext = new SQLContext(sc);
        Dataset<Row> dataset =  sqlContext.read().format("json").load("data/data.json");

        dataset.show();  // 显示数据
        dataset.printSchema(); // 显示字段
        // 条件过滤
        // Dataset<Row> dataset2 = dataset.select(dataset.col("name"),dataset.col("age")).where(dataset.col("age").gt(18));
        // dataset2.show();

        // sql 方式
        dataset.registerTempTable("tem");
        Dataset<Row> dataset3 =  sqlContext.sql("select name,age from tem where age > 18");
        dataset3.show();
        sc.close();
    }
}

SCALA方式

object SparkSqlTest {

  def main(args: Array[String]): Unit = {
    var conf = new SparkConf().setMaster("local").setAppName("myapp");
    var sc = new SparkContext(conf);
    var sqlContext = new SQLContext(sc);
    val df: DataFrame =  sqlContext.read.format("json").load("data/data.json");
    df.show()
    df.createOrReplaceTempView("t1");
    var df2 = sqlContext.sql("select name,age from t1 where age > 18");
    df2.show();
  }
}
2、 非Json 格式RDD 创建
public class SparkSQLForProject {
    public static void main(String[] args) {
        SparkConf sparkConf = new SparkConf().setMaster("local").setAppName("wc_2");
        JavaSparkContext sc = new JavaSparkContext(sparkConf);
        SQLContext sqlContext = new SQLContext(sc);
        JavaRDD<String> lineRdd = sc.textFile("data/person.txt");
        // 封装为 Person 类型
        JavaRDD<Person> personRdd = lineRdd.map(new Function<String, Person>() {
            @Override
            public Person call(String s) throws Exception {
                Person person = new Person();
                person.setId(s.split(",")[0]);
                person.setName(s.split(",")[1]);
                person.setAge(Integer.valueOf(s.split(",")[2]));
                return person;
            }
        });
        // 遍历一下看看数据
        personRdd.foreach(new VoidFunction<Person>() {
            @Override
            public void call(Person person) throws Exception {
                System.out.println(person);
            }
        });
        // 将RDD 转换为 dataSet
        Dataset<Row> dataFrame = sqlContext.createDataFrame(personRdd, Person.class);
        dataFrame.show();
        dataFrame.createOrReplaceTempView("person");
        sqlContext.sql("select id, name,age from person").show();
        // rdd<row> 转换为 rdd<row>
        JavaRDD<Row> rowJavaRDD = dataFrame.javaRDD();
        JavaRDD<Person> map = rowJavaRDD.map(new Function<Row, Person>() {
            @Override
            public Person call(Row row) throws Exception {
                Person person = new Person();
                person.setId(row.getAs("id"));
                person.setAge(row.getAs("name"));
                person.setAge(row.getAs("age"));
                return person;
            }
        });
        sc.close();
    }
}

第二种创建方式

public class SparkSQLTest2 {
    public static void main(String[] args) {
        SparkConf sparkConf = new SparkConf().setMaster("local").setAppName("wc_2");
        JavaSparkContext sc = new JavaSparkContext(sparkConf);
        SQLContext sqlContext = new SQLContext(sc);
        JavaRDD<String> lineRdd = sc.textFile("data/person.txt");

        JavaRDD<Row> map = lineRdd.map(new Function<String, Row>() {
            @Override
            public Row call(String s) throws Exception {
                return new RowFactory().create(
                        s.split(",")[0],
                        s.split(",")[1],
                        Integer.valueOf(s.split(",")[2])
                );
            }
        });

        List<StructField> asList = Arrays.asList(
                DataTypes.createStructField("id",DataTypes.StringType,true),
                DataTypes.createStructField("name",DataTypes.StringType,true),
                DataTypes.createStructField("age",DataTypes.IntegerType,true)
        );
        StructType schema = DataTypes.createStructType(asList);
        Dataset<Row> dataFrame = sqlContext.createDataFrame(map, schema);
        dataFrame.show();
        dataFrame.printSchema();
        sc.close();
    }
}

生成文件

 // dataSet数据保存成文件
dataset.write().mode(SaveMode.Overwrite).format("parquet").save("./sparksql/parquet");
dataset.write().mode(SaveMode.Append).parquet("./sparksql/parquet");
// 文件读取为dataSet
sqlContext.read().format("parquet").load("./sparksql/parquet");
// 或
sqlContext.read().parquet("./sparksql/parquet");

数据库方式读取

// 数据库方式读取
Map<String, String> options = new HashMap<String,String>();
options.put("url", "jdbc:mysql://192.168.179.4:3306/spark");
options.put("driver", "com.mysql.jdbc.Driver");
options.put("user", "root");
options.put("password", "123456");
options.put("dbtable", "person");
Dataset<Row> person = sqlContext.read().format("jdbc").options(options).load();
person.show();
person.registerTempTable("person");

// 读取mysql
Properties properties = new Properties();
properties.setProperty("user", "root");
properties.setProperty("password", "123456");
person.write().mode(SaveMode.Overwrite).jdbc("jdbc:mysql://192.168.179.4:3306/spark", "result", properties);

数据量非常小的话可以设置分区小点sparkConf.set("spark.sql.shuffle.partition","1");

关于savemode:
1、默认为SaveMode.ErrorIfExists模式,该模式下,如果数据库中已经存在该表,则会直接报异常,导致数据不能存入数据库.另外三种模式如下:
2、SaveMode.Append 如果表已经存在,则追加在该表中;若该表不存在,则会先创建表,再插入数据;
3、SaveMode.Overwrite 重写模式,其实质是先将已有的表及其数据全都删除,再重新创建该表,最后插入新的数据;
4、SaveMode.Ignore 若表不存在,则创建表,并存入数据;在表存在的情况下,直接跳过数据的存储,不会报错。

Spark-Sql 读取Hive

1、 将hive_home /conf 下的hive-site 复制到 spark_home/conf 下,内容如下

 <property>
  	 <name>hive.metastore.uris</name>
     <value>thrift://node3:9083</value>
  </property>

2、 启动spark-shell

./spark-shell --master spark://node2:7077

详细指令:

./spark-shell 
--master spark://node1:7077,node2:7077 
--executor-cores 1 
--executor-memory 1g 
--total-executor-cores 1

node2 为 spark 的master
在这里插入图片描述

3、 执行sql

    import org.apache.spark.sql.{DataFrame, SQLContext, SparkSession}
    val spark = SparkSession.builder().appName("hive").enableHiveSupport().getOrCreate()
    spark.sql("show tables");

在这里插入图片描述

自定义函数

1、UDF函数 (针对于每一列的函数)

实现功能:统计一下每个字符串的长度

public class SparkForUDF {
    public static void main(String[] args) {
        SparkConf conf = new SparkConf().setMaster("local").setAppName("test");
        JavaSparkContext sc = new JavaSparkContext(conf);
        SQLContext context = new SQLContext(sc);

        JavaRDD<String> line = sc.parallelize(Arrays.asList("lei", "ying", "chen"));
        // 转换为rdd<row>
        JavaRDD<Row> map = line.map(new Function<String, Row>() {
            @Override
            public Row call(String s) throws Exception {
                return RowFactory.create(s);
            }
        });
        // 封装为dataset数据
        List<StructField> typeList = new ArrayList<>();
        typeList.add(DataTypes.createStructField("name", DataTypes.StringType,true));
        StructType schema = DataTypes.createStructType(typeList);
        Dataset<Row> dataFrame = context.createDataFrame(map, schema);
        // 创建UDF 函数
        context.udf().register("len", new UDF1<String, Integer>() {
            @Override
            public Integer call(String o) throws Exception {
                return o.length();
            }
        }, DataTypes.IntegerType);
        // 注册为临时变
        dataFrame.createOrReplaceTempView("t1");
        context.sql("select name,len(name) from t1").show();
        sc.close();
    }
}

2、 自定义聚合函数

功能: 统计相同名称的个数

public class SparkForUDAF {
    public static void main(String[] args) {
        SparkConf sparkConf = new SparkConf().setMaster("local").setAppName("UDAF");
        JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
        SQLContext sqlContext = new SQLContext(javaSparkContext);

        JavaRDD<String> lineRdd = javaSparkContext.parallelize(Arrays.asList(
                "java", "php", "photo", "c#", "java", "c", "java", "php"
        ));
        JavaRDD<Row> rowRdd = lineRdd.map(new Function<String, Row>() {
            @Override
            public Row call(String s) throws Exception {
                return RowFactory.create(s);
            }
        });

        // 自定义函数
        sqlContext.udf().register("strcount", new UserDefinedAggregateFunction() {
            // 指定输入字段的字段及类型
            @Override
            public StructType inputSchema() {
                return DataTypes.createStructType(Arrays.asList(
                        DataTypes.createStructField("name", DataTypes.StringType,true)
                ));
            }
            // 初始化一个内部的自己定义的值
            @Override
            public void initialize(MutableAggregationBuffer buffer) {
                buffer.update(0,0);
            }
            // 分组中的一个组, 每当组里来一个人就 +1
            @Override
            public void update(MutableAggregationBuffer buffer, Row input) {
                buffer.update(0,buffer.getInt(0)+1);
            }
            /**
             * 合并 update操作,可能是针对一个分组内的部分数据,在某个节点上发生的 但是可能一个分组内的数据,会分布在多个节点上处理
             * 此时就要用merge操作,将各个节点上分布式拼接好的串,合并起来
             * buffer1.getInt(0) : 大聚和的时候 上一次聚合后的值
             * buffer2.getInt(0) : 这次计算传入进来的update的结果
             * 这里即是:最后在分布式节点完成后需要进行全局级别的Merge操作
             */
            @Override
            public void merge(MutableAggregationBuffer buffer1, Row buffer2) {
                buffer1.update(0,(buffer1.getInt(0)+buffer2.getInt(0)));
            }
            // 指定返回数据类型
            @Override
            public DataType dataType() {
                return DataTypes.IntegerType;
            }

            @Override
            public boolean deterministic() {
                return true;
            }
            /**
             * 在进行聚合操作的时候所要处理的数据的结果的类型
             */
            @Override
            public StructType bufferSchema() {
                return DataTypes.createStructType(Arrays.asList(
                   DataTypes.createStructField("name", DataTypes.IntegerType,true)
                ));
            }
            //最终结果
            @Override
            public Object evaluate(Row buffer) {
                return buffer.getInt(0);
            }
        });

        StructType structType = DataTypes.createStructType(Arrays.asList(
                DataTypes.createStructField("name",DataTypes.StringType,true)
        ));
        Dataset<Row> dataFrame = sqlContext.createDataFrame(rowRdd, structType);
        dataFrame.createOrReplaceTempView("t1");
        sqlContext.sql("select name,strcount(name) from t1 group by name").show();
        javaSparkContext.close();
    }
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值