You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@seatunnel.apache.org by "怼烎@" <72...@qq.com> on 2026/09/10 08:55:25 UTC
Subject: [Question] JDBC Connector + Oracle ojdbc8: batch_size not working for large column tables due to 2MB internal threshold
Hi all,
I've been investigating an issue where SeaTunnel's JDBC batch_size
configuration does not take effect when syncing Oracle tables with many
large VARCHAR2 columns. I've traced the root cause to the Oracle JDBC
driver itself and would like to ask the community for help.
==========
Environment
==========
- SeaTunnel: 2.3.12
- Connector-JDBC: 2.3.12 (bundled)
- Oracle JDBC driver: ojdbc8-21.9.0.0 (Thin driver)
- Deploy mode: local (-m local)
- parallelism: 1 (both source and sink)
==========
Issue
==========
With batch_size = 2000 configured, the behavior differs drastically
between two test cases:
Test 1: Small table (4 columns, ~500 bytes/row)
-> Oracle V$SQL shows: EXECUTIONS=10, ROWS=20000, AVG_PER_EXEC=2000
-> batch_size = 2000 works PERFECTLY
Test 2: Large table (210 columns with VARCHAR2(1000)~(4000), ~25KB/row)
-> Oracle V$SQL shows: EXECUTIONS=63, ROWS=5000, AVG_PER_EXEC=79.4
-> batch_size = 2000 is completely IGNORED
==========
SeaTunnel Config (sanitized)
==========
env {
parallelism = 1
job.mode = "BATCH"
}
source {
Jdbc {
url = "jdbc:oracle:thin:@//<SRC_HOST>:1521/<SRC_SERVICE>"
driver = "oracle.jdbc.OracleDriver"
username = "<SRC_USER>"
password = "<SRC_PASSWORD>"
query = "SELECT * FROM <SCHEMA>.<SOURCE_TABLE>"
fetch_size = 5000
}
}
sink {
Jdbc {
url = "jdbc:oracle:thin:@//<DST_HOST>:1521/<DST_SERVICE>"
driver = "oracle.jdbc.OracleDriver"
username = "<DST_USER>"
password = "<DST_PASSWORD>"
generate_sink_sql = false
query = "INSERT INTO <SCHEMA>.<TARGET_TABLE> (col1, ..., col210) VALUES (?, ..., ?)"
batch_size = 2000
auto_commit = true
batch_interval_ms = 0
}
}
==========
What I've verified so far
==========
(1) SeaTunnel layer: NOT the problem
- parallelism=1 confirmed from DEBUG logs
- Source reads only 1 split, 1 chunk
- Connection pool maximumPoolSize=1, only 1 JDBC connection total
- Only 1 writer thread (st-multi-table-sink-writer-1)
- SeaTunnel DOES call executeBatch() after accumulating 2000 rows
- No multi-threading, no multi-connection, no parallel writes
(2) Root cause: ojdbc8 Thin driver hardcoded 2MB threshold
I decompiled ojdbc8-21.9.0.0.jar and found:
// OraclePreparedStatement (internal)
private static final int BATCH_SIZE_THRESHOLD = 2097152; // 2MB
The full picture is:
SeaTunnel adds 2000 rows via addBatch(), then calls executeBatch()
|
v
ojdbc8 accumulates bind parameter data in an internal buffer
|
v
2000 rows x ~25KB/row = ~50MB total data
|
v
50MB >> 2MB threshold -> ojdbc8 auto-splits into ~25 sub-batches
|
v
executeBatch() sends each sub-batch as a separate network call
|
v
Oracle V$SQL sees 25 separate INSERT executions instead of 1
For the small table: 2000 rows x ~500B = ~1MB < 2MB -> no split -> works
(3) Other driver findings:
- ojdbc6: no 2MB threshold, but ojdbc6's Oracle-style batching
(defaultExecuteBatch > 1) is mutually exclusive with standard JDBC
addBatch() API, which is what SeaTunnel uses internally
- ojdbc8: defaultExecuteBatch connection property is a NO-OP
(hardcoded to return 1, cannot be changed)
- I also patched ojdbc8 by changing 2MB to Integer.MAX_VALUE,
which improved AVG_PER_EXEC from ~80 to ~1500 (but still not 2000)
==========
Questions for the community
==========
1. Is there any way in SeaTunnel's JDBC Connector to work around or
mitigate this ojdbc8 2MB threshold issue?
2. Has the community considered special handling for Oracle large-column
tables in Connector-JDBC?
3. Is there a specific Oracle JDBC driver version recommended that
doesn't have this 2MB hardcoded limitation?
4. Does Oracle JDBC have any hidden connection property to adjust
this internal threshold? I couldn't find any official documentation
about this.
5. Any other suggestions for improving batch insert performance with
Oracle large-column tables?
==========
Verification SQL (Oracle side)
==========
SELECT
SUBSTR(SQL_TEXT, 1, 80) AS SQL_TEXT,
EXECUTIONS,
ROWS_PROCESSED,
ROUND(ROWS_PROCESSED / EXECUTIONS, 1) AS AVG_PER_EXEC
FROM V$SQL
WHERE SQL_TEXT LIKE '%INSERT%'
AND SQL_TEXT LIKE '%<TARGET_TABLE>%'
ORDER BY LAST_ACTIVE_TIME DESC;
Looking forward to your feedback. Thanks!
Best regards,
hongyingjie
怼烎@
727662995@qq.com