Подтвердить что ты не робот

Hibernate, JDBC и Java для среднего и большого набора результатов

Вопрос

Мы пытаемся оптимизировать наше приложение dataserver. Он хранит запасы и котировки по базе данных mysql. И нас не устраивает выборка.

Контекст

- database
    - table stock : around 500 lines
    - table quote : 3 000 000 to 10 000 000 lines
    - one-to-many association : one stock owns n quotes
    - fetching around 1000 quotes per request
    - there is an index on (stockId,date) in the quote table
    - no cache, because in production, querys are always different
- Hibernate 3
- mysql 5.5
- Java 6
- JDBC mysql Connector 5.1.13
- c3p0 pooling

Тесты и результаты

Протокол

  • Время выполнения на сервере mysql получается при запуске сгенерированных запросов sql в буфере командной строки mysql.
  • Сервер находится в тестовом контексте: никаких других показаний БД, нет записей DB
  • Мы получаем 857 котировок для акций AAPL.

Случай 1: Спящий режим с ассоциацией

Это заполняет наш фондовый объект 857 кавычками (все правильно отображено в hibernate.xml)

session.enableFilter("after").setParameter("after", 1322910573000L);
Stock stock = (Stock) session.createCriteria(Stock.class).
add(Restrictions.eq("stockId", stockId)).
setFetchMode("quotes", FetchMode.JOIN).uniqueResult();

Сгенерировано SQL:

SELECT this_.stockId AS stockId1_1_,
       this_.symbol AS symbol1_1_,
       this_.name AS name1_1_,
       quotes2_.stockId AS stockId1_3_,
       quotes2_.quoteId AS quoteId3_,
       quotes2_.quoteId AS quoteId0_0_,
       quotes2_.value AS value0_0_,
       quotes2_.stockId AS stockId0_0_,
       quotes2_.volume AS volume0_0_,
       quotes2_.quality AS quality0_0_,
       quotes2_.date AS date0_0_,
       quotes2_.createdDate AS createdD7_0_0_,
       quotes2_.fetcher AS fetcher0_0_
FROM stock this_
LEFT OUTER JOIN quote quotes2_ ON this_.stockId=quotes2_.stockId
AND quotes2_.date > 1322910573000
WHERE this_.stockId='AAPL'
ORDER BY quotes2_.date ASC

Результаты:

  • Время выполнения на сервере mysql: ~ 10 мс
  • Время выполнения в Java: ~ 400 мс

Случай 2: Hibernate без ассоциации без HQL

Размышляя о повышении производительности, мы использовали этот код, который извлекает только объекты кавычек, и мы вручную добавляем их в запас (поэтому мы не получаем повторную информацию о запасе для каждой строки). Мы использовали createSQLQuery для минимизации последствий псевдонимов и беспорядка HQL.

String filter = " AND q.date>1322910573000";
filter += " ORDER BY q.date DESC";
Stock stock = new Stock(stockId);
stock.addQuotes((ArrayList<Quote>) session.createSQLQuery("select * from quote q where stockId='" + stockId + "' " + filter).addEntity(Quote.class).list());

Сгенерировано SQL:

SELECT *
FROM quote q
WHERE stockId='AAPL'
  AND q.date>1322910573000
ORDER BY q.date ASC

Результаты:

  • Время выполнения на сервере mysql: ~ 10 мс
  • Время выполнения в Java: ~ 370ms

Случай 3: JDBC без спящего режима

String filter = " AND q.date>1322910573000";
filter += " ORDER BY q.date DESC";
Stock stock = new Stock(stockId);
Connection conn = SimpleJDBC.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from quote q where stockId='" + stockId + "' " + filter);
while(rs.next())
{
    stock.addQuote(new Quote(rs.getInt("volume"), rs.getLong("date"), rs.getFloat("value"), rs.getByte("fetcher")));
}
stmt.close();
conn.close();

Результаты:

  • Время выполнения на сервере mysql: ~ 10 мс
  • Время выполнения в Java: ~ 100 мс

Наши договоренности

  • Драйвер JDBC является общим для всех случаев
  • Существует фундаментальная временная стоимость вождения JDBC
  • С подобными запросами sql Hibernate тратит больше времени, чем чистый код JDBC при преобразовании наборов результатов в объекты
  • Hibernate createCriteria, createSQLQuery или createQuery похожи по времени
  • В производстве, где мы много писем одновременно, чистое решение JDBC показалось медленнее, чем спящий режим (возможно, потому, что наши решения JDBC не были объединены).
  • Mysql мудрый, сервер, похоже, очень хорошо себя ведет, и стоимость времени очень приемлема.

Наши вопросы

  • Есть ли способ оптимизировать производительность драйвера JDBC?
  • И поможет ли Hibernate оптимизация?
  • Есть ли способ оптимизировать производительность Hibernate при преобразовании наборов результатов?
  • Мы сталкиваемся с чем-то не настраиваемым из-за управления базовыми объектами и памятью Java?
  • Нам не хватает точки, мы глупы, и все это напрасно?
  • Мы французы? Да.

Ваша помощь очень приветствуется.

4b9b3361

Ответ 1

Можете ли вы сделать smoke test с помощью простого запроса, например:

SELECT current_timestamp()

или

SELECT 1 + 1

Это скажет вам, каковы фактические накладные расходы JDBC. Также неясно, выполняются ли оба теста с одного и того же компьютера.

Есть ли способ оптимизировать производительность драйвера JDBC?

Запустите тот же запрос несколько тысяч раз в Java. JVM требуется некоторое время для разминки (класс загрузки, JIT). Кроме того, я предполагаю, что SimpleJDBC.getConnection() использует пул соединений C3P0 - стоимость установления соединения довольно высока, поэтому вначале небольшое выполнение может быть медленным.

Также предпочитает именованные запросы для запроса ad-hoc или запроса критериев.

И поможет ли Hibernate эту оптимизацию?

Hibernate - очень сложная структура. Как вы видите, он потребляет 75% общего времени выполнения по сравнению с сырым JDBC. Если вам нужна необработанная ORM (без ленивой загрузки, грязной проверки, расширенного кэширования), рассмотрите mybatis. Или даже JdbcTemplate с RowMapper абстракция.

Есть ли способ оптимизировать производительность Hibernate при преобразовании наборов результатов?

Не совсем. Ознакомьтесь с Глава 19. Повышение производительности в документации Hibernate. Существует много отражений, происходящих там + поколения классов. Еще раз, Hibernate может оказаться не лучшим решением, если вы хотите выжать каждую миллисекунду из своей базы данных.

Однако это хороший выбор, если вы хотите увеличить общий пользовательский интерфейс из-за обширной поддержки кеширования. Еще раз просмотрите performance doc. В основном речь идет о кешировании. Существует кеш первого уровня, кеш второго уровня, кеш запросов... Это место, где Hibernate может фактически опередить простой JDBC - он может много кэшировать, как вы даже не могли себе представить. С другой стороны - плохая конфигурация кэша приведет к еще более медленной настройке.

Отъезд: Кэширование с Hibernate + Spring - некоторые вопросы!

Мы сталкиваемся с чем-то не настраиваемым из-за управления базовыми объектами и памятью Java?

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

Нам не хватает точки, мы глупы, и все это напрасно?

Я считаю, что JDBC - хороший выбор, если производительность - это ваша самая большая проблема. Java успешно используется во многих приложениях с большой нагрузкой.