{"id":79893,"date":"2020-05-01T13:43:12","date_gmt":"2020-05-01T11:43:12","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov"},"modified":"2020-05-01T13:43:12","modified_gmt":"2020-05-01T11:43:12","slug":"postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","title":{"rendered":"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Vi invito a esaminare la trascrizione della relazione di Vladimir Sitnikov all'inizio del 2016 intitolata \"PostgreSQL e JDBC: spremere ogni goccia\"<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/9d94c8a024bd2821e431c525aae0127d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/030666abda53aa388b1cb1d0c46a7524.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Buongiorno! Mi chiamo Vladimir Sitnikov. Lavoro da 10 anni in NetCracker e mi occupo principalmente di prestazioni. Tutto ci\u00f2 che riguarda Java e SQL \u00e8 ci\u00f2 che amo. <\/p>\n<p><\/p>\n<p>Oggi parler\u00f2 delle esperienze che abbiamo avuto in azienda quando abbiamo iniziato a utilizzare PostgreSQL come server di database. Principalmente lavoriamo con Java, ma ci\u00f2 di cui parler\u00f2 oggi non riguarda solo Java. Come ha dimostrato la pratica, sorgono anche in altri linguaggi. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b01a214d782c7e32797a6c6466457979.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Parleremo di:<\/p>\n<p><\/p>\n<ul>\n<li>estrazione dei dati. <\/li>\n<li>salvataggio dei dati. <\/li>\n<li>e delle prestazioni. <\/li>\n<li>E delle insidie nascoste che ci sono. <\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/57da0c6aa4bb62e1beb77160f5aee7e7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Iniziamo con una domanda semplice. Estraiamo una riga da una tabella utilizzando la chiave primaria. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/d5fcb1172aef402a87064498bf8a5218.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Il database si trova sullo stesso host. E tutto questo richiede 20 millisecondi.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/103b7f9736b82a3313491b2f0d2a5824.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questi 20 millisecondi sono veramente molti. Se hai 100 richieste, spendi tempo in un secondo per elaborarle, ovvero perdi tempo inutilmente.<\/p>\n<p><\/p>\n<p>Non ci piace fare questo e osserviamo cosa ci offre il database. Il database ci propone due modalit\u00e0 di esecuzione delle query. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b467e86a0f8c4c32cea182fe27a28e6e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La prima opzione \u00e8 una query semplice. Qual \u00e8 il suo vantaggio? Il fatto che la prendiamo e la inviamo, e nient'altro. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7e333a62ba68feb0781ff96e39486591.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/478\">https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/478<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Il database ha anche una query avanzata, che \u00e8 pi\u00f9 complessa, ma pi\u00f9 funzionale. Puoi inviare separatamente richieste per parsing, esecuzione, legame di variabili, ecc. <\/p>\n<p><\/p>\n<p>La super query estesa \u00e8 qualcosa che non tratteremo in questa presentazione. Potremmo avere delle esigenze specifiche, che in qualche modo sono state formulate in una lista, cio\u00e8 ci\u00f2 che desideriamo, ma che attualmente non \u00e8 possibile realizzare e nemmeno nel prossimo anno. Quindi lo abbiamo semplicemente annotato e andremo a convincere le persone principali.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/19094729074fd28c5b9f236833a9a266.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ci\u00f2 che possiamo fare \u00e8 utilizzare la query semplice e la query estesa.<\/p>\n<p><\/p>\n<p>Qual \u00e8 la particolarit\u00e0 di ciascun approccio? <\/p>\n<p><\/p>\n<p>La query semplice \u00e8 utile per un'esecuzione una tantum. Una volta eseguita, puoi dimenticarla. Il problema \u00e8 che non supporta il formato binario dei dati, quindi non \u00e8 adatta per sistemi ad alte prestazioni.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/062c0e45cefd91ece331a306a7651bde.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>La query estesa consente di risparmiare tempo sul parsing. Questo \u00e8 quello che abbiamo fatto e iniziato a utilizzare. Ci \u00e8 stato di grande aiuto. Non solo risparmiamo sul parsing, ma anche sulla trasmissione dei dati. Trasmettere dati in formato binario \u00e8 molto pi\u00f9 efficiente. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/6df83a2f3736568668cf9d74de5d9758.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Passiamo alla pratica. Ecco come appare un'applicazione tipica. Potrebbe essere Java, ecc. <\/p>\n<p><\/p>\n<p>Abbiamo creato uno statement. Abbiamo eseguito un comando. Abbiamo creato un close. Qual \u00e8 qui l'errore? Qual \u00e8 il problema? Non ci sono problemi. \u00c8 cos\u00ec che \u00e8 scritto in tutti i libri. \u00c8 cos\u00ec che bisogna scrivere. Se desideri la massima prestazione, scrivi in questo modo. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/6923293946dec45b3fd508235728ab95.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ma la pratica ha dimostrato che questo non funziona. Perch\u00e9? Perch\u00e9 abbiamo il metodo \u00abclose\u00bb. E quando facciamo cos\u00ec, dal punto di vista del database, \u00e8 come se un fumatore lavorasse con il database. Abbiamo detto \u00abPARSE EXECUTE DEALLOCATE\u00bb.<\/p>\n<p><\/p>\n<p>Perch\u00e9 creare e liberare statement \u00e8 superfluo? Non serve a nessuno. Tuttavia, di solito, in PreparedStatement succede proprio cos\u00ec: quando li chiudiamo, chiudono tutto nel database. Non \u00e8 ci\u00f2 che vogliamo. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5a357a209e414024c437d042f600f251.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vogliamo lavorare con il database in modo sano. Prepariamo il nostro statement una volta e poi lo eseguiamo pi\u00f9 volte. In realt\u00e0, molte volte significa una sola volta per tutta la vita dell'applicazione, in modo da utilizzare lo stesso statement id in diversi REST. Questo \u00e8 il nostro obiettivo. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/ac4aed702a624ad9f9addc4362daf760.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Come possiamo raggiungere questo obiettivo? <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/0f101d890d1a2af8e99f8a3a8a06fd5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00c8 molto semplice: non chiudiamo gli statement. Scriviamo in questo modo: \u00abprepare\u00bb, \u00abexecute\u00bb. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/0b3fd8fd4f5861d4a76cad5a8421ddad.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/724646bfc55b5e2b611b1584ca8ba6aa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se eseguiamo questo, \u00e8 chiaro che da qualche parte qualcosa si riempir\u00e0. Se non \u00e8 chiaro, possiamo misurarlo. Prendiamo e scriviamo un benchmark in cui utilizziamo questo metodo semplice. Creiamo uno statement, lo eseguiamo su una certa versione del driver e scopriamo che crolla piuttosto rapidamente con la perdita di tutta la memoria <\/p>\n<p><\/p>\n<p>\u00c8 chiaro che tali errori possono essere facilmente corretti. Non parler\u00f2 di essi. Ma dir\u00f2 che nella nuova versione funziona molto pi\u00f9 velocemente. Il metodo \u00e8 inefficace, ma comunque. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b232a6205e708c70f52be0024bbe9980.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Come lavorare correttamente? Cosa dobbiamo fare per questo?<\/p>\n<p><\/p>\n<p>In realt\u00e0, le applicazioni chiudono sempre gli statement. In tutti i libri scrivono di chiuderli, altrimenti si verifica una perdita di memoria. <\/p>\n<p><\/p>\n<p>E PostgreSQL non \u00e8 in grado di memorizzare nella cache le query. Ogni sessione deve crearsi autonomamente questa cache. <\/p>\n<p><\/p>\n<p>E noi non vogliamo nemmeno perdere tempo sul parsing. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/85b9574938aa6e6bf59c956e3728bdc3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>E come al solito, abbiamo due opzioni. <\/p>\n<p><\/p>\n<p>La prima opzione \u00e8 che possiamo dire di avvolgere tutto in PgSQL. L\u00ec c'\u00e8 la cache. Memorizza tutto nella cache. Sarebbe fantastico. Abbiamo esaminato questa soluzione. Abbiamo 100500 query. Non funziona. Non siamo d'accordo: non trasformeremo manualmente le query in procedure. No, no. <\/p>\n<p><\/p>\n<p>Abbiamo una seconda opzione: prendere e modificarlo noi stessi. Apriamo i sorgenti e iniziamo a modificare. Modifichiamo, modifichiamo. Si \u00e8 rivelato che non \u00e8 cos\u00ec difficile da fare. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/501620b014800406167d20668baef709.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/319\">https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/319<\/a><\/noindex><\/p>\n<p><\/p>\n<p>\u00c8 stato introdotto nell'agosto 2015. Ora c'\u00e8 una versione pi\u00f9 moderna. E tutto va alla grande. Funziona cos\u00ec bene che non cambiamo nulla nell'applicazione. E abbiamo anche smesso di considerare PgSQL, cio\u00e8 ci \u00e8 bastato per ridurre praticamente a zero tutte le spese generali. <\/p>\n<p><\/p>\n<p>Di conseguenza, le dichiarazioni preparate dal server si attivano alla quinta esecuzione per non consumare memoria nel database per ogni singola richiesta. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/8ee95e71f92187941989ad0c18ece0c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Si potrebbe chiedere: dove sono i numeri? Cosa state ottenendo? E qui non posso dare numeri, perch\u00e9 ogni richiesta ha i suoi.<\/p>\n<p><\/p>\n<p>Abbiamo avuto richieste tali che spendevamo circa 20 millisecondi per il parsing nelle richieste OLTP. C'erano 0,5 millisecondi per l'esecuzione e 20 millisecondi per il parsing. La richiesta consisteva in 10 KiB di testo, 170 righe di piano. Questa \u00e8 una richiesta OLTP. Richiede 1, 5, 10 righe, a volte di pi\u00f9. <\/p>\n<p><\/p>\n<p>Ma non volevamo assolutamente spendere 20 millisecondi. Siamo riusciti a ridurlo a zero. Tutto va bene. <\/p>\n<p><\/p>\n<p>Cosa potete dedurre da tutto ci\u00f2? Se utilizzate Java, prendete la versione moderna del driver e ne sarete contenti. <\/p>\n<p><\/p>\n<p>Se utilizzate un altro linguaggio, pensate: forse questo vi serve anche? Perch\u00e9 dal punto di vista del linguaggio finale, ad esempio, se PL 8 o avete LibPQ, non \u00e8 ovvio che stiate perdendo tempo non nell'esecuzione, ma nel parsing e questo vale la pena verificarlo. Come? Tutto gratuitamente. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/fda6b1c1b126cb85c970e42335e1dc24.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A parte eventuali errori o particolarit\u00e0. E proprio di questo parleremo adesso. La maggior parte riguarder\u00e0 l'archeologia industriale, cio\u00e8 ci\u00f2 che abbiamo trovato e a cosa ci siamo imbattuti. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/9a3b383f83c5e227cd02952c5825b64f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se la richiesta \u00e8 generata dinamicamente. Pu\u00f2 succedere. Qualcuno concatena le stringhe e ottiene una richiesta SQL.<\/p>\n<p><\/p>\n<p>Qual \u00e8 il problema? Il problema \u00e8 che ogni volta otteniamo una stringa diversa.<\/p>\n<p><\/p>\n<p>E per questa stringa diversa \u00e8 necessario ricalcolare nuovamente il hashCode. Questa \u00e8 davvero una questione di CPU: trovare un testo lungo nella richiesta anche se gi\u00e0 presenti nel hash non \u00e8 cos\u00ec semplice. Pertanto, la conclusione \u00e8 semplice: non generare richieste. Conservatele in una sola variabile. E siate contenti.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/bcd4f729204cecdb572a98f357bcc33f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Un altro problema. I tipi di dati sono importanti. Ci sono ORM che dicono che non importa quale sia NULL, va bene qualunque cosa. Se \u00e8 un Int, diciamo setInt. E se \u00e8 NULL, potrebbe sempre essere VARCHAR. E in fin dei conti, quale differenza fa quale NULL? Al database non importa nulla. <\/p>\n<p><\/p>\n<p>In pratica, al database non \u00e8 affatto indifferente. <strong>Se per la prima volta dite che \u00e8 un numero, e la seconda volta dite che \u00e8 VARCHAR, non \u00e8 possibile riutilizzare le dichiarazioni preparate dal server. In tal caso, \u00e8 necessario ricreare la nostra dichiarazione.<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/552d9c2e25cf9abbdfef12c98d027740.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se eseguite la stessa richiesta, assicuratevi che i tipi di dati nella colonna non si confondano. Dovete prestare attenzione a NULL. Questo \u00e8 un errore frequente che abbiamo riscontrato dopo aver iniziato ad utilizzare PreparedStatements.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5d73c8f5dfa281c3bda962fc3e5edd84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Bene, l'abbiamo attivato. Abbiamo preso, forse, il driver. E le prestazioni sono calate. \u00c8 andato tutto male. <\/p>\n<p><\/p>\n<p>Com'\u00e8 possibile? \u00c8 un bug o una funzionalit\u00e0? Sfortunatamente, non siamo riusciti a capire se fosse un bug o una funzionalit\u00e0. Ma c'\u00e8 uno scenario piuttosto semplice per riprodurre questo problema. Ci ha davvero colti di sorpresa. E consiste nel selezionare letteralmente da una sola tabella. Naturalmente, avevamo pi\u00f9 di queste richieste. Solitamente comprendevano due o tre tabelle, ma c'\u00e8 tale scenario di riproduzione. Prendete il vostro database di qualsiasi versione e provate a riprodurlo.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/fa0fdd037c2eaa31667d5a527bb71f9c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1\">https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Il punto \u00e8 che abbiamo due colonne, ognuna delle quali \u00e8 indicizzata. In una colonna ci sono un milione di righe con il valore NULL. E nell'altra colonna ci sono solo 20 righe. Quando eseguiamo senza variabili collegate, tutto funziona bene. <\/p>\n<p><\/p>\n<p>Se iniziamo ad eseguire con variabili collegate, cio\u00e8 utilizziamo il segno \u00ab?\u00bb o \u00ab$1\u00bb per la nostra richiesta, cosa otteniamo alla fine?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e91c397796c7ddcbade3bc090719e9d1.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1\">https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1<\/a><\/noindex><\/p>\n<p><\/p>\n<p>La prima esecuzione \u2013 come previsto. La seconda \u2013 un po' pi\u00f9 veloce. Qualcosa \u00e8 stato memorizzato nella cache. La terza, quarta, quinta. Poi bam \u2013 e in qualche modo cos\u00ec. E la cosa peggiore \u00e8 che questo accade alla sesta esecuzione. Chi sapeva che era necessario farne esattamente sei per comprendere quale fosse il piano di esecuzione effettivo?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/893c955bc1e2e15a6986690e516f72a9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Chi \u00e8 colpevole? Cosa \u00e8 successo? Il database contiene ottimizzazione. Ed \u00e8 ottimizzato per casi generici. E, di conseguenza, a partire da un certo numero di esecuzioni, passa a un piano generico che, sfortunatamente, pu\u00f2 rivelarsi diverso. Potrebbe essere anche lo stesso, ma potrebbe anche essere diverso. E c'\u00e8 un valore soglia che porta a questo comportamento. <\/p>\n<p><\/p>\n<p>Cosa si pu\u00f2 fare a riguardo? Qui, naturalmente, \u00e8 complicato fare delle ipotesi. Esiste una soluzione semplice che utilizziamo. \u00c8 +0, OFFSET 0. Sicuramente conoscete soluzioni simili. Prendiamo e aggiungiamo \u00ab+0\u00bb alla richiesta e va tutto bene. Ve lo mostrer\u00f2 pi\u00f9 tardi. <\/p>\n<p><\/p>\n<p>C'\u00e8 anche un'altra opzione: guardare i piani con pi\u00f9 attenzione. Lo sviluppatore deve non solo scrivere la richiesta, ma anche dire \u00abexplain analyze\u00bb 6 volte. Se lo dice 5, non andr\u00e0 bene. <\/p>\n<p><\/p>\n<p>E c'\u00e8 un terzo modo: scrivere un\u2019email a pgsql-hackers. L'ho fatto, ma non \u00e8 ancora chiaro se sia un bug o una feature.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/8dc8d3a78e0b794e1ceba20f6ca914ae.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1\">https:\/\/gist.github.com\/vlsi\/df08cbef370b2e86a5c1<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Mentre riflettiamo se sia un bug o una feature, vediamo di aggiustarlo. Prendiamo la nostra richiesta e aggiungiamo \u00ab+0\u00bb. Va tutto bene. Solo due caratteri e non \u00e8 nemmeno necessario pensare a come fare. Molto semplice. Abbiamo semplicemente impedito al database di utilizzare un indice su questa colonna. Non abbiamo un indice sulla colonna \u00ab+0\u00bb e quindi il database non usa l'indice, tutto va bene. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5261d80d084786a15b9764f6367014cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questa \u00e8 la regola dei 6 explain. Nelle versioni attuali \u00e8 necessario farlo 6 volte se avete variabili collegate. Se non avete variabili collegate, procediamo in questo modo. E alla fine, \u00e8 proprio questa richiesta che fallisce. Non \u00e8 affatto complicato.<\/p>\n<p><\/p>\n<p>Sembra strano, quanto ci si pu\u00f2 aspettare? Qui un bug, l\u00ec un bug. Realmente ci sono bug ovunque. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/674dc7e7b4624baa5c79eee9e1e89db0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Diamo un'altra occhiata. Ad esempio, abbiamo due schemi. Schema A con la tabella Y e schema B con la tabella Y. La richiesta \u00e8 di selezionare dati dalla tabella. Cosa avremo? Avremo un errore. Tutto quanto detto sopra. La regola \u00e8: ci sono bug ovunque, quindi avremo tutto quanto menzionato.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/dd6f544bb9e930c39e36523c9afe6003.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ora la domanda \u00e8: \u00abPerch\u00e9?\u00bb. Sembrerebbe che ci sia documentazione, dato che se abbiamo uno schema, c\u2019\u00e8 la variabile \u00absearch_path\u00bb che indica dove cercare la tabella. Sembrerebbe che la variabile ci sia.<\/p>\n<p><\/p>\n<p>Qual \u00e8 il problema? Il problema \u00e8 che le dichiarazioni preparate dal server non sospettano che qualcuno possa cambiare il search_path. Questo valore rimane come costante per il database. E alcune parti potrebbero non cogliere i nuovi valori. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/f738301606d84d72bfa7cc5b568c0df8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Certamente, dipende dalla versione su cui stai effettuando il test. Dipende da quanto le tue tabelle differiscono. E la versione 9.1 eseguir\u00e0 semplicemente le vecchie richieste. Le versioni pi\u00f9 recenti potrebbero scoprire l'inganno e segnalare un errore.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4761cd92835b94acbf9d6c9d21817316.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/www.postgresql.org\/message-id\/CAB=Je-GQOW7kU9Hn3AqP1vhaZg_wE9Lz6F4jSp-7cm9_M6DyVA@mail.gmail.com\">Set search_path + server-prepared statements =<br \/>\nil piano memorizzato nella cache non deve cambiare il tipo di risultato<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Come si pu\u00f2 affrontare questo problema? C'\u00e8 una semplice ricetta: non fatelo. Non cambiate il search_path mentre l'applicazione \u00e8 in esecuzione. Se lo cambiate, \u00e8 meglio creare una nuova connessione.<\/p>\n<p><\/p>\n<p>Possiamo discuterne, cio\u00e8 aprirlo, discuterlo, aggiungerlo. Forse convinceremo gli sviluppatori del database che, nel caso in cui qualcuno cambi il valore, il database dovrebbe avvisare il cliente: \u00abGuarda, il tuo valore \u00e8 stato aggiornato. Forse dovresti resettare o ricreare le dichiarazioni?\u00bb. Attualmente il database si comporta in modo subdolo e non informa affatto che le dichiarazioni interne sono cambiate. <\/p>\n<p><\/p>\n<p>E ancora una volta sottolineo: questo non \u00e8 tipico per Java. Lo stesso lo vedremo in PL\/pgSQL uno a uno. Ma l\u00ec sar\u00e0 riprodotto.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/a4e7b201d0c0aa4a0092d2b6ff152df7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Proviamo a selezionare di nuovo i dati. Selezioniamo, selezioniamo. Abbiamo una tabella con un milione di righe. Ogni riga \u00e8 di un kilobyte. Circa un gigabyte di dati. E abbiamo 128 megabyte di memoria di lavoro nella macchina Java. <\/p>\n<p><\/p>\n<p>Come consigliato in tutti i libri, utilizziamo l'elaborazione a flusso. Cio\u00e8, apriamo il resultSet e leggiamo i dati poco alla volta. Funzioner\u00e0? Non andr\u00e0 in errore di memoria? Legger\u00e0 poco alla volta? Crediamo nel database, crediamo in Postgres. Non ci fidiamo. Cadremo in OutOfMemory? Chi di voi ha ricevuto un OutOfMemory? E chi \u00e8 riuscito a risolverlo dopo? Qualcuno \u00e8 riuscito a risolverlo. <\/p>\n<p><\/p>\n<p>Se hai un milione di righe, non puoi semplicemente selezionare cos\u00ec. Devi assolutamente usare OFFSET\/LIMIT. Chi \u00e8 a favore di questa opzione? E chi \u00e8 a favore dell'idea di giocare con autoCommit? <\/p>\n<p><\/p>\n<p>Qui, come al solito, l'opzione pi\u00f9 inaspettata si rivela quella giusta. E se all'improvviso disabiliti autoCommit, questo aiuter\u00e0. Perch\u00e9? La scienza non lo sa. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2e5d9d2a8450a9069155de86a8ec387a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ma per impostazione predefinita, tutti i client che si connettono al database Postgres selezionano i dati per intero. PgJDBC non fa eccezione, seleziona tutte le righe.<\/p>\n<p><\/p>\n<p>C'\u00e8 una variante sul tema del FetchSize, cio\u00e8 puoi dire a livello di singola dichiarazione di selezionare i dati in blocchi di 10, 50. Ma questo non funziona finch\u00e9 non disattivi autoCommit. Disabilitato autoCommit \u2013 inizia a funzionare. <\/p>\n<p><\/p>\n<p>Ma sfogliare il codice e impostare ovunque setFetchSize \u00e8 scomodo. Quindi abbiamo creato una configurazione che imposta un valore predefinito per l'intera connessione.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/c9ac8e483dc6bfdbbc300972ee1ab1e0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco, lo abbiamo impostato. Abbiamo configurato il parametro. E cosa abbiamo ottenuto? Se selezioniamo in piccolo, ad esempio, 10 righe, abbiamo costi di overhead piuttosto elevati. Pertanto, dovremmo impostare questo valore intorno a cento. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/073e7a4af63688396eefe332ba8261d6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>In ideale, naturalmente, dovremmo anche imparare a limitare in byte, ma la ricetta \u00e8 questa: impostiamo defaultRowFetchSize superiore a cento e ci rallegriamo. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e3078faf2078fcaec10d6d99872a9990.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Passiamo all'inserimento dei dati. L'inserimento \u00e8 pi\u00f9 semplice e offre diverse opzioni. Ad esempio, INSERT, VALUES. Questo \u00e8 un buon metodo. Possiamo anche dire 'INSERT SELECT'. In pratica, \u00e8 la stessa cosa. Non c'\u00e8 alcuna differenza in termini di prestazioni. <\/p>\n<p><\/p>\n<p>I libri affermano che \u00e8 necessario eseguire il Batch statement, e che si possono eseguire comandi pi\u00f9 complessi con pi\u00f9 parentesi. In Postgres c'\u00e8 una funzione fantastica: \u00e8 possibile fare COPY, ovvero farlo pi\u00f9 velocemente. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4d281c9515bdf8de7fbf1e9922d192d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Misurando, possiamo scoprire cose interessanti. Come vogliamo che funzioni? Vogliamo evitare il parsing e l'esecuzione di comandi superflui. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7b2e4343c51dab3ddb6919bf0f117c4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>In pratica, il TCP non ci permette di farlo. Se il client \u00e8 impegnato a inviare una richiesta, il database non legge le richieste mentre cerca di inviarci le risposte. Di conseguenza, il client attende che il database legga la richiesta, mentre il database aspetta che il client legga la risposta. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/17a062c30ddd83cb890775ec1722044f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Pertanto, il client \u00e8 costretto a inviare periodicamente un pacchetto di sincronizzazione. Interazioni di rete non necessarie, perdita di tempo aggiuntiva.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/ae7a4f32e2949c1d0550da051937de02.jpg\" style=\"display:block;margin: 0 auto;\" \/>E pi\u00f9 ne aggiungiamo, peggio diventa. Il driver \u00e8 piuttosto pessimista e le aggiunge abbastanza frequentemente, circa una volta ogni 200 righe, a seconda della lunghezza delle righe, ecc. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/82b1d597986633a8c4b7952517ca6477.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/380\">https:\/\/github.com\/pgjdbc\/pgjdbc\/pull\/380<\/a><\/noindex><\/p>\n<p><\/p>\n<p>A volte, modificando una sola riga, tutto pu\u00f2 diventare dieci volte pi\u00f9 veloce. Questo pu\u00f2 capitare. Perch\u00e9? Come al solito, una costante \u00e8 stata gi\u00e0 utilizzata da qualche parte, e il valore '128' significava non utilizzare il batching.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7b6bcd95591b36441037b4c4631d2ad0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/openjdk.java.net\/projects\/code-tools\/jmh\/\">Java microbenchmark harness<\/a><\/noindex><\/p>\n<p><\/p>\n<p>\u00c8 un bene che questo non sia finito nella versione ufficiale. \u00c8 stato scoperto prima di iniziare a rilasciare la versione. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28c82d9e11f6bdaf3cf62b49fc074d68.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Misuriamo. Stiamo misurando InsertBatch semplice. Stiamo misurando InsertBatch multiplo, cio\u00e8 lo stesso, ma con molti valori. Un trucco astuto. Non tutti lo sanno fare, ma \u00e8 un approccio semplice, molto pi\u00f9 facile di COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/72f7cf6c3b9d9175d3e5a6d5410fe209.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00c8 possibile fare COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2b0d8bbcc50f1293c9a8a1eb8eb70d2e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>E pu\u00f2 essere fatto con strutture. Dichiarare un tipo di utente predefinito, passare un array e inserire direttamente nella tabella. <\/p>\n<p><\/p>\n<p>Se aprite il link: pgjdbc\/ubenchmsrk\/InsertBatch.java, potete trovare questo codice su GitHub. Potete vedere specificamente quali query vengono generate. Non \u00e8 questo il punto.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28dede211fe401286685ca62081453de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Abbiamo eseguito il test. E la prima cosa che abbiamo capito \u00e8 che non usare il batch \u00e8 semplicemente impossibile. Tutte le opzioni di batching sono pari a zero, cio\u00e8 il tempo di esecuzione \u00e8 praticamente pari a zero rispetto all'esecuzione singola. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4ea68f712bbdeadd35f5baa4ddaeff33.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Stiamo inserendo dati. La tabella \u00e8 piuttosto semplice. Tre colonne. E cosa vediamo qui? Vediamo che tutte e tre le opzioni sono comparabili. E COPY \u00e8 ovviamente la migliore.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2686f1d7eea347a839ac23aef2a27e53.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Questo \u00e8 quando ci inseriamo a pezzi. Quando dicevamo valore VALUES singolo, due valori VALUES, tre valori VALUES, o se ne menzioniamo 10 separati da virgole. Questo \u00e8 il modo in cui appare ora in orizzontale. 1, 2, 4, 128. \u00c8 chiaro che Batch Insert, rappresentato in blu, ne guadagna molto. Cio\u00e8, quando inserite uno alla volta, o persino quando inserite quattro alla volta, diventa due volte pi\u00f9 veloce, semplicemente perch\u00e9 abbiamo inserito un po' pi\u00f9 di dati in VALUES. Meno operazioni EXECUTE.<\/p>\n<p><\/p>\n<p>Usare COPY su piccole quantit\u00e0 \u00e8 estremamente inefficace. Non l'ho nemmeno disegnato per le prime due. Vanno alle stelle, cio\u00e8 questi numeri verdi per COPY.<\/p>\n<p><\/p>\n<p>COPY dovrebbe essere usato quando hai un volume di dati di almeno pi\u00f9 di cento righe. Le spese generali per l'apertura di quella connessione sono elevate. E, a essere onesti, non ho approfondito questo aspetto. Ho ottimizzato il batch, ma non COPY. <\/p>\n<p><\/p>\n<p>Cosa facciamo dopo? Abbiamo misurato. Comprendiamo che dobbiamo usare o strutture, o un astuto batch che combina pi\u00f9 valori. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC spremiamo ogni risorsa. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e02fa2574e2d1b678382db15064610d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Qual \u00e8 il messaggio chiave di questa presentazione?<\/p>\n<p><\/p>\n<ul>\n<li>PreparedStatement \u00e8 tutto per noi. Offre molto in termini di prestazioni. Porta una buona dose di problemi. <\/li>\n<li>E dobbiamo eseguire EXPLAIN ANALYZE 6 volte.<\/li>\n<li>Dobbiamo anche diversificare OFFSET 0 e utilizzare trucchi come +0 per correggere il restante percentuale dei nostri problemi con le query.<\/li>\n<\/ul>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/499794\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot; \u0414\u043e\u0431\u0440\u044b\u0439 \u0434\u0435\u043d\u044c! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432. \u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e 10 \u043b\u0435\u0442 \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 NetCracker. \u0418 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u044f \u0437\u0430\u043d\u0438\u043c\u0430\u044e\u0441\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e. \u0412\u0441\u0435, \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 Java, \u0432\u0441\u0435, \u0447\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0441 SQL \u2013 \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e \u044f \u043b\u044e\u0431\u043b\u044e. \u0418 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":79894,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-79893","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot;\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438. \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432 | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot;\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-05-01T11:43:12+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-01T11:43:12+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47PostgreSQL e JDBC: ottimizziamo ogni goccia. Vladimir Sitnikov | ProHoster","description":"Vi invitiamo a consultare la trascrizione della presentazione di Vladimir Sitnikov all'inizio del 2016 \"PostgreSQL e JDBC: tiriamo fuori tutto il potenziale\".","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438. \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432 | ProHoster","og:description":"\u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0440\u0430\u0441\u0448\u0438\u0444\u0440\u043e\u0432\u043a\u043e\u0439 \u0434\u043e\u043a\u043b\u0430\u0434\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 2016 \u0433\u043e\u0434\u0430 \u0412\u043b\u0430\u0434\u0438\u043c\u0438\u0440\u0430 \u0421\u0438\u0442\u043d\u0438\u043a\u043e\u0432\u0430 &quot;PostgreSQL \u0438 JDBC \u0432\u044b\u0436\u0438\u043c\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u043a\u0438&quot;","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-05-01T11:43:12+00:00","article:modified_time":"2020-05-01T11:43:12+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"79893","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:29:39","updated":"2022-10-10 00:32:43","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/79893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=79893"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/79893\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/79894"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=79893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=79893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=79893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}