Высокопроизводительная вставка для спящего режима
Я работаю над чувствительной к задержкам частью приложения, в основном я получаю сетевое событие, преобразующее данные, а затем вставляю все данные в БД. После профилирования я вижу, что в основном все мое время тратится на сохранение данных. вот код
private void insertAllData(Collection<Data> dataItems)
{
long start_time = System.currentTimeMillis();
long save_time = 0;
long commit_time = 0;
Transaction tx = null;
try
{
Session s = HibernateSessionFactory.getSession();
s.setCacheMode(CacheMode.IGNORE);
s.setFlushMode(FlushMode.NEVER);
tx = s.beginTransaction();
for(Data data : dataItems)
{
s.saveOrUpdate(data);
}
save_time = System.currentTimeMillis();
tx.commit();
s.flush();
s.clear();
}
catch(HibernateException ex)
{
if(tx != null)
tx.rollback();
}
commit_time = System.currentTimeMillis();
System.out.println("Save: " + (save_time - start_time));
System.out.println("Commit: " + (commit_time - save_time));
System.out.println();
}
Размер коллекции всегда меньше 20. Вот данные синхронизации, которые я вижу:
Save: 27
Commit: 9
Save: 27
Commit: 9
Save: 26
Commit: 9
Save: 36
Commit: 9
Save: 44
Commit: 0
Это меня смущает. Я полагаю, что save
должен быть быстрым и все время тратится на commit
. но ясно, что я ошибаюсь. Я также попытался удалить транзакцию (ее действительно не нужно), но я видел худшие времена... Я установил hibernate.jdbc.batch_size = 20...
Я могу ожидать получить до 500 сообщений в секунду, поэтому мне нужно, чтобы одна обработка сообщений составляла менее 20 миллисекунд.
Мне нужно, чтобы эта операция была как можно быстрее, в идеале, в базу данных будет только одна обратная связь. Как я могу это сделать?
Ответы
Ответ 1
Перенесите выделение первичного ключа из автоматического увеличения. Ваш код Java должен нести ответственность за генерацию ПК, чтобы избежать круглых поездок.
Для обеспечения достаточной производительности вставки вставки вам нужен метод, который не нужно ударять по базе данных при каждом вызове saveOrUpdate. Использование UUID в качестве первичного ключа или реализация HiLo может помочь в достижении этого. В противном случае там вообще не происходит объемной вставки.
Чтобы иметь как производительность, так и взаимодействие с другими внешними системами, оптимальным выбором являются объединенные или pooled-lo.
Ответ 2
Честно говоря, я не знаю, что может быть разумно сделано из вашего теста и из "мер", которые вы показываете (я подозреваю, что из-за разминки много накладных расходов, коллекция очень мала, а образец очень мал).
В любом случае, я могу сказать, что ваш текущий код не будет масштабироваться, и вы, скорее всего, взорвете сессию при передаче большой коллекции. Вам необходимо очистить сессию с регулярными интервалами (каждые 20 записей, если размер партии равен 20).
На самом деле, я рекомендую прочитать целую Глава 13. Пакетная обработка.
Ответ 3
Некоторые основные вещи:
- У вас есть триггеры или внешний ключ
ограничения без индекса?
- У вас есть пакетные драйверы?
- Являются ли ваши драйверы в пакетном режиме (см. hibernate.jdbc.batch_size из ссылки Pascal)?
- Любые индексы на ваших таблицах (если у вас много индексов, иногда они могут замедлять вставку)?
Batching является частью JDBC 2.0, он позволяет выполнять несколько операторов в пакете; идея заключается в сокращении задержки в оба конца (вы можете выполнять несколько партий на транзакцию).
Statement stmt = dbCon.createStatement("insert into DataTable values (?,?,?)");
stmt.setInt(1, x1); stmt.setInt(2, x2), stmt.setString(3, "some value");
stmt.addBatch();
...
stmt.setInt(1, x2); stmt.setInt(2, x3), stmt.setString(3, "some other value");
stmt.addBatch();
stmt.executeBatch();
dbCon.commit();
Вероятно, вы можете использовать это как тестовый тест. Я также посмотрел бы на SQL, который генерирует hibernate, чтобы проверить, выполняет ли он запрос на каждую вставку, чтобы получить сгенерированные идентификаторы.