Domanda

Ho un'applicazione web che riceve messaggi tramite un'interfaccia HTTP, ad esempio:

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

Questa richiesta contiene l'ID del mittente, l'ID del destinatario e il testo del messaggio.

Questo messaggio dovrebbe essere elaborato come:

  • trovare l'oggetto Utente corrispondente sia per l'origine che per la destinazione dal database
  • creando un albero di oggetti:un Messaggio che contiene un campo per il testo del messaggio e due oggetti Utente per l'origine e la destinazione
  • persistente questo albero in un database.

L'albero verrà caricato da altre applicazioni che non posso toccare.

Utilizzo Oracle come database di supporto e JPA con Toplink per le attività di gestione del database.Se possibile, resterei con questi.

Senza molta ottimizzazione posso raggiungere un throughput di circa 30 richieste/sec nel mio ambiente.Non è molto, avrei bisogno di circa 300 richieste/sec.Quindi ho misurato dove si trova il collo di bottiglia delle prestazioni e ho scoperto che le chiamate a em.persist() impiega la maggior parte del tempo.Se commento semplicemente quella riga, il throughput supera ben le 1000 richieste/sec.

Ho provato a scrivere una piccola applicazione di prova che utilizzasse semplici chiamate JDBC per persistere 1 milione di messaggi nello stesso database.Ho utilizzato il batching, ovvero ho eseguito 100 inserimenti, quindi un commit e ho ripetuto finché tutti i record non erano nel database.Ho misurato un throughput di circa 500 richieste/sec in questo scenario, che soddisferebbe le mie esigenze.

È chiaro che qui devo ottimizzare le prestazioni dell'inserto.Tuttavia, come ho detto prima, vorrei continuare a utilizzare JPA e Toplink per questo, non JDBC puro.

Conosci un modo per creare inserti batch con JPA e Toplink?Potete consigliare qualche altra tecnica per migliorare le prestazioni di persistenza di JPA?

INFORMAZIONI ADDIZIONALI:

"richieste/sec" significa qui:numero totale di richieste/tempo totale dall'inizio del test all'ultimo record scritto nel database.

Ho provato a effettuare le chiamate a em.persist() asincrono creando una coda in memoria tra il materiale servlet e il persistente.Ha aiutato molto la performance.Tuttavia, la coda è cresciuta molto rapidamente e poiché l'applicazione riceverà circa 200 richieste al secondo ininterrottamente, non è una soluzione accettabile per me.

In questo approccio disaccoppiato ho raccolto richieste per 100 msec e ho chiamato em.persist() su tutti gli oggetti raccolti prima di effettuare la transazione.L'EntityManagerFactory viene memorizzato nella cache tra ogni transazione.

È stato utile?

Soluzione

Dovresti disaccoppiarti dall'interfaccia JPA e utilizzare la semplice API TopLink.Probabilmente puoi inserire gli oggetti che stai persistendo in una UnitOfWork e impegnare la UnitOfWork secondo la tua pianificazione (sincronizzata o asincrona).Si noti che uno dei costi di em.persist() è il clone implicito che avviene dell'intero oggetto grafico.TopLink funzionerà piuttosto meglio se uow.registerObject() i tuoi due utenti si oppongono personalmente, risparmiandosi i test di identità che altrimenti dovrebbe fare.Quindi ti ritroverai con:

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();

Questo è un TopLink molto vecchia scuola tra l'altro ;)

Tieni presente che il batch sarà di grande aiuto, perché verrà avviata la scrittura in batch e, più in particolare, la scrittura in batch con l'associazione dei parametri, che per questo semplice esempio avrà probabilmente un impatto molto grande sulle tue prestazioni.

Altre cose da cercare:la dimensione del sequenziamento.Gran parte del tempo impiegato a scrivere oggetti in TopLink viene in realtà impiegato leggendo le informazioni sulla sequenza dal database, soprattutto con valori predefiniti piccoli (probabilmente ne avrei diverse centinaia o anche di più come dimensione della sequenza).

Altri suggerimenti

Qual è la tua misura di "richieste/sec"?In altre parole, cosa succede per la 31a richiesta?Quale risorsa viene bloccata?Se si tratta della parte front-end/servlet/web, puoi eseguire em.persist() in un altro thread e tornare immediatamente?

Inoltre, stai creando transazioni ogni volta?Stai creando oggetti EntityManagerFactory con ogni richiesta?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top