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();
}
}