{"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 tutto. Vladimir Sitnikov","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Ti invitiamo a prendere visione della trascrizione della relazione di inizio 2016 di Vladimir Sitnikov \"PostgreSQL e JDBC spremiamo tutto il succo\".<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 tutto. 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 principalmente mi occupo di prestazioni. Tutto ci\u00f2 che riguarda Java, tutto ci\u00f2 che riguarda SQL \u2013 \u00e8 quello che amo. <\/p>\n<p><\/p>\n<p>E oggi parler\u00f2 di ci\u00f2 con cui ci siamo trovati in azienda quando abbiamo iniziato a utilizzare PostgreSQL come server di database. E lavoriamo principalmente con Java. Ma ci\u00f2 di cui parler\u00f2 oggi non riguarda solo Java. Come la pratica ha dimostrato, si verifica anche in altri linguaggi. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 anche delle prestazioni. <\/li>\n<li>E delle trappole per principianti, che si nascondono l\u00ec. <\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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. Selezioniamo una riga da una tabella in base alla chiave primaria. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 ci\u00f2 richiede 20 millisecondi.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 molti. Se ne avete 100 di richieste, state sprecando tempo in secondi per far passare queste richieste, cio\u00e8 state sprecando tempo.<\/p>\n<p><\/p>\n<p>Non ci piace farlo e vediamo cosa ci offre il database. Il database ci propone due opzioni per l'esecuzione delle richieste. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 semplice richiesta. Cosa ha di buono? Che la prendiamo e la inviamo, e nient'altro. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 richiesta avanzata, che \u00e8 pi\u00f9 astuta, ma pi\u00f9 funzionale. \u00c8 possibile inviare separatamente la richiesta per il parsing, l'esecuzione, l'associazione delle variabili, ecc. <\/p>\n<p><\/p>\n<p>La super richiesta estesa \u2013 \u00e8 qualcosa che non tratteremo nella relazione attuale. Potremmo voler fare qualcosa dal database e c'\u00e8 una lista di desideri che \u00e8 stata formata in qualche modo, cio\u00e8 \u00e8 ci\u00f2 che vogliamo, ma non \u00e8 possibile al momento e nel prossimo anno. Pertanto, \u00e8 stata semplicemente scritta e andremo a parlare con le persone principali.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 una richiesta semplice e una richiesta estesa.<\/p>\n<p><\/p>\n<p>Qual \u00e8 la peculiarit\u00e0 di ciascun approccio? <\/p>\n<p><\/p>\n<p>La richiesta semplice \u00e8 bene usarla per esecuzioni una tantum. Una volta eseguita, basta dimenticarla. E il problema \u00e8 che non supporta il formato binario dei dati, cio\u00e8 non \u00e8 adatta per alcuni sistemi ad alte prestazioni.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 durante il parsing. \u00c8 ci\u00f2 che abbiamo fatto e iniziato a utilizzare. Ci ha aiutato enormemente. Non c'\u00e8 solo risparmio sul parsing. C'\u00e8 risparmio anche nella trasmissione dei dati. Trasmettere i dati in formato binario \u00e8 molto pi\u00f9 efficiente. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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. Pu\u00f2 essere Java e cos\u00ec via. <\/p>\n<p><\/p>\n<p>Abbiamo creato un statement. Abbiamo eseguito il comando. Abbiamo creato un close. Qual \u00e8 qui l'errore? Qual \u00e8 il problema? Non ci sono problemi. Cos\u00ec \u00e8 scritto in tutti i libri. \u00c8 cos\u00ec che si deve scrivere. Se vuoi massimizzare le prestazioni, scrivi cos\u00ec. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 \"close\". E quando facciamo cos\u00ec, dal punto di vista del database, sembra che sia come il lavoro di un fumatore con il database. Abbiamo detto \"PARSE EXECUTE DEALLOCATE\".<\/p>\n<p><\/p>\n<p>Perch\u00e9 creare e scaricare statement inutilmente? Non servono a nessuno. Ma di solito, in PreparedStatement, accade cos\u00ec, quando li chiudiamo, chiudono tutto nel database. Non \u00e8 quello che vogliamo. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5a357a209e414024c437d042f600f251.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Vogliamo, come persone sane, lavorare con il database. Una volta abbiamo preparato il nostro statement e poi lo eseguiamo molte volte. In realt\u00e0, molte volte significa una volta per tutta la vita dell'applicazione, abbiamo fatto il parsing. E utilizziamo lo stesso statement id in diversi REST. Questa \u00e8 la nostra meta. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/0f101d890d1a2af8e99f8a3a8a06fd5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Molto semplice: non dobbiamo chiudere gli statement. Scriviamo cos\u00ec: \"prepare\" \"execute\". <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/724646bfc55b5e2b611b1584ca8ba6aa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se lanciamo qualcosa di simile, \u00e8 chiaro che da qualche parte abbiamo un overflow. Se non \u00e8 chiaro, possiamo misurare. Prendiamo e scriviamo un benchmark, in cui usiamo questo metodo semplice. Creiamo uno statement. Lo eseguiamo su una certa versione del driver e vediamo che va gi\u00f9 piuttosto velocemente, con perdita di tutta la memoria di cui disponevamo. <\/p>\n<p><\/p>\n<p>\u00c8 chiaro che questi errori sono facili da correggere. Non parler\u00f2 di loro. Ma dir\u00f2 che nella nuova versione funziona molto pi\u00f9 velocemente. Il metodo \u00e8 poco sensato, ma tuttavia. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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>Nella realt\u00e0, le applicazioni chiudono sempre gli statement. In tutti i libri si scrive che devono essere chiusi, altrimenti la memoria perde. <\/p>\n<p><\/p>\n<p>E PostgreSQL non sa come memorizzare nella cache le query. Ogni sessione deve creare questa cache per se stessa. <\/p>\n<p><\/p>\n<p>E non vogliamo nemmeno spendere tempo per il parsing. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 diciamo di avvolgere tutto in PgSQL. L\u00ec c'\u00e8 una cache. Essa memorizza tutto. Risulter\u00e0 fantastico. Abbiamo dato un'occhiata a questo. Abbiamo 100500 richieste. Non funziona. Non siamo d'accordo a trasformare le richieste manualmente in procedure. No-no. <\/p>\n<p><\/p>\n<p>Abbiamo una seconda opzione: prenderci e svilupparlo noi stessi. Apriamo il codice sorgente, iniziamo a scrivere. Scriviamo e scriviamo. Si \u00e8 scoperto che non \u00e8 cos\u00ec difficile farlo. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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>Questo \u00e8 emerso nell'agosto del 2015. Ora c'\u00e8 gi\u00e0 una versione pi\u00f9 moderna. E tutto \u00e8 fantastico. Funziona cos\u00ec bene che non cambiamo nulla nell'applicazione. E abbiamo persino smesso di pensare a 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 al quinto utilizzo per non sprecare memoria nel database per ogni richiesta una tantum. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/8ee95e71f92187941989ad0c18ece0c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Si pu\u00f2 chiedere: dove sono i numeri? Cosa ottenete? E qui non dar\u00f2 numeri, perch\u00e9 ogni richiesta ha i propri.<\/p>\n<p><\/p>\n<p>Le nostre richieste erano tali che su richieste OLTP impiegavamo circa 20 millisecondi per il parsing. Ci volevano 0,5 millisecondi per l'esecuzione e 20 millisecondi per il parsing. La richiesta \u00e8 di 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 affatto spendere 20 millisecondi. Siamo riusciti a ridurre a 0. Tutto \u00e8 fantastico. <\/p>\n<p><\/p>\n<p>Cosa potete trarre da questo? Se avete Java, prendete la versione moderna del driver e godetevela. <\/p>\n<p><\/p>\n<p>Se avete un'altra lingua, pensate: potrebbe servirvi anche a voi? Perch\u00e9 dal punto di vista del linguaggio finale, ad esempio, se PL 8 o avete LibPQ, non \u00e8 chiaro che state sprecando tempo non per l'esecuzione, ma per il parsing e questo vale la pena verificare. Come? \u00c8 tutto gratuito. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/fda6b1c1b126cb85c970e42335e1dc24.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A parte che ci sono errori e alcune peculiarit\u00e0. E di questo parleremo ora. Gran parte sar\u00e0 sulla archeologia industriale, su ci\u00f2 che abbiamo trovato, su cosa ci siamo imbattuti. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 viene generata dinamicamente. Questo accade. Qualcuno concatena le stringhe e ottiene una richiesta SQL.<\/p>\n<p><\/p>\n<p>Perch\u00e9 \u00e8 negativa? \u00c8 negativa perch\u00e9 ogni volta finiamo con stringhe diverse.<\/p>\n<p><\/p>\n<p>E anche questa stringa diversa deve ricalcolare il hashCode. \u00c8 davvero un compito da CPU \u2013 trovare un lungo testo di query in un hash esistente non \u00e8 cos\u00ec semplice. Quindi, la conclusione \u00e8 semplice: non generare query. Conservale in una singola variabile. E sii soddisfatto.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 Int, diciamo setInt. E se \u00e8 NULL, deve sempre essere VARCHAR. E che differenza fa alla fine quale NULL? Il database capir\u00e0 tutto da solo. E questo approccio non funziona. <\/p>\n<p><\/p>\n<p>In pratica, al database non importa affatto. <strong>Se la prima volta hai detto che \u00e8 un numero e la seconda volta hai detto che \u00e8 VARCHAR, non puoi riutilizzare le dichiarazioni preparate dal server. E in questo caso devi ricreare la nostra dichiarazione.<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/552d9c2e25cf9abbdfef12c98d027740.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se esegui la stessa query, assicurati che i tipi di dati nella colonna non siano confusi. Devi prestare attenzione a NULL. Questo \u00e8 un errore comune che abbiamo commesso dopo aver iniziato a utilizzare i PreparedStatements.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5d73c8f5dfa281c3bda962fc3e5edd84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Va bene, l'abbiamo attivata. Abbiamo preso, forse, un driver. E le prestazioni sono diminuite. Tutto \u00e8 andato male. <\/p>\n<p><\/p>\n<p>Com'\u00e8 possibile? \u00c8 un bug o una funzione? Purtroppo, non siamo riusciti a capire se fosse un bug o una funzione. Ma c'\u00e8 uno scenario di riproduzione piuttosto semplice per questo problema. Ci ha colto di sorpresa. Si tratta di una selezione da una sola tabella. Naturalmente, avevamo pi\u00f9 di queste query. In genere includevano due o tre tabelle, ma c'\u00e8 questo scenario di riproduzione. Prendi il tuo database di qualsiasi versione e riproducilo.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 senso \u00e8 che abbiamo due colonne, ognuna delle quali \u00e8 indicizzata. In una colonna ci sono un milione di righe con 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 a eseguire con variabili collegate, cio\u00e8 utilizziamo il segno \"?\" o \"$1\" per la nostra query, cosa otteniamo alla fine?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 di consueto. La seconda \u2013 un po' pi\u00f9 veloce. Qualcosa \u00e8 stato memorizzato nella cache. La terza, quarta, quinta. Poi, bam \u2013 e in questo modo. E la cosa peggiore \u00e8 che succede alla sesta esecuzione. Chi sapeva che erano necessarie sei esecuzioni per capire qual \u00e8 realmente il piano di esecuzione?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/893c955bc1e2e15a6986690e516f72a9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Chi \u00e8 il colpevole? Cosa \u00e8 successo? Il database contiene un'ottimizzazione. E sembra essere ottimizzato per il caso generico. Di conseguenza, a un certo punto, si passa a un piano generico, che purtroppo potrebbe rivelarsi diverso. Potrebbe essere lo stesso, oppure diverso. E c'\u00e8 un certo valore soglia che porta a tale comportamento. <\/p>\n<p><\/p>\n<p>Cosa si pu\u00f2 fare al riguardo? Qui, ovviamente, \u00e8 pi\u00f9 difficile fare previsioni. C'\u00e8 una soluzione semplice che utilizziamo. \u00c8 +0, OFFSET 0. Sicuramente conoscete queste soluzioni. Prendiamo semplicemente e aggiungiamo \u00ab+0\u00bb alla query ed \u00e8 tutto a posto. Mostrer\u00f2 pi\u00f9 tardi. <\/p>\n<p><\/p>\n<p>E c'\u00e8 un altro modo: guardare i piani pi\u00f9 attentamente. Lo sviluppatore non deve solo scrivere la query, ma anche dire \u00abexplain analyze\u00bb sei volte. Se lo dice cinque volte, non funziona. <\/p>\n<p><\/p>\n<p>E c'\u00e8 anche una terza opzione: scrivere una lettera a pgsql-hackers. L'ho fatto, ma finora non \u00e8 chiaro se sia un bug o una funzionalit\u00e0.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 pensiamo se sia un bug o una funzionalit\u00e0, ripariamo. Prendiamo la nostra query e aggiungiamo \u00ab+0\u00bb. Va tutto bene. Due simboli e non \u00e8 nemmeno necessario pensare a come stanno le cose. Molto semplice. Abbiamo semplicemente vietato al database di utilizzare l'indice su questa colonna. Non abbiamo un indice sulla colonna \u00ab+0\u00bb e quindi il database non utilizza l'indice, tutto a posto. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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, bisogna farlo sei volte, se avete variabili collegate. Se non avete variabili collegate, procediamo cos\u00ec. E alla fine proprio questa query fallisce. Non \u00e8 difficile.<\/p>\n<p><\/p>\n<p>Sembra che non ci sia fine? Qui un bug, l\u00e0 un bug. In realt\u00e0, c'\u00e8 bug ovunque. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 \u042b e schema B con la tabella \u042b. La query deve selezionare i dati dalla tabella. Cosa succeder\u00e0? Avremo un errore. Tutto ci\u00f2 di cui abbiamo parlato finora ci sar\u00e0. La regola \u00e8: bug ovunque, avremo tutto quanto precedentemente elencato.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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: \u00abPerch\u00e9?\u00bb. Sembra che ci sia documentazione che, se abbiamo uno schema, c'\u00e8 una variabile \u00absearch_path\u00bb che indica dove cercare la tabella. Sembra che la variabile esista.<\/p>\n<p><\/p>\n<p>Qual \u00e8 il problema? Il problema \u00e8 che le dichiarazioni prepare del server non sospettano che qualcuno possa cambiare il search_path. Questo valore rimane come una 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 tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/f738301606d84d72bfa7cc5b568c0df8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Certo, dipende dalla versione su cui stai testando. Dipende da quanto siano diverse le tue tabelle. E la versione 9.1 eseguir\u00e0 semplicemente le vecchie query. Le nuove versioni possono individuare l'inganno e dire che hai un errore.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 deve mantenere lo stesso tipo di risultato<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Come si risolve questo? C'\u00e8 una ricetta semplice: non farlo. Non bisogna cambiare il search_path mentre l'applicazione \u00e8 in esecuzione. Se devi cambiare, \u00e8 meglio creare una nuova connessione.<\/p>\n<p><\/p>\n<p>Possiamo discuterne, cio\u00e8 aprire, discutere, aggiungere. Forse convinceremo anche gli sviluppatori del database che, nel caso in cui qualcuno cambi il valore, il database dovrebbe comunicarlo al cliente: 'Guarda, qui il tuo valore \u00e8 stato aggiornato. Forse devi resettare le statements, ricrearle?'. Attualmente, il database si comporta in modo riservato e non informa in alcun modo che da qualche parte le statements sono cambiate. <\/p>\n<p><\/p>\n<p>E sottolineo di nuovo: questo non \u00e8 tipico per Java. Vedremo la stessa cosa 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 tutto. 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 ancora i dati. Selezioniamo e 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>Noi, come raccomandato in tutti i libri, utilizziamo l'elaborazione a flusso. Cio\u00e8, apriamo il resultSet e leggiamo gradualmente i dati. Funzioner\u00e0? Non andr\u00e0 in OutOfMemory? Legger\u00e0 a poco a poco? Crediamo nel database, crediamo in Postgres. Non crediamo. Andremo in OutOfMemory? Chi \u00e8 mai andato in OutOfMemory? E chi \u00e8 riuscito a risolvere il problema dopo? Qualcuno ci \u00e8 riuscito. <\/p>\n<p><\/p>\n<p>Se hai un milione di righe, non puoi semplicemente selezionare. Devi sicuramente usare OFFSET\/LIMIT. Chi \u00e8 favorevole a questa opzione? E chi \u00e8 favorevole a giocare con l'autoCommit? <\/p>\n<p><\/p>\n<p>Qui, come al solito, l'opzione pi\u00f9 inaspettata si rivela essere quella giusta. E se per caso disattivi l'autoCommit, questo ti aiuter\u00e0. Perch\u00e9? Alla scienza non \u00e8 noto. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 clienti che si connettono al database Postgres selezionano i dati nella loro interezza. PgJDBC non \u00e8 un'eccezione e seleziona tutte le righe.<\/p>\n<p><\/p>\n<p>C'\u00e8 una variazione sulla questione di FetchSize, cio\u00e8 puoi a livello di singola statement dire che qui, per favore, seleziona i dati a blocchi di 10, 50. Ma questo non funziona finch\u00e9 non disattivi l'autoCommit. Disattiva l'autoCommit e inizia a funzionare. <\/p>\n<p><\/p>\n<p>Ma camminare nel codice e mettere setFetchSize ovunque \u00e8 scomodo. Per questo motivo abbiamo fatto una configurazione che imposta il valore predefinito per tutta la connessione.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/c9ac8e483dc6bfdbbc300972ee1ab1e0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ecco, l'abbiamo detto. Abbiamo configurato il parametro. E cosa abbiamo ottenuto? Se scegliamo in piccole quantit\u00e0, come per esempio 10 righe, abbiamo costi di overhead piuttosto elevati. Quindi dovremmo impostare questo valore attorno a cento. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/073e7a4af63688396eefe332ba8261d6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ideale sarebbe anche limitare in byte, ma la ricetta \u00e8 questa: impostiamo defaultRowFetchSize a pi\u00f9 di cento e ci godiamo il risultato. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e3078faf2078fcaec10d6d99872a9990.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Passiamo ora all'inserimento dei dati. L'inserimento \u00e8 pi\u00f9 semplice, ci sono diverse opzioni. Ad esempio, INSERT, VALUES. Questo \u00e8 un buon modo. Possiamo dire \"INSERT SELECT\". Nella pratica sono la stessa cosa. Non c'\u00e8 differenza in termini di prestazioni. <\/p>\n<p><\/p>\n<p>I libri dicono che \u00e8 necessario eseguire i Batch statement, i libri dicono che \u00e8 possibile eseguire comandi pi\u00f9 complessi con diverse parentesi. E in Postgres c'\u00e8 una funzione fantastica: possiamo fare COPY, cio\u00e8 farlo pi\u00f9 rapidamente. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4d281c9515bdf8de7fbf1e9922d192d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se misuriamo, possiamo fare di nuovo alcune scoperte interessanti. Come vogliamo che questo funzioni? Vogliamo evitare di analizzare e di eseguire comandi superflui. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7b2e4343c51dab3ddb6919bf0f117c4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nella pratica il TCP non ci permette di farlo. Se il client \u00e8 occupato a inviare una richiesta, il database tenta di inviarci le risposte ma non legge le richieste. Di conseguenza, il client aspetta 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 tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/17a062c30ddd83cb890775ec1722044f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>E quindi il client \u00e8 costretto a inviare periodicamente un pacchetto di sincronizzazione. Interazioni di rete superflue, perdita di tempo non necessaria.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 li aggiunge abbastanza spesso, circa ogni 200 righe, a seconda della dimensione delle righe e cos\u00ec via. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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, correggendo solo una riga, tutto pu\u00f2 accelerare di dieci volte. Questo succede. Perch\u00e9? Come al solito, una costante \u00e8 stata gi\u00e0 utilizzata da qualche parte. E il valore \"128\" significava \u2013 non utilizzare il batching.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 una buona cosa che questo non sia finito nella versione ufficiale. \u00c8 stato scoperto prima che iniziassero a rilasciare la versione. Tutti i valori che menziono si basano su versioni moderne. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28c82d9e11f6bdaf3cf62b49fc074d68.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Misuriamo. Misuriamo InsertBatch semplice. Misuriamo InsertBatch multiplo, cio\u00e8 la stessa cosa, ma con molti valori. Un trucco astuto. Non tutti lo sanno fare, ma \u00e8 un modo semplice, molto pi\u00f9 facile rispetto a COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2b0d8bbcc50f1293c9a8a1eb8eb70d2e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>E si pu\u00f2 farlo nelle strutture. Dichiarare il tipo predefinito User, passare un array e INSERT direttamente nella tabella. <\/p>\n<p><\/p>\n<p>Se apri il link: pgjdbc\/ubenchmsrk\/InsertBatch.java, troverai questo codice su GitHub. Puoi vedere specificamente quali query vengono generate l\u00ec. Non \u00e8 l'essenziale.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28dede211fe401286685ca62081453de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Abbiamo lanciato. 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 uguale a zero rispetto all'esecuzione singola. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4ea68f712bbdeadd35f5baa4ddaeff33.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Stiamo inserendo i dati. \u00c8 una tabella piuttosto semplice. Tre colonne. E cosa vediamo qui? Vediamo che tutte e tre queste opzioni sono pi\u00f9 o meno comparabili. E COPY, ovviamente, \u00e8 migliore.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. 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 inseriamo a pezzi. Quando dicevamo, un valore VALUES, due valore VALUES, tre valore VALUES o li abbiamo specificati 10 separati da virgola. Questo \u00e8 proprio ora in orizzontale. 1, 2, 4, 128. \u00c8 evidente che l'insert batch, che \u00e8 disegnato in blu, ne trae grande vantaggio. Cio\u00e8, quando inserisci uno alla volta, o anche quando inserisci quattro, migliora di due volte, semplicemente perch\u00e9 abbiamo messo un po' di pi\u00f9 nei VALUES. Meno operazioni EXECUTE.<\/p>\n<p><\/p>\n<p>Usare COPY su piccole dimensioni \u00e8 estremamente poco promettente. Non ho nemmeno disegnato i primi due. Vanno verso il cielo, cio\u00e8 queste cifre verdi per COPY.<\/p>\n<p><\/p>\n<p>COPY deve essere usato quando hai un volume di dati di almeno pi\u00f9 di cento righe. Le spese generali per aprire questa connessione sono elevate. E, onestamente, non ho approfondito in quella direzione. Ho ottimizzato il batch, ma non il COPY. <\/p>\n<p><\/p>\n<p>Cosa facciamo dopo? Misuriamo. Comprendiamo che dobbiamo usare o strutture o un raffinato batch che unisce pi\u00f9 valori. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL e JDBC: spremiamo tutto. Vladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e02fa2574e2d1b678382db15064610d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Cosa bisogna portare via dalla relazione di oggi?<\/p>\n<p><\/p>\n<ul>\n<li>PreparedStatement \u00e8 tutto per noi. Questo offre molto in termini di prestazioni. Porta una grande botte di catrame. <\/li>\n<li>E bisogna fare EXPLAIN ANALYZE 6 volte.<\/li>\n<li>E bisogna diluire OFFSET 0, e trucchi come +0 per modificare il restante percentuale dai nostri query problematici.<\/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.1.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.1.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 spremiamo ogni goccia. Vladimir Sitnikov | ProHoster","description":"Vi invito a prendere visione della trascrizione della relazione di inizio 2016 di Vladimir Sitnikov \"PostgreSQL e JDBC: spremiamo ogni goccia\"","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}]}}