Спящий режим: конфигурация пула C3p0 замедляет работу всего сервера.

Я работаю над приложением Spring-MVC, в котором мы используем Hibernate и c3p0 для транзакций базы данных и пула соединений. Большую часть времени он работает очень хорошо, никаких проблем. Но в определенных ситуациях мне приходится копировать множество объектов и файлов в текущей транзакции. Когда это происходит, весь сервер замедляется, и, наконец, я начинаю получать could not rollback exception . Что-то не так с моими настройками c3p0? Спасибо.

пом.xml:

 <!--- Hibernate dependencies -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>4.3.9.Final</version>
        </dependency>

корень-контекст.xml :

<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
    <beans:property name="driverClassName" value="org.postgresql.Driver"/>
    <beans:property name="url"
                    value="jdbc:postgresql://localhost:PORT/DB_NAME"/>
    <beans:property name="username" value="USERNAME"/>
    <beans:property name="password" value="PASSWORD"/>
    <beans:property name="removeAbandoned" value="true"/>
    <beans:property name="removeAbandonedTimeout" value="20"/>
    <beans:property name="defaultAutoCommit" value="false"/>
</beans:bean>

<!-- Hibernate 4 SessionFactory Bean definition -->
<beans:bean id="hibernate4AnnotatedSessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <beans:property name="dataSource" ref="dataSource"/>
    <beans:property name="packagesToScan" value="com.ourapp.spring.model"/>
    <beans:property name="hibernateProperties">
        <beans:props>
            <beans:prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</beans:prop>
            <beans:prop key="hibernate.show_sql">false</beans:prop>
               <!--<beans:prop key="hibernate.jdbc.batch_size">1000</beans:prop>-->
            <beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
            <beans:prop key="cache.use_second_level_cache">true</beans:prop>
            <beans:prop key="cache.use_query_cache">true</beans:prop>
            <beans:prop key="hibernate.order_updates">true</beans:prop>
            <beans:prop key="show_sql">false</beans:prop>
            <beans:prop key="hibernate.c3p0.min_size">1</beans:prop>
            <beans:prop key="hibernate.c3p0.max_size">750</beans:prop>
            <beans:prop key="hibernate.c3p0.acquire_increment">1</beans:prop>
            <beans:prop key="hibernate.c3p0.idle_test_period">1000</beans:prop>
            <beans:prop key="hibernate.c3p0.max_statements">150</beans:prop>
            <beans:prop key="hibernate.c3p0.timeout">1200</beans:prop>
            <beans:prop key="hibernate.connection.release_mode">auto</beans:prop>
        </beans:props>
    </beans:property>

</beans:bean>

Спасибо.


person We are Borg    schedule 10.04.2018    source источник
comment
Суть в том, чтобы знать, что c3p0 на самом деле не может обрабатывать большие объемы данных, что приводит к медленным серверам и ошибкам, подобным описанным вами. Когда у вас такие проблемы, вам нужно либо попробовать оптимизировать свой запрос, либо вам нужно перейти на собственные SQL-запросы.   -  person DamCx    schedule 10.04.2018
comment
@DamCx: есть несколько запросов, так как от БД требуется много информации. Все эти запросы используются на регулярной основе без каких-либо проблем.   -  person We are Borg    schedule 10.04.2018
comment
Я не могу комментировать конфигурацию, но вы пробовали заменить C3PO на что-то вроде Hikari? github.com/brettwooldridge/HikariCP   -  person Rich    schedule 10.04.2018
comment
Как вы определили, что виноват именно C3P0?   -  person Kayaman    schedule 10.04.2018
comment
@WeareBorg они используются отдельно или всегда вместе? Возможно, вы захотите проверить это и, возможно, провести сравнительный анализ загрузки данных, которые могут обрабатывать ваши запросы и сервер.   -  person DamCx    schedule 10.04.2018
comment
@DamCx: Хорошо, есть идеи, как это сделать? Я проверяю Хикари. Это выглядит хорошо. Сначала попробую локально...   -  person We are Borg    schedule 10.04.2018
comment
@Kayaman: Просто отслеживая состояние процесса сверху, на тестовом сервере не обнаружено утечки памяти. И исключения отката, которые есть у многих других пользователей, связаны с c3p0.   -  person We are Borg    schedule 10.04.2018
comment
@WeareBorg попробуйте выполнить ваши запросы к разным объемам данных, контролируя жизненно важные параметры вашего сервера (память, время отклика), пока не обнаружите, что его слишком много для обработки. Есть ли на вашем тестовом сервере такой же объем данных для обработки?   -  person DamCx    schedule 10.04.2018
comment
Использование top не очень подходит для профилирования процесса Java. Хотя вы можете заметить, что реальных утечек памяти нет, вы ничего не знаете о шаблонах использования памяти.   -  person Kayaman    schedule 10.04.2018
comment
@Каяман: Хорошо. Я буду искать некоторые профилировщики Java, которые могут контролировать приложение. мы запускаем 4 приложения на одном сервере. Это может вызвать некоторые проблемы с профилированием. Посмотрим, смогу ли я изолировать это приложение. Видите ли вы что-нибудь проблемное с текущей конфигурацией c3p0.   -  person We are Borg    schedule 10.04.2018
comment
Почему 4 приложения на одном сервере могут вызывать проблемы с профилированием? Нет смысла просто смотреть на конфигурацию c3p0, это примерно так же полезно, как использование top для мониторинга процесса Java.   -  person Kayaman    schedule 10.04.2018
comment
На самом деле вы не используете c3p0... Вы используете Commons DBCP независимо от того, что вы настроили в спящем режиме. Вы вводите предварительно настроенный DataSource в LocalSessionFactoryBean, в основном делая все свойства hibernate.c3p0 и другие параметры, связанные с подключением, в этом отношении бесполезными (как и должно быть, поскольку вы настроили внешний источник данных, как это рекомендуется Hibernate и многими другими ресурсами).   -  person M. Deinum    schedule 10.04.2018
comment
@M.Deinum M.Deinum: теперь я вижу это с bean dataSource. Как я могу переопределить его и использовать вместо него c3p0? Спасибо.   -  person We are Borg    schedule 10.04.2018
comment
Как правило, проблема заключается не в конфигурации пула соединений/DataSource, а в том, как обрабатывается большой объем данных. Как правило, при обработке больших объемов данных вы хотите периодически flush и clear кэшировать первый уровень, иначе это в конечном итоге все замедлит.   -  person M. Deinum    schedule 10.04.2018
comment
@M.Deinum: после того, как каждый новый объект записывается в базу данных, я вызываю session.flush(), но не session.clear();   -  person We are Borg    schedule 10.04.2018
comment
Я бы предложил использовать HikariCP вместо C3P0 и/или Commons DBCP. Кроме того (как упоминалось в моем предыдущем комментарии), ваша проблема, вероятно, связана не с конфигурацией вашего источника данных, а с тем, как вы обрабатываете свои данные.   -  person M. Deinum    schedule 10.04.2018
comment
Не вызывайте flush после каждой записи (так как это замедлит работу вашего приложения). Звоните после разумных пакетов (желательно таких же, как вы указали в настройках пакета гибернации), например, 50 или 100.   -  person M. Deinum    schedule 10.04.2018
comment
См. vladmihalcea.com/ для хороший ресурс о том, как выполнить пакетную обработку и настроить для нее спящий режим.   -  person M. Deinum    schedule 10.04.2018


Ответы (1)


Во-первых, вы не используете C3P0 просто потому, что вы настроили org.apache.commons.dbcp.BasicDataSource как DataSource и внедряете его в свой LocalSessionFactoryBean. Это в основном делает все настройки hibernate.c3p0 бесполезными, поскольку они будут игнорироваться.

Затем у вас возникают проблемы с обработкой больших объемов данных, и я очень сомневаюсь, что проблема связана с вашим DataSource или пулом соединений, а скорее с тем, как вы обрабатываете свои объекты и как вы настроили Hibernate.

Чтобы ускорить пакетную обработку, вы хотите сбросить каждые x записей в базу данных и очистить кеш первого уровня. Почему вы хотите это сделать, вы можете задаться вопросом. Все это связано с тем, как работает Hibernate: когда вы сохраняете объект, который делает Hibernate, он добавляет его в кеш первого уровня (Session или EntityManager в случае JPA). Каждый раз, когда вы добавляете элемент в кеш первого уровня, он будет выполнять грязную проверку ВСЕХ сущностей в кеше первого уровня, чтобы определить, нужно ли что-то сбрасывать. Теперь это будет быстро для первых нескольких объектов, но будет становиться все медленнее и медленнее.

Давайте настроим и закодируем вещи для размера пакета 50.

Сначала вы хотите настроить hibernate на правильный размер пакета, и вы хотите заказать операторы вставки и обновления. Если вы сделаете это, вы сможете извлечь выгоду из того факта, что JDBC теперь может выполнять пакетное обновление (т. е. один оператор вставки или обновления для изменения 50 записей вместо 50 отдельных операторов вставки/обновления).

Спящий режим

<beans:bean id="hibernate4AnnotatedSessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <beans:property name="dataSource" ref="dataSource"/>
    <beans:property name="packagesToScan" value="com.ourapp.spring.model"/>
    <beans:property name="hibernateProperties">
        <beans:props>
            <beans:prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</beans:prop>
            <beans:prop key="hibernate.show_sql">false</beans:prop>
               <!--<beans:prop key="hibernate.jdbc.batch_size">1000</beans:prop>-->
            <beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
            <beans:prop key="hibernate.cache.use_second_level_cache">true</beans:prop>
            <beans:prop key="hibernate.cache.use_query_cache">true</beans:prop>
            <beans:prop key="hibernate.jdbc.batch_size">50</beans:prop>
            <beans:prop key="hibernate.order_inserts">true</beans:prop>
            <beans:prop key="hibernate.order_updates">true</beans:prop>
            <!-- If you use versioned entities set this to true as well -->
            <beans:prop key="hibernate.jdbc.batch_versioned_data">true<beans:prop> 
        </beans:props>
    </beans:property>
</beans:bean>

Модификация кода

public void yourLargeDataSetProcessingMethod() {
    Session session = sessionFactory.getCurrentSession();

    int i = 0;
    for (YourItem item : List<YourItem> items) {
        i++:
        // Here will be processing / creation

        if (i % 50 == 0) {
            session.flush();
            session.clear();
        }
    }
    session.flush();
    session.clear();
} 

Это, вероятно, ускорит вашу обработку и блокировку базы данных.

Последнее замечание: вместо Commons DBCP или C3P0 я бы предложил использовать HikariCP в качестве пула соединений. Он очень маленький, очень быстрый и активно поддерживается (в то время как C3P0 уже некоторое время бездействует).

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

person M. Deinum    schedule 10.04.2018
comment
Во-первых, я работаю над добавлением HikariCP. Как только это будет сделано. Я изменю большой код, чтобы использовать пакетную обработку. Наконец, если это не поможет, вместо спящего режима будут писать собственные SQL-запросы. Я получаю сообщение об ошибке при использовании HikariCP, где Transactionmanager жалуется, что bean-компонент должен иметь тип sessionFactory. Не могли бы вы проверить эту конфигурацию: pastebin.com/xE0HSggC. Спасибо. - person We are Borg; 10.04.2018
comment
Вы должны заменить DataSource НЕ LocalSessionFactoryBean. Только изменение источника данных мало что даст, реальный недостаток заключается в том, как вы обрабатываете данные. - person M. Deinum; 10.04.2018
comment
Хорошо. Будет изменен источник данных. Я задам новый вопрос, когда закончу с изменением кода обработки. - person We are Borg; 10.04.2018
comment
Это сработало хорошо, метод все еще медленный, поэтому я разместил вопрос в CodeReview. Не могли бы вы проверить: заголовок codereview.stackexchange.com/questions/191768/ Спасибо. :-) - person We are Borg; 11.04.2018
comment
Каждый раз, когда вы добавляете элемент в кеш первого уровня, он выполняет грязную проверку ВСЕХ объектов в кеше первого уровня, чтобы определить, нужно ли что-то очищать Зачем нужна грязная проверка? - person antonro; 08.07.2018