- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-26-2018 12:36 PM
Its really good how you explained the problem. I ran into similar issue with too many parquet files & too much time to write or stages hanging in the middle when i have to create dynamic columns (more than 1000) and write atleast 10M rows to S3.
One mistake i was making was i was doing all the operations in RDD instead of dataframe something like sqlContext().toJavaRDD or .rdd(). You lose lot of the good things once you move to rdd from dataframe. The below approach kind of worked for me, try it out and let me know
You can split your spark job into two parts:
1. Run a reduce() job to determine the dynamic column names that is required for storing the data
2. Run a flatMap() to transform the data that you received and convert them in to the schema that was created using the dynamic columns in step 1. Once that is done you can directly write them out as delta. Since delta is transactional based and does lot of book keeping you will not end up with too many parquet files.
Here is a sample of the step that i explained above:
- // Part 1 --> Determine the dynamic column names
- Row schemaOutput = testDataSet.reduce((ReduceFunction<Row>)(v1, v2)->{
- List<String> set1, set2;
- String data = v1.size()> OUTPUT_COLUMN ? v1.getString(OUTPUT_COLUMN):"";
- if(data.startsWith("{"))
- set1 =ScoringTransformer.getColumns(data);
- elseset1 =newLinkedList<>((Collection<String>)(Collection<?>)JavaConverters.asJavaCollectionConverter(v1.toSeq()).asJavaCollection());
- data = v2.size()> OUTPUT_COLUMN ? v2.getString(OUTPUT_COLUMN):"";
- if(data.startsWith("{"))
- set2 =ScoringTransformer.getColumns(data);
- elseset2 =newLinkedList<>((Collection<String>)(Collection<?>)JavaConverters.asJavaCollectionConverter(v2.toSeq()).asJavaCollection());
- Set<String> columns =newLinkedHashSet<>(set1);
- columns.addAll(set2);
- returnRowFactory.create(columns.toArray());
- });
- List<String> columns =newLinkedList<>((Collection<String>)(Collection<?>)
- JavaConverters.asJavaCollectionConverter(schemaOutput.toSeq()).asJavaCollection());
- // You get a schema out for all the dynamic column names that is required for the next step
- ExpressionEncoder<Row> encoder =RowEncoder.apply(ReportUtils.getSchemaBasedOnString(columns));
- // Part 2 --> Run Flatmap and encode it with the schema that you created out of dynamic columns, then repartition in whatever way you want and write it out to s3
- testDataSet.flatMap((FlatMapFunction<Row,Row>) row ->{
- List<Map<String,String>> data =ScoringTransformer.getTransformedData(row.getString(OUTPUT_COLUMN));
- if(data !=null&&!data.isEmpty())
- return data.stream()
- .map(x ->RowFactory.create(columns.parallelStream()
- .map(y -> x.getOrDefault(y,""))
- .collect(Collectors.toList()).toArray()))
- .collect(Collectors.toList())
- .iterator();
- returnnewLinkedList<Row>().iterator();
- }, encoder)
- .repartition(col("date_group"))
- .write()
- .option("mergeSchema","true")
- .format(FORMAT)
- .mode("append")
- .save(s3Output +"/xyz");