высокопроизводительная гибернационная вставка - PullRequest
15 голосов
/ 12 июня 2010

Я работаю над чувствительной к задержке частью приложения, в основном я получу сетевое событие, преобразует данные и затем вставлю все данные в БД.После профилирования я вижу, что в основном все мое время тратится на сохранение данных.вот код

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 миллисекунд.

мне нужно, чтобы эта операция выполнялась как можно быстрее, в идеале в базе данных должен быть только один прием.Как я могу это сделать?

Ответы [ 3 ]

14 голосов
/ 12 июня 2010

Переместите генерацию первичного ключа от автоматического увеличения на стороне сервера.Ваш Java-код должен нести ответственность за генерацию PK, чтобы избежать циклических переходов.

Для приличной производительности массовой вставки вам нужен метод, который не должен попадать в базу данных при каждом вызове saveOrUpdate.Использование UUID в качестве первичного ключа или , реализующих HiLo , может помочь в этом.В противном случае массовая вставка фактически не выполняется.

Для обеспечения как производительности, так и взаимодействия с другими внешними системами, pooled или pooled-lo *Оптимизаторы 1012 * - лучший выбор.

3 голосов
/ 12 июня 2010

Честно говоря, я не знаю, на что можно разумно сделать вывод из вашего теста и из "мер", которые вы показываете (я подозреваю, что из-за разминки много накладных расходов, коллекция очень маленькая, а образец очень маленький).

В любом случае, я могу вам сказать, что ваш текущий код не будет масштабироваться, и вы, скорее всего, собираетесь взорвать Сессию при передаче большей коллекции.Вам необходимо регулярно очищать и очищать сеанс (каждые 20 записей, если размер пакета равен 20).

На самом деле, я рекомендую прочитать всю главу 13. Пакетная обработка .

0 голосов
/ 12 июня 2010

Некоторые базовые вещи:

  • У вас есть триггеры или внешний ключ ограничения без индекса?
  • Есть ли у вас пакетные драйверы?
  • Ваши драйверы в пакетном режиме (см. Hibernate.jdbc.batch_size из справочника Паскаля)?
  • Какие-либо индексы в ваших таблицах (если у вас много индексов, иногда это может замедлить вставку)?

Пакетная обработка является частью 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, чтобы увидеть, выполняет ли он запрос на вставку, чтобы получить сгенерированные идентификаторы.

...