Import data using Spark
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.
Prerequisites
Before you begin, make sure you have:
-
Spark Doris Connector 1.3.1 or later installed
-
An ApsaraDB for SelectDB instance. See Create an instance
-
A SelectDB instance connection. See Connect to an ApsaraDB for SelectDB instance
-
The public IP address of your Spark environment added to the SelectDB IP address whitelist. See Configure an IP address whitelist
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
jarsdirectory of your Spark installation. -
YARN cluster mode: Upload the JAR as a pre-deployment package.
-
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/`` -
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-sqlval 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-shellval 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=trueto 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.sizetogether withsink.task.use.repartition=trueto consolidate partitions and reduce write frequency. -
Retry on failure: Set
sink.max-retriesto 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
-
Start the Spark SQL shell:
bin/spark-sql -
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; -
After the job completes, log on to the ApsaraDB for SelectDB console to verify the imported data.
Import using DataFrame
-
Start the Spark shell:
bin/spark-shell -
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() -
After the job completes, log on to the ApsaraDB for SelectDB console to verify the imported data.