Данные по JPA слишком медленные - PullRequest
0 голосов
/ 09 апреля 2020

Я недавно переключил свое приложение на Spring Boot 2. Я использую Spring Data JPA для обработки всех транзакций, и я заметил огромную разницу в скорости между этой и моей старой конфигурацией. Хранение около 1000 элементов делалось за 6 секунд, а теперь это занимает более 25 секунд. Я видел ТАК сообщения о пакетировании с данными JPA, но ни один из них не работал.

Позвольте мне показать вам 2 конфигурации:

Сущность (общая для обоих):

    @Entity
    @Table(name = "category")
    public class CategoryDB implements Serializable
    {
        private static final long serialVersionUID = -7047292240228252349L;

        @Id
        @Column(name = "category_id", length = 24)
        private String category_id;

        @Column(name = "category_name", length = 50)
        private String name;

        @Column(name = "category_plural_name", length = 50)
        private String pluralName;

        @Column(name = "url_icon", length = 200)
        private String url;

        @Column(name = "parent_category", length = 24)
        @JoinColumn(name = "parent_category", referencedColumnName = "category_id")
        private String parentID;

        //Getters & Setters

     }

Старый репозиторий (с указанием только вставка):

@Override
    public Set<String> insert(Set<CategoryDB> element)
    {
        Set<String> ids = new HashSet<>();
        Transaction tx = session.beginTransaction();
        for (CategoryDB category : element)
        {
            String id = (String) session.save(category);
            ids.add(id);
        }
        tx.commit();
        return ids;
    }

Старый Hibernate XML Файл конфигурации:

    <property name="show_sql">true</property>
    <property name="format_sql">true</property>

    <!-- connection information -->
    <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

    <!-- database pooling information -->
    <property name="connection_provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
    <property name="hibernate.c3p0.min_size">5</property>
    <property name="hibernate.c3p0.max_size">100</property>
    <property name="hibernate.c3p0.timeout">300</property>
    <property name="hibernate.c3p0.max_statements">50</property>
    <property name="hibernate.c3p0.idle_test_period">3000</property>

Старая статистика:

18949156 nanoseconds spent acquiring 2 JDBC connections;
5025322 nanoseconds spent releasing 2 JDBC connections;
33116643 nanoseconds spent preparing 942 JDBC statements;
3185229893 nanoseconds spent executing 942 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
3374152568 nanoseconds spent executing 1 flushes (flushing a total of 941 entities and 0 collections);
6485 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)

Новый репозиторий:

@Repository
public interface CategoryRepository extends JpaRepository<CategoryDB,String>
{
    @Query("SELECT cat.parentID FROM CategoryDB cat WHERE cat.category_id = :#{#category.category_id}")
    String getParentID(@Param("category") CategoryDB category);
}

И я использую saveAll() в своем сервисе.

Новое application.properties:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.hikari.connection-timeout=6000
spring.datasource.hikari.maximum-pool-size=10

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.generate_statistics = true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true

Новая статистика:

24543605 nanoseconds spent acquiring 1 JDBC connections;
0 nanoseconds spent releasing 0 JDBC connections;
136919170 nanoseconds spent preparing 942 JDBC statements;
5457451561 nanoseconds spent executing 941 JDBC statements;
19985781508 nanoseconds spent executing 19 JDBC batches;
0 nanoseconds spent performing 0 L2C puts;
0 nanoseconds spent performing 0 L2C hits;
0 nanoseconds spent performing 0 L2C misses;
20256178886 nanoseconds spent executing 3 flushes (flushing a total of 2823 entities and 0 collections);
0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)

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

1 Ответ

0 голосов
/ 09 апреля 2020

Давайте объединим статистику, чтобы ее можно было легко сравнить. Старые строки имеют префикс o, новые - n. Строки с числом 0 игнорируются. Измерения в наносекундах отформатированы так, чтобы миллисекунды могли быть перед .

o:    18 949156 nanoseconds spent acquiring 2 JDBC connections;
n:    24 543605 nanoseconds spent acquiring 1 JDBC connections;

o:    33 116643 nanoseconds spent preparing 942 JDBC statements;
n:   136 919170 nanoseconds spent preparing 942 JDBC statements;

o:  3185 229893 nanoseconds spent executing 942 JDBC statements;
n:  5457 451561 nanoseconds spent executing 941 JDBC statements; //loosing ~2sec

o:            0 nanoseconds spent executing 0 JDBC batches;
n: 19985 781508 nanoseconds spent executing 19 JDBC batches; // loosing ~20sec

o:  3374 152568 nanoseconds spent executing 1 flushes (flushing a total of 941 entities and 0 collections);
n: 20256 178886 nanoseconds spent executing 3 flushes (flushing a total of 2823 entities and 0 collections); // loosing ~20sec, processing 3 times the entities

o:         6485 nanoseconds spent executing 1 partial-flushes (flushing a total of 0 entities and 0 collections)
n:            0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)

Следующие точки кажутся соответствующими:

  • В новой версии 19 партии, которые берут 20se c, которых вообще не существует в старой версии.

  • В новой версии вместо 3 используются 3 сброса, которые собирают вместе 20 se c больше или примерно в 6 раз длиннее. Это, вероятно, более или менее то же самое дополнительное время, что и партии, так как они, безусловно, являются частью этих сбросов.

Хотя партии должны ускорять работу, есть отчеты, в которых они делают что-то медленнее, особенно с MySql: Почему Spring jdbcTemplate.batchUpdate () такой медленный?

Это приводит нас к нескольким вещам, которые вы можете попробовать / исследовать:

  • Отключите пакетирование, чтобы проверить, действительно ли вы страдаете от какой-то проблемы с медленным пакетированием.
  • Используйте связанный пост SO для ускорения пакетирования.
  • зарегистрируйте SQL операторы, которые фактически выполняются, чтобы найти разницу. Поскольку это приведет к довольно длинным журналам для манипуляции, попробуйте извлечь только операторы SQL в двух файлах и сравнить их с помощью инструмента сравнения.
  • сбрасывает журналы, чтобы понять, почему возникают дополнительные сбросы.
  • используйте точки останова и отладчик или дополнительное ведение журнала, чтобы узнать, какие сущности сбрасываются и почему у вас больше сущностей во втором варианте.

Все предложения выше работают на JPA , Но ваша статистика и содержание вопросов показывают, что вы делаете простые вставки в одну или несколько таблиц. Выполнение этого с JDB C, например с JdbcTemplate, может быть более эффективным и, по крайней мере, более простым для понимания.

...