說明
本博客週五更新 本文記錄spark 分佈式數據類型DataSet的基本原理和使用方法。 DataSet是Spark1.6添加的分佈式數據集合,Spark2.0合併DataSet和DataFrame數據集合API,DataFrame變成DataSet的子集。 DataSet繼承RDD優點,並使用Spark SQL優化的執行引擎。支持JVM對象構建,支持函數式轉換(map/flatmap/filter)等多種操作
資料
- 代碼博客
優勢
- 靜態類型和運行時類型檢查,編譯時檢查類型,避免程序運行中因類型錯誤造成任務失敗。
- High-level抽象以及結構化半結構化數據集的自定義視圖
- DataFrame是Dataset[Row]的集合,把結構化數據集視圖轉換成半結構化數據集。例如,有個海量IoT設備事件數據集,用JSON格式表示。JSON是一個半結構化數據格式,很適合使用Dataset, 轉成強類型的Dataset[DeviceIoTData]。
- 簡單易用的API
- 結構化數據給spark程序操作數據集帶來很多限制,同時也引入了豐富的語義和易用的特定領域語言,如filter()、map()等。
- 性能和優化
- 因爲Spark DataFrame和DataSet構建於Spark SQL引擎紙上,它會使用Catalys優化器生成優化的邏輯計劃和物理查詢計劃任務,以帶來空間和速度上的提升。
代碼實例
- java版DataSet方式操作數據,測試數據爲|分隔,共6個字段:date、name、condition、city、platform、version,類型string。
public class WordPro {
private static SparkSession gloableSpark;
private static Logger logger = LoggerFactory.getLogger(WordPro.class);
public static void main(String[] args) {
//獲取sparkSession
SparkSession spark = SparkSession.builder().appName("wordPro").master("local[4]").getOrCreate();
gloableSpark = spark;
JavaRDD<Row> rowRDD = spark.read().text("D:\\data\\sparksql\\pro\\keyword.txt").toJavaRDD();
logger.info("完整數據:");
rowRDD.collect().forEach(line -> System.out.println(line));
//將長度不符合規範的數據先過濾掉
JavaRDD<Row> programRdd = rowRDD.filter(new Function<Row, Boolean>() {
private static final long serialVersionUID = 975739302260154160L;
@Override
public Boolean call(Row v1) throws Exception {
boolean result = v1.get(0).toString().split("\\|").length == 6;
return result;
}
}).map(new Function<Row, Row>() {
//將數據根據tab進行拆分重組成爲符合StructType的JavaRdd<Row>
@Override
public Row call(Row v1) throws Exception {
String[] datas = v1.get(0).toString().split("\\|");
Row row = RowFactory.create(
datas[0],
datas[1],
datas[2],
datas[3],
datas[4],
datas[5]
);
return row;
}
});
//根據StructField來創建StructType
List<StructField> structFieldList = new ArrayList<>();
structFieldList.add(DataTypes.createStructField("date", DataTypes.StringType, true));
structFieldList.add(DataTypes.createStructField("name", DataTypes.StringType, true));
structFieldList.add(DataTypes.createStructField("condition", DataTypes.StringType, true));
structFieldList.add(DataTypes.createStructField("city", DataTypes.StringType, true));
structFieldList.add(DataTypes.createStructField("platform", DataTypes.StringType, true));
structFieldList.add(DataTypes.createStructField("version", DataTypes.StringType, true));
StructType structType = DataTypes.createStructType(structFieldList);
//通過structType將rdd轉換爲Dataset
Dataset<Row> infoDataset = spark.createDataFrame(programRdd, structType);
//創建臨時表
infoDataset.createOrReplaceTempView("info");
//構造查詢條件
Map<String, String> params = new HashMap<>(2);
params.put("city", "nanjing");
selectByCondition(params);
}
private static void selectByCondition(Map<String, String> params) {
String city = params.get("city");
String platform = params.get("platform");
//拼接查詢sql
StringBuffer sqlBuffer = new StringBuffer("select * from info where 1=1 ");
sqlBuffer = city == null ? sqlBuffer : sqlBuffer.append(" and city = '" + city + "'");
sqlBuffer = platform == null ? sqlBuffer : sqlBuffer.append(" and platform = '" + platform + "'");
String sql = sqlBuffer.toString();
logger.info("查詢sql:" + sql);
gloableSpark.sql(sql).show();
}
}
DataFrame和DataSet的區別
- DataFrame 一行記錄中沒有指定特定的數據類型
- Dataset 一行記錄中每個對象有明確類型
總結
- DataSet在DataFrame基礎上支持更強的數據類型控制,更精細化操作數據,避免因數據類型異常,造成的程序運行異常。