From a409f43dabe04e9fcce82ffba54d21648137c1d7 Mon Sep 17 00:00:00 2001 From: Alex K Date: Wed, 24 Feb 2021 16:44:49 -0500 Subject: [PATCH] introducing sql-delta-import --- ...2021-03-01-introducing-sql-delta-import.md | 144 ++++++++++++++++++ .../2021-03-sql-delta-import/missing_rows.png | Bin 0 -> 16957 bytes .../row_density_increase.png | Bin 0 -> 12937 bytes 3 files changed, 144 insertions(+) create mode 100644 _posts/2021-03-01-introducing-sql-delta-import.md create mode 100644 post-images/2021-03-sql-delta-import/missing_rows.png create mode 100644 post-images/2021-03-sql-delta-import/row_density_increase.png diff --git a/_posts/2021-03-01-introducing-sql-delta-import.md b/_posts/2021-03-01-introducing-sql-delta-import.md new file mode 100644 index 0000000..62dee52 --- /dev/null +++ b/_posts/2021-03-01-introducing-sql-delta-import.md @@ -0,0 +1,144 @@ +--- +layout: post +title: "Importing MySQL Data into Delta Lake" +author: alexk +tags: +- databricks +- spark +- deltalake +team: Data Engineering +--- + +OLTP databases are a common data source for Data Lake based warehouses which use Big Data tools to run +batch analytics pipelines. Classic hadoop toolset comes with +[Apache Sqoop](https://sqoop.apache.org/) - a tool for bulk import/export +of data between HDFS and relational data stores. Our pipelines were using this tool as well, primarily +to import MySQL data into HDFS. When Platform Engineering team at Scribd took on a effort +to migrate our on-premise Hadoop workloads to [Databricks Lakehouse Platform](https://databricks.com/product/data-lakehouse) +on AWS we had to write our own tool to import data from MySQL directly into S3 backed [Delta Lake](https://delta.io/). +In this post I will share the details about [sql-delta-import](https://github.com/scribd/sql-delta-import) - an +open-source spark utility to import data from any JDBC compatible database into Delta Lake + +### Sample import + +Importing data into a Delta Lake table is as easy as + +```shell script +spark-submit / +--class "com.scribd.importer.spark.ImportRunner" sql-delta-import_2.12-0.1.0-SNAPSHOT.jar / +--jdbc-url jdbc:mysql://hostName:port/database / +--source source.table +--destination destination.table +--split-by id +``` + +### This looks a lot like `sqoop`... why didn't you just use that? + +We considered using `sqoop` at first but quickly dismissed that option for multiple reasons + +#### 1. Databricks Lakehouse Platform does not come with `sqoop` +Yes we could have ran our sqoop jobs on EMR clusters but we wanted to run everything in Databricks and +avoid additional technology footprint. But even if we drop that restriction... + +#### 2. `sqoop` does not support writing data directly to Delta Lake +`scoop` can only import data as text or parquet. Writing to delta directly allows us to +optimize data storage for best performance on reads by just adding a couple of configuration options + +```shell script +spark-submit / +--conf spark.databricks.delta.optimizeWrite.enabled=true / +--conf spark.databricks.delta.autoCompact.enabled=true / +--class "com.scribd.importer.spark.ImportRunner" sql-delta-import_2.12-0.1.0-SNAPSHOT.jar / +--jdbc-url jdbc:mysql://hostName:port/database / +--source source.table +--destination destination.table +--split-by id +``` + +#### 3. `--num-mappers` just not good enough to control parallelism when working with a database +`sqooop` uses map-reduce under the hood. We can specify `--num-mappers` parameter that controls how many +mappers will be used to import data. Small number of mappers can result in large volume +of data per import and long running transactions. Large number of mappers will result in many connections +to database potentially overloading it especially when there are a lot of `sqoop` jobs running in parallel. +Additionally since there are no reduce stages in `sqoop` jobs large number of mappers will result in large +number of output files and potentially introducing a small files problem. + +`sql delta import` uses `--chunks` parameter to control number of... well... chunks to split the source table +into and standard spark parameters like `--num-executors` and `--executor-cores` to control data import +concurrency thus allowing you to tune those parameters independently + +```shell script +spark-submit --num-executors 15 --executor-cores 4 / +--conf spark.databricks.delta.optimizeWrite.enabled=true / +--conf spark.databricks.delta.autoCompact.enabled=true / +--class "com.scribd.importer.spark.ImportRunner" sql-delta-import_2.12-0.1.0-SNAPSHOT.jar / +--jdbc-url jdbc:mysql://hostName:port/database / +--source source.table +--destination destination.table +--split-by id +--chunks 500 +``` + +in the example above source table will be split into 500 chunks resulting in quick transactions and released connections +but no more than 60 concurrent connections will be used for import since max degree of parallelism is 60 (15 executors x 4 cores). +`delta.optimizeWrite` and `delta.autoCompact` configuration will yield optimal file size output for the destination table + +#### 3.1 `--num-mappers` and data skew just don't play nicely together + +When `sqoop` imports data, source table will be split into ranges based on `--split-by` column and each mapper +would import it's corresponding range. This works good when `--split-by` column has a near uniform distribution +of data, but that's not always the case with source tables... As tables age we tend to add additional columns to them to +take on new business requirements so over time data in latest rows has a higher fill rate than earlier rows. + +![row density increase over time](/post-images/2021-03-sql-delta-import/row_density_increase.png) + +Our source tables here at Scribd definitely have these characteristics. We also have some tables that have entire +ranges of data missing due to data cleanup. At some point large chunks of data were just deleted from these tables. + +![missing rows](/post-images/2021-03-sql-delta-import/missing_rows.png) + +This type of data skew will result in processing time skew and output file size skew when you can only control number of +mappers. Yes we can introduce additional computed synthetic column in the source table as our `split-by` column but now +there is an additional column that does not add business value, app developers need to be aware of it, computing and +storing it takes up database resources and if we plan to use it for imports it's better be indexed, thus even more +compute and storage resources. + +With `sql-delta-import` we can "solve" this problem by making number of chunks much larger than max degree of parallelism. +This way large chunks with high data density are broken up into smaller pieces that a single executor can handle. +Executors that get chunks with little or no data can just quickly process them and move on to do some real work. + + +### Advanced use cases + +For advanced use cases you don't have to use provided spark application directly. `sql-delta-import` +libraries can be imported into your own project. You can specify custom data transformations or JDBC dialect to gain a +more precised control of data type handling + +```scala +... +import com.scribd.importer.spark._ +import com.scribd.importer.spark.transform.DataTransform._ + + implicit val spark: SparkSession = SparkSession.builder().master("local").getOrCreate() + + + // All additional possible jdbc connector properties described here - https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html + val jdbcUrl = "jdbc:mysql://hostName:port/database" + + val config = ImportConfig(source = "table", destination = "target_database.table", splitBy = "id", chunks = 10) + + // Whatever functions are passed to below transform will be applied during import + val transforms = new DataTransform(Seq( + df => df.withColumn("id", col("id").cast(types.StringType)), //custom function to cast id column to string + timeStampsToStrings //included transform function converts all Timestamp columns to their string representation + )) + + val importer = new JDBCImport(jdbcUrl = jdbcUrl, importConfig = config, dataTransform = transforms) + + importer.run() +``` + +--- +Prior to migrating to Databricks Lakehouse Platform we had roughly 300 `sqoop` jobs. We were able to +successfully port all of them to `sql-delta-import`. Today they happily coexist in production with other spark +jobs allowing us to use uniform set of tools for orchestrating, scheduling, monitoring and logging for all of our jobs. diff --git a/post-images/2021-03-sql-delta-import/missing_rows.png b/post-images/2021-03-sql-delta-import/missing_rows.png new file mode 100644 index 0000000000000000000000000000000000000000..17ea648dc0f3d795c562b49f69fd8500f7f13b5d GIT binary patch literal 16957 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