15、Spark Sql(一),生成DataFrame的方式
一、一些基本概念
1、Shark
Shark是基於Spark計算框架之上且相容Hive語法的Sql執行引擎,由於底層計算採用了Spark,效能比MapReduce的Hive普遍快2倍以上,當資料全部load到記憶體的話,快10倍以上。
2、相對於Shark,Spark Sql的優勢
(1)Spark Sql產生的根本原因,其完全脫離了Hive的限制
(2)Spark Sql支援查詢原生的RDD,這點極為關鍵。RDD是Spark平臺的核心概念,是Spark能高效處理大資料的各種場景的基礎
(3)能夠在Scaka中寫Sql語句,支援簡單的Sql語法檢查,能在Scala中寫Hive語句訪問Hive,並將結果取回作為RDD使用
3、DataFrame
(1)分散式資料容器
(2)像傳統的資料庫二維表格,除了資料外,還掌握資料的結構資訊,即Schema
(3)支援巢狀資料型別(struct,array和map)
二、Spark Sql優化
1、謂詞下推
2、將表資料比較小的表自動廣播
SELECT table1.name,table2.score
FROM table1 JOIN table2 ON (table1.id=table2.id)
WHERE table1.age>50 AND table2.score>90
三、Spark Sql第一個程式
public class DataFrameOpsJsonRdd {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("dataFrameOpsJsonRdd").setMaster("local");
JavaSparkContext jsc = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(jsc);
// 建立一個本地集合
List<String> nameList = Arrays.asList(
"{'name':'zhangsan', 'age':55}",
"{'name':'lisi', 'age':30}",
"{'name':'lisisi', 'age':30}",
"{'name':'wangwu', 'age':19}");
List<String> scoreList = Arrays.asList(
"{'name':'zhangsan', 'score':100}",
"{'name':'lisi', 'score':99}");
// 並行化成一個RDD, 現在RDD中元素格式是json格式
JavaRDD<String> nameRDD = jsc.parallelize(nameList);
JavaRDD<String> scoreRDD = jsc.parallelize(scoreList);
// 通過json格式的RDD建立dataframe
DataFrame nameDF = sqlContext.read().json(nameRDD);
DataFrame scoreDF = sqlContext.read().json(scoreRDD);
// DataFrame resultDF = nameDF.join(scoreDF, nameDF.col("name").$eq$eq$eq(scoreDF.col("name")))
// .select(nameDF.col("name"), nameDF.col("age"), scoreDF.col("score"));
// resultDF.show();
// 把dataframe的內容註冊成一張臨時表
nameDF.registerTempTable("name");
scoreDF.registerTempTable("score");
String sql = "select name.name, name.age, score.score from name join score on (name.name = score.name)";
sqlContext.sql(sql).show();
}
}
resultDF.show();完了之後,資料才會載入到記憶體中
總結:建立DataFrame的方式
1、通過json格式的檔案建立(json格式的檔案不能巢狀,如果是巢狀需要自己去解析好了,再用Spark Sql)
2、通過json格式的RDD建立(RDD中的資料是String型別,但資料是json格式)
3、通過一個普通的RDD(非json格式)建立
3.1、反射的方式
(1)自定義的類必須是public
(2)自定義的類必須是可序列化的
(3)RDD轉成DataFrame的時候,DataFrame中列的順序會根據自定義類中的欄位名按照字典順序進行排序
(4)從DataFrame中取值的時候要注意列的順序,getAs("列名")
(5)不建議使用,如果以後想增加新的列,或者修改已有的列的資訊,麻煩
public class RDD2DataFrameByReflect {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("RDD2DataFrameByReflect").setMaster("local");
JavaSparkContext jsc = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(jsc);
JavaRDD<String> lines = jsc.textFile("Peoples.txt");
// 拆開每一行的資料,賦值給Person
JavaRDD<Person> personRDD = lines.map(new Function<String, Person>() {
private static final long serialVersionUID = 1L;
@Override
public Person call(String lines) throws Exception {
String[] split = lines.split(",");
Person person = new Person();
person.setId(Integer.valueOf(split[0]));
person.setName(split[1]);
person.setAge(Integer.valueOf(split[2]));
return person;
}
});
DataFrame dataFrame = sqlContext.createDataFrame(personRDD, Person.class);
dataFrame.registerTempTable("personTable");
DataFrame resultDataFrame = sqlContext.sql("select * from personTable where age > 7");
resultDataFrame.show();
// 把DataFrame轉成Person型別的RDD
JavaRDD<Row> rowRDD = resultDataFrame.javaRDD();
JavaRDD<Person> pRDD = rowRDD.map(new Function<Row, Person>() {
private static final long serialVersionUID = 1L;
@Override
public Person call(Row row) throws Exception {
int id = row.getAs("id");
String name = row.getAs("name");
int age = row.getAs("age");
Person p = new Person(id, name, age);
return p;
}
});
pRDD.foreach(new VoidFunction<Person>() {
@Override
public void call(Person person) throws Exception {
System.out.println("person:" + person);
}
});
}
}
3.2、動態建立Schema方式將一個普通的RDD變成DataFrame(可以將列的資訊放到外部儲存中,修改的時候只需要去修改配置檔案或資料庫即可)
public class RDD2DataFrameByProgrammatically {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("RDD2DataFrameByProgrammatically").setMaster("local");
JavaSparkContext jsc = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(jsc);
JavaRDD<String> lines = jsc.textFile("Peoples.txt");
// 拆分每條記錄
JavaRDD<Row> rowRDD = lines.map(new Function<String, Row>() {
private static final long serialVersionUID = 1L;
@Override
public Row call(String line) throws Exception {
String[] split = line.split(",");
return RowFactory.create(Integer.valueOf(split[0]), split[1], Integer.valueOf(split[2]));
}
});
// 動態構造DataFrame的後設資料,
// 一般而言,有多少列以及每列的具體型別可能來自於Json,也可能來自於DB
ArrayList<StructField> structFields = new ArrayList<StructField>();
// 我們可以將每列的資訊儲存到外部檔案或者資料庫中
// 在建立schema的時候去外部檔案或者資料庫中查詢
// 修改每一列的列名以及列的型別的時候就變的非常方便了
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 schema = DataTypes.createStructType(structFields);
// 基於已有的MetaData以及RDD<Row> 來構造DataFrame
DataFrame dataFrame = sqlContext.createDataFrame(rowRDD, schema);
dataFrame.registerTempTable("persons");
DataFrame resultDF = sqlContext.sql("select * from persons where age > 6");
resultDF.show();
JavaRDD<Row> resultRDD = resultDF.javaRDD();
resultRDD.foreach(new VoidFunction<Row>() {
private static final long serialVersionUID = 1L;
@Override
public void call(Row row) throws Exception {
int id = row.getInt(0);
String name = row.getString(1);
int age = row.getInt(2);
System.out.println("id:" + id + " name:" + name + " age:" + age);
}
});
jsc.stop();
}
}
4.1、通過讀取parquet格式的檔案建立
public class ParquetLoadData {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("ParquetLoadData").setMaster("local");
JavaSparkContext jsc = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(jsc);
// 從hdfs上讀取users.parquet
DataFrame personDF = sqlContext.read().parquet("hdfs://master:9000/input/users.parquet");
//測試時如果沒有users.parquet檔案,可以自己手動生成一個
// List<Person> personList = new ArrayList<Person>();
// personList.add(new Person(1, "xinxin1", 21));
// personList.add(new Person(2, "xinxin2", 23));
// personList.add(new Person(3, "xinxin2", 25));
// JavaRDD<Person> personRDD = jsc.parallelize(personList);
// DataFrame personDF = sqlContext.createDataFrame(personRDD, Person.class);
// personDF.show();
// 結果DataFrame內容寫回到hdfs
personDF.write().format("parquet").mode(SaveMode.Overwrite)
.save("hdfs://master:9000/output/result.parquet");
}
}
4.2、parquet格式的檔案能夠自動推斷分割槽
如:hdfs上檔案路徑為/users/gender=male/country=US/users.parquet,讀取內容如下
根據目錄名增加兩列:gender,country,防止暴力掃描全表
說明:(1)目錄格式必須為KV格式
(2)如何將txt檔案轉成parquet檔案?
RDD -> DataFrame -> write
RDD.saveAsHadoopFIle()
5、讀取mysql中資料建立一個DataFrame
public class JDBCDataSource {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("JDBCDataSource").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);
SQLContext sqlContext = new SQLContext(sc);
// 方法1、分別將mysql中兩張表的資料載入為DataFrame
/*
* Map<String, String> options = new HashMap<String, String>();
* options.put("url", "jdbc:mysql://hadoop1:3306/testdb");
* options.put("driver", "com.mysql.jdbc.Driver");
* options.put("user","spark");
* options.put("password", "spark2016");
* options.put("dbtable", "student_info");
* DataFrame studentInfosDF = sqlContext.read().format("jdbc").options(options).load();
*
* options.put("dbtable", "student_score");
* DataFrame studentScoresDF = sqlContext.read().format("jdbc") .options(options).load();
*/
// 方法2、分別將mysql中兩張表的資料載入為DataFrame
DataFrameReader reader = sqlContext.read().format("jdbc");
reader.option("url", "jdbc:mysql://hadoop1:3306/testdb");
reader.option("dbtable", "student_info");
reader.option("driver", "com.mysql.jdbc.Driver");
reader.option("user", "spark");
reader.option("password", "spark2016");
DataFrame studentInfosDF = reader.load();
reader.option("dbtable", "student_score");
DataFrame studentScoresDF = reader.load();
// 將兩個DataFrame轉換為JavaPairRDD,執行join操作
studentInfosDF.registerTempTable("studentInfos");
studentScoresDF.registerTempTable("studentScores");
String sql = "SELECT studentInfos.name,age,score "
+ " FROM studentInfos JOIN studentScores"
+ " ON (studentScores.name = studentInfos.name)"
+ " WHERE studentScores.score > 80";
DataFrame resultDF = sqlContext.sql(sql);
resultDF.show();
//使用forachPartition來優化
resultDF.javaRDD().foreach(new VoidFunction<Row>() {
private static final long serialVersionUID = 1L;
@Override
public void call(Row row) throws Exception {
String sql = "insert into good_student_info values(" + "'" + String.valueOf(row.getString(0)) + "',"
+ Integer.valueOf(String.valueOf(row.get(1))) + ","
+ Integer.valueOf(String.valueOf(row.get(2))) + ")";
Class.forName("com.mysql.jdbc.Driver");
Connection conn = null;
Statement stmt = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://hadoop1:3306/testdb", "spark", "spark2016");
stmt = conn.createStatement();
stmt.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
}
}
});
/**
* 將SparkContext 關閉,釋放資源
*/
sc.close();
}
}
相關文章
- Spark API 全集(1):Spark SQL Dataset & DataFrame APISparkAPISQL
- Spark SQL學習——DataFrame和DataSetSparkSQL
- Spark SQL中的RDD與DataFrame轉換SparkSQL
- 【Spark篇】---SparkSQL初始和建立DataFrame的幾種方式SparkSQL
- Pandas 基礎 (3) - 生成 Dataframe 的幾種方式
- Spark建立空的DataFrameSpark
- Spark修煉之道(進階篇)——Spark入門到精通:第八節 Spark SQL與DataFrame(一)SparkSQL
- Spark SQL,如何將 DataFrame 轉為 json 格式SparkSQLJSON
- Spark註冊UDF函式,用於DataFrame DSL or SQLSpark函式SQL
- Spark DataFrame的groupBy vs groupByKeySpark
- SparkSQL /DataFrame /Spark RDD誰快?SparkSQL
- spark dataframe 型別轉換Spark型別
- Apache Spark Dataframe Join語法教程ApacheSpark
- Spark SQL 程式設計API入門系列之Spark SQL的作用與使用方式SparkSQL程式設計API
- sparkrdd轉dataframe的兩種方式Spark
- Spark SQL 1.3.0 DataFrame介紹、使用及提供了些完整的資料寫入SparkSQL
- 從 Spark 的 DataFrame 中取出具體某一行詳解Spark
- spark: RDD與DataFrame之間的相互轉換Spark
- spark 的方式Spark
- pyspark.sql.DataFrame與pandas.DataFrame之間的相互轉換SparkSQL
- Spark SQL:4.對Spark SQL的理解SparkSQL
- Spark SQL | 目前Spark社群最活躍的元件之一SparkSQL元件
- pandas 實現兩個dataframe相減的方式
- Spark系列 - (3) Spark SQLSparkSQL
- spark sql在scala中使用的兩種方式,以及其他一些知識點SparkSQL
- Spark SQLSparkSQL
- python--建立一個與已有DataFrame行數相同的資料框的方式Python
- Spark SQL 教程: 通過示例瞭解 Spark SQLSparkSQL
- Spark SQL 教程: 透過示例瞭解 Spark SQLSparkSQL
- Spark_SQlSparkSQL
- Spark的四種部署方式概括Spark
- spark學習筆記--Spark SQLSpark筆記SQL
- 【SQL】查詢資料的方式 (一)SQL
- Cris 的 Spark SQL 筆記SparkSQL筆記
- Spark2 Dataset DataFrame空值null,NaN判斷和處理SparkNullNaN
- Spark 系列(九)—— Spark SQL 之 Structured APISparkSQLStructAPI
- spark2.2.0 配置spark sql 操作hiveSparkSQLHive
- Spark SQL | Spark,從入門到精通SparkSQL