Ко мне обратились разработчи с вопросом об эффективности использования executeBatch при использовании JDBC в сравнении с Oracle PL/SQL BULK (forall) insert.
В результате родился небольшой код на java, который производит вызов PL/SQL блока с реализованной BULK вставкой, а так же проделывает такой же объем работы посредством Java JDBC вызовов, но уже с использованием executeBatch().
PL/SQL код BULK операции:
declare
type t_tbl is table of batchtest.n%type;
Java код Batch вызова:
SQL Self - Time, Totals,
Waits, Binds and Row Source Plan
В результате родился небольшой код на java, который производит вызов PL/SQL блока с реализованной BULK вставкой, а так же проделывает такой же объем работы посредством Java JDBC вызовов, но уже с использованием executeBatch().
PL/SQL код BULK операции:
declare
type t_tbl is table of batchtest.n%type;
v_tbl t_tbl := t_tbl();
begin
for i in 1..1000 loop
v_tbl.extend;
v_tbl(i) := i;
end loop;
forall n in 1..v_tbl.count insert into batchtest /*PLSQL*/ values(1, v_tbl(n));
end;
Java код Batch вызова:
PreparedStatement psttm2 = conn.prepareStatement("insert
into batchtest /*JDBC*/ values(2, ?)");
for (int n=1; n<=1000;n++) {
psttm2.setInt(1, n);
psttm2.addBatch();
}
psttm2.executeBatch();
conn.commit();
Результат, полученный из анализа Trace файла:
Rank:2(28.6%) Self:0.946s
Recursive:0.021s Invoker:52 Definer:52 Depth:1
INSERT INTO BATCHTEST VALUES(1, :B1 )
SQL Self - Time, Totals,
Waits, Binds and Row Source Plan
Call
|
Response Time
Accounted-for |
Elapsed
Time |
CPU Time
|
Non-Idle
Wait Time |
Elapsed Time
Unaccounted-for |
Idle
Wait Time |
Parse:
|
0.001
|
0.001
|
0.001
|
0.000
|
0.000
|
0.000
|
Execute:
|
0.945
|
0.945
|
0.381
|
0.565
|
0.000
|
0.000
|
Fetch:
|
0.000
|
0.000
|
0.000
|
0.000
|
0.000
|
0.000
|
Total:
|
0.946
|
0.946
|
0.382
|
0.565
|
-0.001
|
0.000
|
Call
|
Call
Count |
OS
Buffer Gets (disk) |
BG Consistent
Read Mode (query) |
BG Current
Mode (current) |
Rows
Processed or Returned |
Library
Cache Misses |
Times
Waited Non-Idle |
Times
Waited Idle |
Parse:
|
2
|
0
|
0
|
0
|
0
|
1
|
0
|
0
|
Execute:
|
200
|
17
|
799
|
4793
|
200000
|
1
|
62
|
0
|
Fetch:
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Total:
|
202
|
17
|
799
|
4793
|
200000
|
2
|
62
|
0
|
Rank:3(22.5%) Self:0.742s Recursive:0.000s Invoker:52
Definer:52 Depth:0
insert into batchtest /*JDBC*/ values(2, :1 )
SQL Self - Time, Totals,
Waits, Binds and Row Source Plan
Call
|
Response Time
Accounted-for |
Elapsed
Time |
CPU Time
|
Non-Idle
Wait Time |
Elapsed Time
Unaccounted-for |
Idle
Wait Time |
Parse:
|
0.001
|
0.001
|
0.001
|
0.000
|
0.000
|
0.000
|
Execute:
|
0.742
|
0.574
|
0.297
|
1.807
|
-1.530
|
0.168
|
Fetch:
|
0.000
|
0.000
|
0.000
|
0.000
|
0.000
|
0.000
|
Total:
|
0.742
|
0.575
|
0.298
|
1.807
|
-1.531
|
0.168
|
Call
|
Call
Count |
OS
Buffer Gets (disk) |
BG Consistent
Read Mode (query) |
BG Current
Mode (current) |
Rows
Processed or Returned |
Library
Cache Misses |
Times
Waited Non-Idle |
Times
Waited Idle |
Parse:
|
1
|
0
|
0
|
0
|
0
|
1
|
0
|
0
|
Execute:
|
200
|
12
|
733
|
4559
|
200000
|
1
|
423
|
199
|
Fetch:
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Total:
|
201
|
12
|
733
|
4559
|
200000
|
2
|
423
|
199
|
Исходя из результатов анализа trace файла можно заключить, что
1) Oracle PL/SQL Bulk и Java JDBC Batch имеют одинаковую картину вызова
2) Оба запроса создают близкую нагрузку как по характеру так и по объему
3) *** При многократном запуске усредненная производительность обоих запросов расходилась менее, чем на 1%.
P.S.> Поскольку на большинстве тестов JDBC Batch показывал - как минимум - не меньшую производительность, то тесты с запуском Java из базы, дабы исключить влияние задержек SQL*Net на доставку объема batch, не проводились.
Анализ статистики был проведён посредством Oracle Trace Analyzer.
Ниже привожу java код:
Анализ статистики был проведён посредством Oracle Trace Analyzer.
Ниже привожу java код:
oracleBatchTest.java
|
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import oracle.jdbc.pool.OracleDataSource;
public class oracleBatchTest {
public static void main(String argv[]) throws SQLException {
OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=clu-host-02.net.billing.ru)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=dvlp11.net.billing.ru)(INSTANCE_NAME=dvlp112)))");
ods.setUser("ashindarev");
ods.setPassword("********");
Connection conn = ods.getConnection();
Statement sttm = conn.createStatement();
sttm.execute("begin\n"+
" for rec in (\n"+
" select count(1) as cnt from user_tables where table_name = 'BATCHTEST'\n"+
" ) loop\n"+
" if rec.cnt = 0 then\n"+
" execute immediate 'create table batchtest(id integer, n number) storage(initial 1M next 1M pctincrease 0 minextents 4)';\n"+
" end if;\n"+
" end loop;\n"+
" execute immediate 'truncate table batchtest reuse storage';\n"+
"end;");
sttm.execute("ALTER SESSION SET EVENTS '10046 trace name context forever, level 8'");
PreparedStatement psttm1 = conn.prepareStatement(
"declare\n"+
" type t_tbl is table of batchtest.n%type;\n"+
" v_tbl t_tbl := t_tbl();\n"+
"begin\n"+
" for i in 1..1000 loop\n"+
" v_tbl.extend;\n"+
" v_tbl(i) := i;\n"+
" end loop;\n"+
" forall n in 1..v_tbl.count insert into batchtest /*PLSQL*/ values(1, v_tbl(n));\n"+
"end;"
);
PreparedStatement psttm2 = conn.prepareStatement("insert into batchtest /*JDBC*/ values(2, ?)");
for (int i=0; i<100; i++) {
psttm1.execute();
conn.commit();
for (int n=1; n<=1000;n++) {
psttm2.setInt(1, n);
psttm2.addBatch();
}
psttm2.executeBatch();
conn.commit();
psttm2.clearBatch();
}
for (int i=0; i<100; i++) {
for (int n=1; n<=1000;n++) {
psttm2.setInt(1, n);
psttm2.addBatch();
}
psttm2.executeBatch();
psttm2.clearBatch();
conn.commit();
psttm1.execute();
conn.commit();
}
sttm.execute("ALTER SESSION SET EVENTS '10046 trace name context off'");
sttm.execute("drop table BATCHTEST purge");
conn.close();
}
}
Комментариев нет:
Отправить комментарий