2016-06-21 17 views
11

Lavoro su un sistema che scarica i dati da un sistema cloud in un database locale (PostgreSQL, MySQL, ...). Ora ho un problema con le prestazioni di PostgreSQL perché ci vuole un sacco di tempo per inserire i dati.Inserimento lento su PostgreSQL utilizzando JDBC

Un numero di colonne e la dimensione dei dati possono variare. In un progetto di esempio, ho una tabella con ca. 170 colonne. C'è un indice univoco, ma anche dopo aver perso l'indice la velocità dell'inserto non è cambiata.

Sto utilizzando il driver JDBC per connettermi al database e sto inserendo i dati in batch di 250 righe (utilizzando NamedParameterJdbcTemplate).

Mi ci sono voluti ca. 18 secondi per inserire i dati su Postgres. Lo stesso set di dati su MySQL mi ha richiesto solo un secondo. Questa è un'enorme differenza - da dove viene? Il driver Postgres JDBC è lento? Può essere configurato in qualche modo per renderlo più veloce? Mi manca qualcos'altro? La differenza tra Postgres e MySQL è così grande. Altre idee su come renderlo più veloce?

Ho creato un progetto di esempio disponibile su Github - https://github.com/varad/postgresql-vs-mysql. Tutto avviene in LetsGo class nel metodo "run".

+1

potresti abilitare più debug per vedere cosa è effettivamente lento? (inserire, commettere, connettere)? per il debug in java (per driver) loglevel = 2 (https://jdbc.postgresql.org/documentation/80/connect.html) per la registrazione lato server https://www.drupal.org/node/560192 –

+0

Di quali versioni stiamo parlando? Hai provato con una transazione? – m0skit0

+0

Inoltre, hai provato a fare retromarcia e inseriscilo su MySQL per primo e vedi cosa succede (come 'letsGo.run (Type.MYSQL); letsGo.run (Type.POSTGRES);')? Inoltre, come stai controllando i tempi? – m0skit0

risposta

5

sembra che questa è una combinazione di una molla "bug" e un driver "bug" .

Spring tenta di determinare il tipo di dati di una colonna ogni volta che viene chiamato setValue(). Lo fa chiamando PreparedStatementMetaData.getParameterMetaData()

Questo fa sì che apparentemente un "preparare" dichiarazione da inviare al database che di per sé è abbastanza veloce (non più di 1 ms sul mio portatile), ma, come viene chiamato per ogni colonna per ogni riga questo riassume per un sacco di tempo (è chiamato per ogni valore non nullo che si traduce in circa 23.000 chiamate)

In un certo senso questo è più un bug di primavera che un errore di driver perché non memorizza nella cache il parametro meta i dati non hanno davvero senso (almeno secondo me). Il driver JDBC MySQL non supporta getParameterMetaData() e Spring lo sa e quindi questo "bug" non viene visualizzato con MySQL perché Spring non chiama mai quel metodo.

Non sono sicuro che il comportamento del driver JDBC di Postgres possa essere classificato come un bug, ma sicuramente sarebbe bello se il driver stesse memorizzando nella cache i metadati dopo la prima chiamata.

molla può essere convinto a non ottenere la dichiarazione meta-dati mediante la proprietà spring.jdbc.getParameterType.ignore

Quindi, mettendo:

System.setProperty("spring.jdbc.getParameterType.ignore", "true"); 

prima la linea:

LetsGo letsGo = new LetsGo(); 

Questo comportamento è Disabilitato.

La proprietà deve essere impostata su prima del La molla viene inizializzata.

Quando lo faccio con il vostro progetto di esempio, l'inserto viene eseguito in 500 ms sul mio laptop.


Modifica

Dopo aver visto il commento per quanto riguarda l'utilizzo del driver Postgres-NG ho scavato nelle fonti del pilota "ufficiale" e il conducente NG, e il conducente NG non memorizza nella cache del parametro metadati dopo la prima chiamata mentre il driver ufficiale non spiega il motivo per cui l'utilizzo del driver NG è molto più veloce (senza disabilitare la chiamata in primavera)

+0

Grazie! Credo che questa sia una risposta perfetta. Anche se se si imposta la proprietà ottengo "DataIntegrityViolationException" non sono sicuro del perché. Hai dovuto cambiare qualcos'altro se non per aggiungere la proprietà? – varad

+0

@varad: no, era l'unica cosa che ho cambiato. –

+0

Interessante, sto ricevendo "22P02: sintassi di input non valida per intero". Ma immagino che questo sia un altro tipo di problema. – varad

1

provare a utilizzare il driver pgjdbc-ng e quindi confrontare i risultati.

E 'disponibile qui: http://impossibl.github.io/pgjdbc-ng/

+1

Wow, con questo driver è stato velocissimo. Solo mezzo secondo! – varad

0

nella stringa di connessione, aggiungere il seguente:

&useServerPrepStmts=false&rewriteBatchedStatements=true 
+0

Grazie, ma non ha fatto alcuna differenza. – varad

-1

Spero che stiate usando DB Connection Pool. Puoi provare C3P0. Spring (JDBCTemplate) non fornisce l'implementazione del pool di connessioni.

Problemi correlati