Besoin d'insérer 100000 lignes dans mysql en utilisant hibernate en moins de 5 secondes
Je suis une tentative d'insertion de 100 000 lignes dans une table MYSQL moins de 5 secondes à l'aide d'Hibernate(JPA). J'ai essayé tous les trucs hibernate offre et ne peut toujours pas faire de mieux que de 35 secondes.
1er optimisation : j'ai commencé avec une IDENTITÉ de séquence de générateur qui a entraîné dans les 60 secondes à insérer. Plus tard, j'ai abandonné la séquence générateur et commencé à attribuer les @Id
champ de moi-même par la lecture de la MAX(id)
et à l'aide de AtomicInteger.incrementAndGet()
pour attribuer des champs de moi-même. Que la réduction de l'heure d'insertion à 35 secondes.
2e optimisation : j'ai activé lot insère, en ajoutant
<prop key="hibernate.jdbc.batch_size">30</prop>
<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.jdbc.batch_versioned_data">true</prop>
à la configuration. J'ai été choqué de constater que le lot inserts n'ont absolument rien fait pour diminuer insérer l'heure. Il était encore de 35 secondes!
Maintenant, je pense à essayer d'insérer l'utilisation de plusieurs threads.
Quelqu'un a des pointeurs? Si j'ai choisi MongoDB?
Voici ma configuration:
1. Hibernate
`
<bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.progresssoft.manishkr" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.jdbc.batch_size">30</prop>
<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
<prop key="hibernate.jdbc.batch_versioned_data">true</prop>
</props>
</property>
</bean>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"
id="dataSource">
<property name="driverClassName" value="${database.driver}"></property>
<property name="url" value="${database.url}"></property>
<property name="username" value="${database.username}"></property>
<property name="password" value="${database.password}"></property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoryBean" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
`
- Entité de configuration :
`
@Entity
@Table(name = "myEntity")
public class MyEntity {
@Id
private Integer id;
@Column(name = "deal_id")
private String dealId;
....
....
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "timestamp")
private Date timestamp;
@Column(name = "amount")
private BigDecimal amount;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "source_file")
private MyFile sourceFile;
public Deal(Integer id,String dealId, ....., Timestamp timestamp, BigDecimal amount, SourceFile sourceFile) {
this.id = id;
this.dealId = dealId;
...
...
...
this.amount = amount;
this.sourceFile = sourceFile;
}
public String getDealId() {
return dealId;
}
public void setDealId(String dealId) {
this.dealId = dealId;
}
...
...
....
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
....
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
`
- La persistance de code (service) :
`
@Service
@Transactional
public class ServiceImpl implements MyService{
@Autowired
private MyDao dao;
....
`void foo(){
for(MyObject d : listOfObjects_100000){
dao.persist(d);
}
}
`
4. Classe Dao :
`
@Repository
public class DaoImpl implements MyDao{
@PersistenceContext
private EntityManager em;
public void persist(Deal deal){
em.persist(deal);
}
}
`
Journaux:
`
DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:32.906 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:32.906 [http-nio-8080-exec-2] DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:32.906 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:32.906 [http-nio-8080-exec-2] DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:32.906 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:32.906 [http-nio-8080-exec-2] DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:32.906 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:32.906 [http-nio-8080-exec-2] DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:32.906 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:32.906 [http-nio-8080-exec-2]
...
...
DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:34.002 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:34.002 [http-nio-8080-exec-2] DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:34.002 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:34.002 [http-nio-8080-exec-2] DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:34.002 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:34.002 [http-nio-8080-exec-2] DEBUG o.h.e.j.b.internal.AbstractBatchImpl - Reusing batch statement
18:26:34.002 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - insert into deal (amount, deal_id, timestamp, from_currency, source_file, to_currency, id) values (?, ?, ?, ?, ?, ?, ?)
18:26:34.002 [http-nio-8080-exec-2] DEBUG o.h.e.j.batch.internal.BatchingBatch - Executing batch size: 27
18:26:34.011 [http-nio-8080-exec-2] DEBUG org.hibernate.SQL - update deal_source_file set invalid_rows=?, source_file=?, valid_rows=? where id=?
18:26:34.015 [http-nio-8080-exec-2] DEBUG o.h.e.j.batch.internal.BatchingBatch - Executing batch size: 1
18:26:34.018 [http-nio-8080-exec-2] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - committed JDBC Connection
18:26:34.018 [http-nio-8080-exec-2] DEBUG o.h.e.t.i.jdbc.JdbcTransaction - re-enabling autocommit
18:26:34.032 [http-nio-8080-exec-2] DEBUG o.s.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@2354fb09] after transaction
18:26:34.032 [http-nio-8080-exec-2] DEBUG o.s.o.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
18:26:34.032 [http-nio-8080-exec-2] DEBUG o.h.e.j.internal.JdbcCoordinatorImpl - HHH000420: Closing un-released batch
18:26:34.032 [http-nio-8080-exec-2] DEBUG o.h.e.j.i.LogicalConnectionImpl - Releasing JDBC connection
18:26:34.033 [http-nio-8080-exec-2] DEBUG o.h.e.j.i.LogicalConnectionImpl - Released JDBC connection
'
sahu Mon configuré la taille des lots est de 30 ans, bien que je vois cela dans les journaux
Executing batch size: 27
. Aussi je vois Executing batch size: 27
qu'une seule fois. Veuillez voir les journaux, que j'ai mis à jour. Un autre point est que j'ai lu que même avec le multi threading, le pilote JDBC plaquettes sont dans synchronised()
méthode, donc réel insère arriver un par un?Comment internet pose un problème? C'est un localhost environnement local tomcat & mysql en cours d'exécution!
Assurez-vous que vous savez où est votre goulot d'étranglement. L'insertion des déclarations doit être exécutée uniquement à s'engager à temps, de sorte que la partie de l'35 secondes est utilisé de faire de la base de données-tations et de la pièce qui est juste hibernate-dessus? Comment est le segment de mémoire d'utilisation? serait
flush()
ing de l'em dans un certain intervalle de temps de l'aide?1. N'utilisez pas de
DriverManagerDataSource
utiliser un pool de connexions. 2. N'utilisez pas de Printemps pour le tx de gestion et de déconner avec le hibernate.current_session_context_class
que les sauts de bonne intégration. 3. votre boucle for est une sorte de défectueux, vous devriez rincer et claire le gestionnaire de l'entité après chaque x enregistrements (de préférence le même que la taille des lots).
OriginalL'auteur Kumar Manish | 2017-05-29
Vous devez vous connecter pour publier un commentaire.
Après avoir essayé toutes les solutions possibles, j'ai enfin trouvé une solution pour insérer 100 000 lignes de moins de 5 secondes!
Choses que j'ai essayé:
1) Remplacé hibernate/base de données de l'auto-incrémentation/GÉNÉRÉE par l'auto-générée à l'aide de AtomicInteger
2) Permettant batch_inserts avec batch_size=50
3) Rinçage cache après chaque "batch_size' nombre de persist() appelle
4) le multithreading (n'a pas de tentative de celui-ci)
Enfin ce qui a fonctionné a l'aide d'un native multi-requête d'insertion et de l'insertion de 1000 lignes dans une requête sql insert au lieu d'utiliser persist() sur chaque entité. Pour l'insertion de 100 000 entités, j'ai créer une requête native comme ce
"INSERT into MyTable VALUES (x,x,x),(x,x,x).......(x,x,x)"
[1000 insertions de lignes dans une requête sql insert]Maintenant il faut environ 3 secondes pour l'insertion de 100 000 dossiers! Si le goulot d'étranglement a été l'orm lui-même! Pour des insertions, la seule chose qui semble fonctionner est originaire des requêtes de type insert!
rewriteBatchedStatements=true
pour la connexion. Pas besoin de réécrire l'application!Je ne vois pas combinés dans mes journaux après l'ajout de
<property name="reWriteBatchedInserts" value="true">
à monpersistence.xml
Cet article n'est également pas clair d'où prendre PGSimpleDatasource: vladmihalcea.com/...avec votre requête native approche de ce qui va se passer si l'un ou plusieurs des inserts défaillant en raison d'une violation de contrainte d'intégrité?
OriginalL'auteur Kumar Manish
Vous êtes à l'aide de Printemps pour la gestion de la transaction, mais le briser en utilisant
thread
comme l'actuel contexte de session. Lors de l'utilisation de Printemps pour gérer vos transactions ne sont pas déconner avec lahibernate.current_session_context_class
de la propriété. Le supprimer.De ne pas utiliser le
DriverManagerDataSource
utiliser un pool de connexion comme HikariCP.Dans votre boucle, vous devriez
flush
etclear
laEntityManager
à intervalles réguliers, de préférence le même que la taille des lots. Si vous n'avez pas une seule persistent prend de plus en plus longtemps, parce que quand vous ne que Hibernate contrôles de premier niveau de cache pour les objets sales, plus d'objets, plus le temps qu'il faut. Avec 10 ou 100 c'est acceptable, mais la vérification de 10000s d'objets pour chaque persistent va prendre son péage.-
Pour une plus ample explication voir ce blog et ce blog.
Il semble que ce soit aussi bon qu'il obtient? Je vais essayer de multithreading comme un dernier recours.
Le Multithreading aidera certainement. Un de mes collegue qui, se fendant d'un processus ETL en 10 sous-processus. Le gain a été autour de 7x.
Le Multithreading de l'aide, il peut également faire fonctionner les choses. Cela dépend de quel type de traitement doit être fait, ce genre de transactionality est utilisé, etc. Ne sous-estimez jamais le pouvoir d'un seul thread.
OriginalL'auteur M. Deinum
Une autre option à considérer est Statelesssession n':
La discussion à ce sujet:
À l'aide de statelesssession n'traitement par Lots
EntityManager
et pasSession
Est-il possible avecEntityManager
s? Peut-être que je peux essayer de désactiver le 1er niveau de cache deEntityManager
?OriginalL'auteur Justas
Uff.
Vous pouvez faire beaucoup de choses pour augmenter la vitesse.
1.) Utiliser @DynamicInsert et @DynamicUpdate pour empêcher la DB de l'insertion des non-vide de colonnes et de la mise à jour a changé colonnes.
2.) Essayez d'insérer les colonnes directement (sans l'utilisation d'hibernate) dans votre base de données pour voir si hibernate est vraiment le goulot d'étranglement.
3.) Utiliser une sessionfactory et seulement valider votre transaction à chaque par exemple 100 insère. Ou seulement d'ouvrir et de fermer la transaction une fois et rincer vos données à chaque 100 insère.
4.) Utiliser la stratégie de génération d'IDENTIFIANT "séquence" et laisser hibernate préallouer (via le paramètre allocationsize) Id.
5.) L'utilisation des caches.
Certaines de ces solutions possibles peut avoir son calendrier des inconvénients lorsqu'il n'est pas utilisé correctement. Mais vous avez beaucoup de possibilités.
Oui M. Deinum, peut mais ne doit pas! Si vous rencontrez des colonnes avec des Gouttes de ces annotations vous donner beaucoup plus de performances, car ils ne seront pas mis à jour lorsque inchangé! Comme je l'ai écrit: "certaines options peuvent avoir différentiel inconvénients".
OriginalL'auteur M46