Я использую драйвер JDBC db2, он же JT400 для подключения на сервер db2 в Application System/400, компьютерной системе среднего уровня.
Моя цель — insert into
три таблицы вне мейнфрейма IBM, которые будут облачными экземплярами (например, Amazon WS).
Чтобы производительность была лучше
1) Я использую уже установленные соединения для подключения к db2. (используя org.apache.commons.dbcp.BasicDataSource
или com.ibm.as400.access.AS400JDBCManagedConnectionPoolDataSource, оба работают нормально.)
public class AS400JDBCManagedConnectionPoolDataSource extends AS400JDBCManagedDataSource implements ConnectionPoolDataSource, Referenceable, Serializable {
}
public class AS400JDBCManagedDataSource extends ToolboxWrapper implements DataSource, Referenceable, Serializable, Cloneable {
}
2) Я хочу кэшировать операторы insert into
для всех трех таблиц, чтобы мне не приходилось каждый раз отправлять запрос и каждый раз компилировать, что дорого. Вместо этого я бы просто передал только параметры. (Очевидно, я делаю это, используя JDBC prepared statements
)
На основе официального документа IBM Оптимизация доступа к DB2 для i5/OS из Java и WebSphere, стр. 17-20 — Включение расширенной динамической поддержки, оператор можно кэшировать с помощью AS400JDBCManagedConnectionPoolDataSource
.
НО, проблема в том, что insert into
запросов компилируются каждый раз, что каждый раз занимает 200ms * 3 queries = 600ms
.
Пример, который я использую,
public class CustomerOrderEventHandler extends MultiEventHandler {
private static Logger logger = LogManager.getLogger(CustomerOrderEventHandler.class);
//private BasicDataSource establishedConnections = new BasicDataSource();
//private DB2SimpleDataSource nativeEstablishedConnections = new DB2SimpleDataSource();
private AS400JDBCManagedConnectionPoolDataSource dynamicEstablishedConnections =
new AS400JDBCManagedConnectionPoolDataSource();
private State3 orderState3;
private State2 orderState2;
private State1 orderState1;
public CustomerOrderEventHandler() throws SQLException {
dynamicEstablishedConnections.setServerName(State.server);
dynamicEstablishedConnections.setDatabaseName(State.DATABASE);
dynamicEstablishedConnections.setUser(State.user);
dynamicEstablishedConnections.setPassword(State.password);
dynamicEstablishedConnections.setSavePasswordWhenSerialized(true);
dynamicEstablishedConnections.setPrompt(false);
dynamicEstablishedConnections.setMinPoolSize(3);
dynamicEstablishedConnections.setInitialPoolSize(5);
dynamicEstablishedConnections.setMaxPoolSize(50);
dynamicEstablishedConnections.setExtendedDynamic(true);
Connection connection = dynamicEstablishedConnections.getConnection();
connection.close();
}
public void onEvent(CustomerOrder orderEvent){
long start = System.currentTimeMillis();
Connection dbConnection = null;
try {
dbConnection = dynamicEstablishedConnections.getConnection();
long connectionSetupTime = System.currentTimeMillis() - start;
state3 = new State3(dbConnection);
state2 = new State2(dbConnection);
state1 = new State1(dbConnection);
long initialisation = System.currentTimeMillis() - start - connectionSetupTime;
int[] state3Result = state3.apply(orderEvent);
int[] state2Result = state2.apply(orderEvent);
long state1Result = state1.apply(orderEvent);
dbConnection.commit();
logger.info("eventId="+ getEventId(orderEvent) +
",connectionSetupTime=" + connectionSetupTime +
",queryPreCompilation=" + initialisation +
",insertionOnlyTimeTaken=" +
(System.currentTimeMillis() - (start + connectionSetupTime + initialisation)) +
",insertionTotalTimeTaken=" + (System.currentTimeMillis() - start));
} catch (SQLException e) {
logger.error("Error updating the order states.", e);
if(dbConnection != null) {
try {
dbConnection.rollback();
} catch (SQLException e1) {
logger.error("Error rolling back the state.", e1);
}
}
throw new CustomerOrderEventHandlerRuntimeException("Error updating the customer order states.", e);
}
}
private Long getEventId(CustomerOrder order) {
return Long.valueOf(order.getMessageHeader().getCorrelationId());
}
}
И состояния с командами вставки выглядят так, как показано ниже,
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class State2 extends State {
private static Logger logger = LogManager.getLogger(DetailState.class);
Connection connection;
PreparedStatement preparedStatement;
String detailsCompiledQuery = "INSERT INTO " + DATABASE + "." + getStateName() +
"(" + DetailState.EVENT_ID + ", " +
State2.ORDER_NUMBER + ", " +
State2.SKU_ID + ", " +
State2.SKU_ORDERED_QTY + ") VALUES(?, ?, ?, ?)";
public State2(Connection connection) throws SQLException {
this.connection = connection;
this.preparedStatement = this.connection.prepareStatement(detailsCompiledQuery); // this is taking ~200ms each time
this.preparedStatement.setPoolable(true); //might not be required, not sure
}
public int[] apply(CustomerOrder event) throws StateException {
event.getMessageBody().getDetails().forEach(detail -> {
try {
preparedStatement.setLong(1, getEventId(event));
preparedStatement.setString(2, getOrderNo(event));
preparedStatement.setInt(3, detail.getSkuId());
preparedStatement.setInt(4, detail.getQty());
preparedStatement.addBatch();
} catch (SQLException e) {
logger.error(e);
throw new StateException("Error setting up data", e);
}
});
long startedTime = System.currentTimeMillis();
int[] inserted = new int[0];
try {
inserted = preparedStatement.executeBatch();
} catch (SQLException e) {
throw new StateException("Error updating allocations data", e);
}
logger.info("eventId="+ getEventId(event) +
",state=details,insertionTimeTaken=" + (System.currentTimeMillis() - startedTime));
return inserted;
}
@Override
protected String getStateName() {
return properties.getProperty("state.order.details.name");
}
}
Таким образом, поток каждый раз, когда принимается событие (например, CustomerOrder
), получает установленное соединение, а затем запрашивает состояния инициализировать свои statement
s.
Показатели времени выглядят так, как показано ниже.
для первого события требуется 580ms
для создания preparedStatement
s для 3 таблиц.
{"timeMillis":1489982655836,"thread":"ScalaTest-run-running-CustomerOrderEventHandlerSpecs","level":"INFO","loggerName":"com.xyz.customerorder.events.handler.CustomerOrderEventHandler",
"message":"eventId=1489982654314,connectionSetupTime=1,queryPreCompilation=580,insertionOnlyTimeTaken=938,insertionTotalTimeTaken=1519","endOfBatch":false,"loggerFqcn":"org.apache.logging.log4j.spi.AbstractLogger","threadId":1,"threadPriority":5}
для второго события требуется 470ms
для подготовки отчетов для 3 таблиц, что меньше, чем для первого события, но всего < 100ms
, я предполагаю, что это значительно меньше, так как оно даже не должно доходить до компиляции.
{"timeMillis":1489982667243,"thread":"ScalaTest-run-running-PurchaseOrderEventHandlerSpecs","level":"INFO","loggerName":"com.xyz.customerorder.events.handler.CustomerOrderEventHandler",
"message":"eventId=1489982665456,connectionSetupTime=0,queryPreCompilation=417,insertionOnlyTimeTaken=1363,insertionTotalTimeTaken=1780","endOfBatch":false,"loggerFqcn":"org.apache.logging.log4j.spi.AbstractLogger","threadId":1,"threadPriority":5}
Я думаю, что поскольку я закрываю preparedStatement
для этого конкретного соединения, оно даже не существует для нового соединения. Если это так, то какой смысл вообще иметь кэширование операторов в многопоточной среде.
В документации есть аналогичный пример, где транзакции выполняются внутри одного и того же connection
, что не относится ко мне, так как мне нужно иметь несколько подключений одновременно.
Вопросы
Основной
Q1) Драйвер DB2 JDBC вообще кэширует операторы между несколькими соединениями? Потому что я не вижу большой разницы при подготовке заявления. (см. пример, первый принимает ~600ms
, второй принимает ~500ms
)
использованная литература
ODP = открытый путь данных
Пакеты SQL — это постоянные объекты, используемые для хранения информации, связанной с подготовленными операторами SQL. Их может использовать драйвер IBM iSeries Access для IBM Toolbox for Java JDBC. Они также используются приложениями, использующими интерфейс API QSQPRCED (SQL Process Extended Dynamic).
В случае JDBC наличие пакета SQL проверяется, когда клиентское приложение выполняет первую подготовку оператора SQL. Если пакет не существует, он создается в это время (даже если он еще не содержит операторов SQL).
Конфигурация пула соединений Tomcat jdbc — DB2 на iSeries(AS400) а>
Драйвер IBM Data Server для кэширования операторов JDBC и SQLJ
~100ms
при подготовке отчетов. Каждый раз требуется не менее~150ms * 3 = 450ms
для компиляции трех запросов. - person prayagupd   schedule 20.03.2017ConnectionPoolDataSource
не является пулом соединений (это источник данных для пула соединений). Хотя иногда реализации делают это неправильно, так чтоAS400JDBCManagedConnectionPoolDataSource
все же может быть одним из них. - person Mark Rotteveel   schedule 20.03.2017ConnectionPoolDataSource
не является пулом соединений? Он обеспечивает физическое соединение в пуле. Я используюAS400JDBCManagedConnectionPoolDataSource
, который реализуетConnectionPoolDataSource
иDataSource
и представляет собой рекомендуется документацией, так как предполагается поддержка кэширования операторов. Я вижу, что он работает как пул соединений. - person prayagupd   schedule 20.03.2017