Hai bisogno di aiuto migliorare le prestazioni di grandi quantità di dati in Grails

StackOverflow https://stackoverflow.com/questions/3730113

  •  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.

È stato utile?

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).

  1. 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 ...
  2. Chiamata session.setReadOnly () o query.setReadOnly () per disabilitare di Hibernate istantanee di stato
  3. 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()
    
  4. 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:

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();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top