Question

J'ai une application Web qui reçoit des messages via une interface HTTP, par exemple:

http://server/application?source=123&destination=234&text=hello

Cette demande contient l'ID de l'expéditeur, l'ID du destinataire et le texte du message.

Ce message doit être traité comme suit:

  • recherche l'objet utilisateur correspondant pour la source et la destination à partir de la base de données
  • création d'une arborescence d'objets: un message contenant un champ pour le texte du message et deux objets utilisateur pour la source et la destination
  • conserver cette arborescence dans une base de données.

L'arborescence sera chargée par d'autres applications que je ne peux pas toucher.

J'utilise Oracle comme base de données de sauvegarde et JPA avec Toplink pour les tâches de traitement de base de données. Si possible, je resterais avec ceux-ci.

Sans beaucoup d'optimisation, je peux atteindre un débit d'environ 30 requêtes / s dans mon environnement. Ce n'est pas beaucoup, j'aurais besoin d'environ 300 demandes / sec. J'ai donc mesuré le goulot d'étranglement des performances et constaté que les appels à em.persist () prenaient la plupart du temps. Si je commente simplement cette ligne, le débit dépasse largement 1 000 demandes / s.

J'ai essayé d'écrire une petite application de test utilisant de simples appels JDBC pour conserver 1 million de messages dans la même base de données. J'ai utilisé le traitement par lots, ce qui signifie que j'ai fait 100 insertions puis un commit, et répété jusqu'à ce que tous les enregistrements soient dans la base de données. Dans ce scénario, j’ai mesuré un débit d’environ 500 requêtes / s, ce qui répondrait à mes besoins.

Il est clair que je dois optimiser les performances des insert ici. Cependant, comme je l’ai mentionné précédemment, je voudrais continuer à utiliser JPA et Toplink à cet effet, et non JDBC pur.

Connaissez-vous un moyen de créer des insertions par lots avec JPA et Toplink? Pouvez-vous recommander une autre technique pour améliorer les performances persistantes de JPA?

INFORMATIONS SUPPLÉMENTAIRES:

" demandes / s " signifie ici: nombre total de demandes / durée totale du début du test au dernier enregistrement écrit dans la base de données.

J'ai essayé de rendre les appels à em.persist () asynchrones en créant une file d'attente en mémoire entre le contenu du servlet et le persister. Cela a grandement aidé la performance. Cependant, la file d’attente s’est développée très rapidement et, comme l’application recevra environ 200 demandes par seconde en continu, ce n’est pas une solution acceptable pour moi.

Dans cette approche découplée, j'ai collecté des requêtes de 100 ms et appelé em.persist () sur tous les éléments collectés avant de valider la transaction. EntityManagerFactory est mis en cache entre chaque transaction.

Était-ce utile?

La solution

Vous devez découpler l'interface JPA et utiliser l'API TopLink nue. Vous pouvez probablement transférer les objets que vous persistez dans un UnitOfWork et valider celui-ci selon votre planning (synchrone ou asynchrone). Notez que l'un des coûts de em.persist () est le clone implicite du graphe d'objet entier. TopLink fonctionnera plutôt mieux si vous uow.registerObject () vos deux objets utilisateur vous-même, en enregistrant lui-même les tests d’identité qu’il doit effectuer autrement. Donc, vous allez vous retrouver avec:

uow=sess.acquireUnitOfWork();
for (job in batch) {
 thingyCl=uow.registerObject(new Thingy());
 user1Cl=uow.registerObject(user1);
 user2Cl=uow.registerObject(user2);
 thingyCl.setUsers(user1Cl,user2Cl);
}
uow.commit();

C’est une très vieille école TopLink d'ailleurs;)

Notez que le lot aidera beaucoup, car l'écriture de lots, et plus particulièrement l'écriture de lots avec liaison de paramètres, commencera, ce qui, pour cet exemple simple, aura probablement un impact très important sur vos performances.

Autres choses à rechercher: la taille de votre séquence. La majeure partie du temps consacré à l'écriture d'objets dans TopLink est en fait consacrée à la lecture des informations de séquençage dans la base de données, en particulier avec les petites valeurs par défaut (j'aurais probablement plusieurs centaines, voire plus, de la taille de ma séquence).

Autres conseils

Quelle est votre mesure des "demandes / s"? En d'autres termes, que se passe-t-il pour la 31e demande? Quelle ressource est bloquée? S'il s'agit de la partie front-end / servlet / web, pouvez-vous exécuter em.persist () dans un autre thread et revenir immédiatement?

De plus, créez-vous des transactions à chaque fois? Créez-vous des objets EntityManagerFactory avec chaque demande?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top