Import data using Spark

Updated at:
Copy as MD

Spark Doris Connector lets you load large volumes of data into ApsaraDB for SelectDB using Spark's distributed compute cluster. The connector reads data from upstream sources—MySQL, PostgreSQL, Hadoop Distributed File System (HDFS), Amazon Simple Storage Service (Amazon S3), and others—into a DataFrame, then writes it to SelectDB using Stream Load. You can also read data from SelectDB tables using Spark Java Database Connectivity (JDBC).

How it works

Spark Doris Connector bridges your external data sources and ApsaraDB for SelectDB. Spark preprocesses the data in a distributed compute cluster, then the connector writes it to SelectDB via Stream Load. This replaces single-node JDBC-based writes with a parallelized pipeline that scales with your Spark cluster.

image

Prerequisites

Before you begin, make sure you have:

Install Spark Doris Connector

Choose one of the following installation methods.

Option 1: Add the Maven dependency

Add the following dependency to your pom.xml. For other versions, see Maven Repository.

<dependency>
    <groupId>org.apache.doris</groupId>
    <artifactId>spark-doris-connector-3.2_2.12</artifactId>
    <version>1.3.2</version>
</dependency>

Option 2: Download a JAR package

Download the JAR package that matches your Spark and Scala version. For other versions, see Maven Repository.

The following JAR packages are compiled with Java 8. Contact technical support if you need a different Java version. The version string encodes, from left to right: Spark version, Scala version, and Spark Doris Connector version.
Version JAR package
2.4-2.12-1.3.2 spark-doris-connector-2.4_2.12-1.3.2
3.1-2.12-1.3.2 spark-doris-connector-3.1_2.12-1.3.2
3.2-2.12-1.3.2 spark-doris-connector-3.2_2.12-1.3.2

After downloading, deploy the JAR based on your Spark cluster mode:

  • Local cluster mode: Place the JAR in the jars directory of your Spark installation.

  • YARN cluster mode: Upload the JAR as a pre-deployment package.

    1. Upload the JAR to HDFS: ``bash hdfs dfs -mkdir /spark-jars/ hdfs dfs -put /<your_local_path>/spark-doris-connector-3.2_2.12-1.3.2.jar /spark-jars/ ``

    2. Add the dependency to your cluster configuration: `` spark.yarn.jars=hdfs:///spark-jars/spark-doris-connector-3.2_2.12-1.3.2.jar ``

Import data

Spark Doris Connector supports two write APIs: Spark SQL and DataFrame. Both use the doris data source format and share the same configuration parameters.

Use Spark SQL

Start the Spark SQL shell and submit a task using a temporary view:

bin/spark-sql
val selectdbHttpPort = "selectdb-cn-****.selectdbfe.rds.aliyuncs.com:8080"
val selectdbJdbc = "jdbc:mysql://selectdb-cn-****.selectdbfe.rds.aliyuncs.com:9030"
val selectdbUser = "admin"
val selectdbPwd = "****"
val selectdbTable = "test_db.test_order"

CREATE TEMPORARY VIEW test_order
USING doris
OPTIONS(
 "table.identifier"="${selectdbTable}",
 "fenodes"="${selectdbHttpPort}",
 "user"="${selectdbUser}",
 "password"="${selectdbPwd}",
 "sink.properties.format"="json"
);

INSERT INTO test_order SELECT order_id, order_amount, order_status FROM tmp_tb;

Use DataFrame

Start the Spark shell and submit a write task:

bin/spark-shell
val spark = SparkSession.builder().master("local[1]").getOrCreate()
val df = spark.createDataFrame(Seq(
  ("1", 100, "Pending Payment"),
  ("2", 200, null),
  ("3", 300, "Received")
)).toDF("order_id", "order_amount", "order_status")

df.write
  .format("doris")
  .option("fenodes", selectdbHttpPort)
  .option("table.identifier", selectdbTable)
  .option("user", selectdbUser)
  .option("password", selectdbPwd)
  .option("sink.batch.size", 100000)
  .option("sink.max-retries", 3)
  .option("sink.properties.file.column_separator", "\t")
  .option("sink.properties.file.line_delimiter", "\n")
  .save()

Parameters

All parameters apply to both Spark SQL and DataFrame unless noted otherwise.

Connection parameters

Parameter Default Required Description
fenodes None Yes HTTP endpoint of the SelectDB instance. Format: <host>:<http-port>. To find your endpoint, go to the Instance Details page in the ApsaraDB for SelectDB console. Under Basic Information > Network Information, copy the VPC Endpoint or Public Endpoint and the HTTP Port. Example: selectdb-cn-****.selectdbfe.rds.aliyuncs.com:8080.
table.identifier None Yes Target table in the format <database>.<table>. Example: test_db.test_table.
user None Yes Username for connecting to the SelectDB instance.
password None Yes Password for connecting to the SelectDB instance.

Read parameters

Parameter Default Required Description
request.retries 3 No Maximum number of retries for requests to the SelectDB instance.
request.connect.timeout.ms 30000 No Connection timeout for requests to the SelectDB instance, in milliseconds.
request.read.timeout.ms 30000 No Read timeout for requests to the SelectDB instance, in milliseconds.
request.query.timeout.s 3600 No Query timeout for the SelectDB instance, in seconds. The default value is 1 hour. Set to -1 for no timeout.
request.tablet.size Integer.MAX_VALUE No Number of SelectDB tablets mapped to each Resilient Distributed Dataset (RDD) partition. A smaller value creates more partitions, increasing Spark-side parallelism at the cost of additional SelectDB pressure.
read.field None No Columns to read from the SelectDB table. Separate multiple columns with commas.
batch.size 1024 No Maximum rows read from a backend node per request. Larger values reduce the number of connections and network latency overhead.
exec.mem.limit 2147483648 No Memory limit for a single query, in bytes. Default is 2 GB.
deserialize.arrow.async false No Whether to deserialize Arrow data to RowBatch asynchronously.
deserialize.queue.size 64 No Internal queue size for asynchronous Arrow deserialization. Takes effect only when deserialize.arrow.async is true.
filter.query.in.max.count 100 No Maximum number of values in an IN clause for predicate pushdown. If the number of values exceeds this threshold, Spark handles the filter locally.
ignore-type None No Field types to ignore when reading the schema for a temporary view. Example: bitmap,hll.

Write parameters

Parameter Default Required Description
write.fields None No Fields to write to the SelectDB table, or the order in which fields are written. Separate multiple fields with commas. Defaults to all fields in the table's column order.
sink.batch.size 100000 No Maximum rows written to the backend per batch.
sink.max-retries 0 No Maximum retries after a write failure.
sink.properties.format csv No Data format for Stream Load. Valid values: csv, json, arrow.
sink.properties.* -- No Stream Load parameters, passed directly to the underlying Stream Load job. For example, set sink.properties.column_separator to specify the column delimiter. For all available parameters, see Import data by using Stream Load.
sink.task.partition.size None No Number of partitions for writing to SelectDB. Use this to reduce write frequency when RDD filtering produces many small partitions. Use together with sink.task.use.repartition.
sink.task.use.repartition false No Whether to repartition data before writing. false uses coalesce (no shuffle). true uses repartition, which produces more evenly distributed partitions but adds shuffle overhead.
sink.batch.interval.ms 50 No Interval between write batches, in milliseconds.
sink.enable-2pc false No Whether to use two-phase commit (2PC). When enabled, transactions commit only after the entire Spark job completes. If any task fails, all pre-committed transactions are rolled back.
sink.auto-redirect true No Whether to redirect Stream Load requests through the frontend node. When enabled, you do not need to specify backend node addresses explicitly.
sink.streaming.passthrough false No (DataFrame only) Writes the first column values without transformation.

Production recommendations

Consider the following settings for production workloads:

  • Exactly-once delivery: Enable sink.enable-2pc=true to ensure that all data commits only after the Spark job succeeds. Any task failure rolls back all pre-committed transactions, preventing partial writes.

  • Partition control: If RDD filtering produces many small partitions, set sink.task.partition.size together with sink.task.use.repartition=true to consolidate partitions and reduce write frequency.

  • Retry on failure: Set sink.max-retries to a positive integer (for example, 3) to automatically retry transient write failures.

End-to-end example

This example imports data from a MySQL database into ApsaraDB for SelectDB using both Spark SQL and DataFrame.

Sample environment:

Software Java Spark Scala SelectDB
Version 1.8 3.1.2 2.12 3.0.4

Set up the environment

1. Configure Spark.

Download and extract the Spark installation package:

wget https://archive.apache.org/dist/spark/spark-3.1.2/spark-3.1.2-bin-hadoop3.2.tgz
tar xvzf spark-3.1.2-bin-hadoop3.2.tgz

Place spark-doris-connector-3.2_2.12-1.3.2.jar in the SPARK_HOME/jars directory.

2. Create the source table in MySQL.

CREATE TABLE `employees` (
  `emp_no` int NOT NULL,
  `birth_date` date NOT NULL,
  `first_name` varchar(14) NOT NULL,
  `last_name` varchar(16) NOT NULL,
  `gender` enum('M','F') NOT NULL,
  `hire_date` date NOT NULL,
  PRIMARY KEY (`emp_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

Use Data Management (DMS) to generate test data.

3. Create the target table in SelectDB.

Connect to your SelectDB instance over the MySQL protocol, then run:

CREATE DATABASE test_db;

USE test_db;
CREATE TABLE employees (
    emp_no       int NOT NULL,
    birth_date   date,
    first_name   varchar(20),
    last_name    varchar(20),
    gender       char(2),
    hire_date    date
)
UNIQUE KEY(`emp_no`)
DISTRIBUTED BY HASH(`emp_no`) BUCKETS 32;

4. Apply for a public endpoint.

Apply for a public endpoint for your SelectDB instance. See Apply for or release a public endpoint.

5. Add your Spark host IP to the whitelist.

Add the public IP address of your Spark environment to the SelectDB IP address whitelist. See Configure an IP address whitelist.

Import using Spark SQL

  1. Start the Spark SQL shell:

    bin/spark-sql
  2. Submit the import task:

    CREATE TEMPORARY VIEW mysql_tbl
    USING jdbc
    OPTIONS(
     "url"="jdbc:mysql://host:port/test_db",
     "dbtable"="employees",
     "driver"="com.mysql.jdbc.Driver",
     "user"="admin",
     "password"="****"
    );
    
    CREATE TEMPORARY VIEW selectdb_tbl
    USING doris
    OPTIONS(
     "table.identifier"="test_db.employees",
     "fenodes"="selectdb-cn-****-public.selectdbfe.rds.aliyuncs.com:8080",
     "user"="admin",
     "password"="****",
     "sink.properties.format"="json"
    );
    
    INSERT INTO selectdb_tbl SELECT emp_no, birth_date, first_name, last_name, gender, hire_date FROM mysql_tbl;
  3. After the job completes, log on to the ApsaraDB for SelectDB console to verify the imported data.

Import using DataFrame

  1. Start the Spark shell:

    bin/spark-shell
  2. Submit the import task:

    val mysqlDF = spark.read.format("jdbc")
        .option("url", "jdbc:mysql://host:port/test_db")
        .option("dbtable", "employees")
        .option("driver", "com.mysql.jdbc.Driver")
        .option("user", "admin")
        .option("password", "****")
        .load()
    
    mysqlDF.write.format("doris")
        .option("fenodes", "host:httpPort")
        .option("table.identifier", "test_db.employees")
        .option("user", "admin")
        .option("password", "****")
        .option("sink.batch.size", 100000)
        .option("sink.max-retries", 3)
        .option("sink.properties.format", "json")
        .save()
  3. After the job completes, log on to the ApsaraDB for SelectDB console to verify the imported data.

What's next