2009-08-23 5 views
32

Ho questo tavolo davvero grande con alcuni milioni di dischi ogni giorno e alla fine di ogni giorno sto estraendo tutti i record del giorno precedente. Sto facendo questo tipo:Che cosa fa veramente il metodo Statement.setFetchSize (nSize) nel driver JDBC di SQL Server?

String SQL = "select col1, col2, coln from mytable where timecol = yesterday"; 
Statement.executeQuery(SQL); 

Il problema è che questo programma prende come 2 GB di memoria, perché ci vuole tutti i risultati in memoria allora elabora.

Ho provato a impostare il Statement.setFetchSize(10) ma richiede esattamente la stessa memoria dal sistema operativo che non fa alcuna differenza. Sto usando Driver JDBC Microsoft SQL Server 2005 per questo.

C'è un modo per leggere i risultati in piccoli blocchi come fa il driver del database Oracle quando la query viene eseguita per mostrare solo poche righe e mentre scorri verso il basso vengono mostrati più risultati?

risposta

2

interfaccia Normativa Doc

SINTESI: void setFetchSize(int rows) concede il driver JDBC un indizio della numero di righe che devono essere recuperati dal database quando altre righe sono necessario.

Leggi questo ebook J2EE and beyond By Art Taylor

1

Sembra a me che si davvero desidera limitare le righe restituiti nella query e scorrere i risultati. Se è così, puoi fare qualcosa del tipo:

select * from (select rownum myrow, a.* from TEST1 a) 
where myrow between 5 and 10 ; 

Devi solo determinare i tuoi confini.

9

È necessario assicurarsi che il commit automatico su Connection sia impostato su off o setFetchSize non abbia alcun effetto.

dbConnection.setAutoCommit(false); 

Edit: ricordare che quando ho usato questo risolvere il problema era Postgres-specifica, ma speriamo che continuerà a funzionare per SQL Server.

+5

Non conosco Postgres ma ... l'impostazione di auto-commit non dovrebbe avere alcun riferimento su un'istruzione SELECT e/o su una query fetch-size (sono davvero non correlate). –

+0

@jwaddell È questo? Se il commit automatico sulla connessione è attivato, setFetchSize non avrà alcun effetto. Anche vero per 'Oracle 11g'? –

+0

Commit è una funzione di istruzioni DML (data-manipulation-language) come INSERT, UPDATE, DELETE nel database di commit a due fasi (2PC). Questo non ha nulla a che fare con le query, che è ciò che "fetch" è correlato a "SELECT". Quindi il valore di auto-commit (l'istruzione DML è auto-commit sull'esecuzione rispetto a dover eseguire separatamente un commit in seguito) non ha nulla a che fare con il recupero in Oracle, MySQL o SQLServer e probabilmente con qualsiasi altro RDBMS. –

3

Suoni come mssql jdbc esegue il buffering dell'intero set di risultati. È possibile aggiungere un parametro stringa di connessione che dice selectMode = cursor o responseBuffering = adaptive. Se si utilizza la versione 2.0+ del driver jdbc mssql 2005, il buffer di risposta deve essere adattato automaticamente.

http://msdn.microsoft.com/en-us/library/bb879937.aspx

22

Il parametro fetchSize è un suggerimento al driver JDBC per molte righe per andare a prendere in un colpo solo dal database. Ma l'autista è libero di ignorarlo e fa ciò che ritiene opportuno. Alcuni driver, come quello di Oracle, recuperano le righe in blocchi, quindi puoi leggere set di risultati molto grandi senza bisogno di molta memoria. Altri piloti hanno appena letto nell'intero set di risultati in una volta sola, e immagino sia quello che sta facendo il tuo autista.

È possibile provare ad aggiornare il driver alla versione di SQL Server 2008 (che potrebbe essere migliore) o al driver jTDS open source.

+0

assolutamente corretto. Per MSSQL il driver jTDS è una scelta migliore. – BalusC

+0

Come si imposta responseBuffering in modo adattivo sul driver jTDS, e intendo, non a livello di driver, ma a livello di query? – eugenevd

46

In JDBC, il metodo setFetchSize(int) è molto importante per le prestazioni e la gestione della memoria all'interno della JVM poiché controlla il numero di chiamate di rete dalla JVM al database e corrispondentemente la quantità di RAM utilizzata per l'elaborazione ResultSet.

Intrinsecamente se setFetchSize (10) viene chiamato e il conducente sta ignorando, ci sono probabilmente solo due opzioni:

  1. provare un driver JDBC diverso che onorerà il suggerimento fetch-size.
  2. Controllare le proprietà specifiche del driver sulla connessione (URL e/o mappa delle proprietà durante la creazione dell'istanza di Connection).

Il set RISULTATO è il numero di righe inserite nel DB in risposta alla query. Il ROW-SET è il blocco di righe che vengono recuperate dal RESULT-SET per chiamata dalla JVM al DB. Il numero di queste chiamate e la RAM risultante necessaria per l'elaborazione dipende dall'impostazione della dimensione del recupero.

Quindi, se il risultato-SET ha 100 righe e il fetch-dimensione è 10, ci saranno 10 di rete chiama per recuperare tutti i dati, utilizzando circa il 10 * {fila-content-size} RAM in un dato tempo.

La dimensione di recupero predefinita è 10, che è piuttosto piccola. Nel caso pubblicato, sembrerebbe che il driver stia ignorando l'impostazione della dimensione del recupero, recuperando tutti i dati in una chiamata (requisito RAM elevato, chiamate di rete minime ottimali).

Cosa succede sotto lo ResultSet.next() è che in realtà non recupera una riga alla volta da RESULT-SET. Esso recupera quello dal (locale) ROW-SET e recupera il successivo ROW-SET (invisibilmente) dal server quando si esaurisce sul client locale.

Tutto ciò dipende dal driver poiché l'impostazione è solo un "suggerimento", ma in pratica ho scoperto che questo è il modo in cui funziona per molti driver e database (verificati in molte versioni di Oracle, DB2 e MySQL).

+0

So che questa è una vecchia risposta. Ma ho una domanda. Qual è la differenza tra fetchsize e scroll? Secondo la mia comprensione, lo scroll è usato in modo che non recuperiamo tutti i risultati alla volta. –

+0

Lo scorrimento è il processo di andare avanti/indietro attraverso il set di risultati. Per scorrere, uno recupera in modo intrinseco o potrebbe essere già stato caricato nella memoria JVM. I cursori in vari database potrebbero essere inoltrati, nonostante le opzioni in JDBC per scorrere all'indietro attraverso un set di dati recuperato. Quindi la dimensione del fetch è ancora il modo in cui si imposta la quantità di dati che abbiamo prelevato dal DB attraverso la rete. Le impostazioni di scorrimento non influiscono su questo in JDBC. –

1

Prova questo:

String SQL = "select col1, col2, coln from mytable where timecol = yesterday"; 

connection.setAutoCommit(false); 
PreparedStatement stmt = connection.prepareStatement(SQL, SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY, SQLServerResultSet.CONCUR_READ_ONLY); 
stmt.setFetchSize(2000); 

stmt.set.... 

stmt.execute(); 
ResultSet rset = stmt.getResultSet(); 

while (rset.next()) { 
    // ...... 
0

ho avuto lo stesso problema in un progetto. Il problema è che anche se la dimensione del recupero potrebbe essere abbastanza piccola, JDBCTemplate legge tutto il risultato della tua query e lo mappa in una lista enorme che potrebbe far saltare in aria la tua memoria. Ho finito per estendere NamedParameterJdbcTemplate per creare una funzione che restituisce un flusso di oggetto. Quel flusso è basato sul ResultSet normalmente restituito da JDBC, ma estrarrà i dati dal ResultSet solo come richiesto dallo Stream. Questo funzionerà se non mantieni un riferimento a tutti gli oggetti che questo Stream sputa. Mi sono ispirato molto all'implementazione di org.springframework.jdbc.core.JdbcTemplate # execute (org.springframework.jdbc.core.ConnectionCallback). L'unica vera differenza ha a che fare con cosa fare con il ResultSet. Ho finito per scrivere questa funzione per concludere il ResultSet:

private <T> Stream<T> wrapIntoStream(ResultSet rs, RowMapper<T> mapper) { 
    CustomSpliterator<T> spliterator = new CustomSpliterator<T>(rs, mapper, Long.MAX_VALUE, NON-NULL | IMMUTABLE | ORDERED); 
    Stream<T> stream = StreamSupport.stream(spliterator, false); 
    return stream; 
} 
private static class CustomSpliterator<T> extends Spliterators.AbstractSpliterator<T> { 
    // won't put code for constructor or properties here 
    // the idea is to pull for the ResultSet and set into the Stream 
    @Override 
    public boolean tryAdvance(Consumer<? super T> action) { 
     try { 
      // you can add some logic to close the stream/Resultset automatically 
      if(rs.next()) { 
       T mapped = mapper.mapRow(rs, rowNumber++); 
       action.accept(mapped); 
       return true; 
      } else { 
       return false; 
      } 
     } catch (SQLException) { 
      // do something with this Exception 
     } 
    } 
} 

si può aggiungere un po 'di logica per fare che Stream "auto richiudibile", altrimenti non dimenticate per chiuderla quando si è fatto.