Hai bisogno di aiuto migliorare le prestazioni di grandi quantità di dati in Grails
-
03-10-2019 - |
Domanda
Questa soluzione funziona, ma la prestazione è inferiore al previsto. Una query di ritorno 200K righe richiede diversi minuti e pioli la CPU sulla mia macchina dev. L'esecuzione della stessa query * Analizzatore query restituisce tutti i risultati in <1 minuto.
Class MyController {
def index = {...}
...
def csv = {
...
def rs = DomainClass.createCritera().scroll {}
while(rs.next()){
response.getOutputStream().print(rs.getString(1)\n)
}
...
}
DB = server SQL Server 2005 in un box dedicato separato dalla mia macchina dev.
Ho anche notato tramite SQL Server Profiler che Gorm / hibernate utilizza sp_cursorprepexec e sp_cursorfetch di leggere il risultato di 128 righe alla volta. Mi piacerebbe provare a non utilizzare un cursore se si tratta di un'opzione.
Non so se è il problema, ma può solo aiutare. In ibernazione è possibile impostare la pergamena come solo in avanti, ma sto avendo problemi a trovare un ambiente simile per graal.
problema .
Soluzione: Bypass sospensione. Da 10 minuti a 15 secondi.
Class MyController {
def DataSource
def index = {...}
...
def csv = {
...
def out = response.getOutoutStream()
Sql sql = new Sql(dataSource)
sql.eachRow("select c1, c2 from t1",{
out.println( it.c1 + "," + it.c2 )
})
...
}
* stesso = tagliare e incollare da SQL Server Profiler, ma escluso lo sProc involucro sp_cursorprepexec.
Soluzione
Hibernate non è davvero fatta per lotti di carico, ma ci sono alcune cose che potete provare (la maggior parte dei quali richiedono di escludere l'utilizzo ScrollableResult e solo fare le query regolari con i risultati degli oggetti).
- Basta bypass Hibernate / GORM e andare a SQL direttamente per la (si spera) manciata di luoghi in cui ne avete bisogno. Ya, lo so, ma se va di male in peggio ...
- Chiamata session.setReadOnly () o query.setReadOnly () per disabilitare di Hibernate istantanee di stato
-
Dare di Hibernate Stateless Session una prova. Se tutto quello che stai facendo è la lettura, questo può funzionare bene. Lo Stateless Session ha un overhead inferiore molto più rispetto alla sessione ordinaria Hibernate, ma darò tutto il tuo caching e oggetto di monitoraggio dello stato. Dovrete fare qualcosa di simile a usarlo:
def Session statelessSession = sessionFactory.openStatelessSession() statelessSession.beginTransaction() // ... statelessSession.getTransaction().commit() statelessSession.close()
-
Lavare la sessione in lotti di 25 o 50. In sostanza, come si sta iterazione sugli elementi che avete portato indietro, fare un session.flush (). Se non lo fai, la sessione continuerà a crescere fino a quando si esaurisce la memoria e la tua garbage collector inizia impazzire. Questo potrebbe essere il motivo per cui il processore è sempre ancorato.
In bocca al lupo!
Altri suggerimenti
E 'semplice da scendere fino a Hibernate direttamente se qualcosa non è supportato da GORM:
import org.hibernate.ScrollMode
class MyController {
def index = {...}
def csv = {
DomainClass.withSession { session ->
def rs = session.createCriteria(DomainClass).scroll(ScrollMode.FORWARD_ONLY)
while (rs.next()) {
response.outputStream.print rs.getString(1)
}
}
}
}
Si potrebbe fare lo stesso per una query HQL utilizzando session.createQuery(...)
invece.
Un altro modo di utilizzare criteri Grails e ScrollMode:
Criteria criteria = Domain.createCriteria().buildCriteria{
eq('id', id)
}
ScrollableResults results = criteria.scroll(ScrollMode.FORWARD_ONLY)
int i = 0
while (results.next()){
...
if (++i % 50 == 0){
Domain.withSession { Session session ->
session.flush()
session.clear()
}
}
}
Un paio di cose da notare:
- Non dovrebbe eseguire query nel controller in quanto potrebbe aprire una nuova sessione di db e la transazione per ogni chiamata.
- Se si desidera che solo un paio di colonne è possibile utilizzare le proiezioni i suoi criteri.
- Potrebbe essere utile per registrare le sqls che Hibernate sta sparando. Fino a questa data, Hibernate carichi pigri annullabile uno di ogni oggetto di una relazione, anche se non lo fanno usarlo . Non si dovrebbe avere a preoccupare se si sta utilizzando proiezioni, però.
Usa inserto in batch ed è più veloce di metodo di Gorm pulizia e stateless esempio method.Below sessione aiuta come implementare inserto lotto in Grails.
Date startTime = new Date()
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
(1..50000).each {counter ->
Person person = new Person()
person.firstName = "abc"
person.middleName = "abc"
person.lastName = "abc"
person.address = "abc"
person.favouriteGame = "abc"
person.favouriteActor = "abc"
session.save(person)
if(counter.mod(100)==0) {
session.flush();
session.clear();
}
if(counter.mod(10000)==0) {
Date endTime =new Date()
println "Total record insert Counter =>"+counter+" Time =>"+TimeCategory.minus(endTime,startTime)
}
}
tx.commit();
session.close();