{"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\/de\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","title":{"rendered":"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Ich lade Sie ein, sich mit der Zusammenfassung des Berichts von Wladimir Sitnikov aus Anfang 2016 \"PostgreSQL und JDBC: Alle S\u00e4fte herauspressen\" vertraut zu machen.<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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 und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/030666abda53aa388b1cb1d0c46a7524.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Guten Tag! Mein Name ist Wladimir Sitnikov. Ich arbeite seit 10 Jahren bei NetCracker. Und haupts\u00e4chlich besch\u00e4ftige ich mich mit der Leistung. Alles, was mit Java zu tun hat, alles, was mit SQL zu tun hat - das ist, was ich liebe. <\/p>\n<p><\/p>\n<p>Und heute werde ich dar\u00fcber sprechen, womit wir in der Firma konfrontiert wurden, als wir begannen, PostgreSQL als Datenbankserver zu verwenden. Und wir arbeiten haupts\u00e4chlich mit Java. Aber das, was ich heute erz\u00e4hlen werde, betrifft nicht nur Java. Wie die Praxis zeigt, tritt dies auch in anderen Sprachen auf. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b01a214d782c7e32797a6c6466457979.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir werden sprechen \u00fcber:<\/p>\n<p><\/p>\n<ul>\n<li>die Datenauswahl. <\/li>\n<li>Die Datenspeicherung. <\/li>\n<li>Und auch \u00fcber die Leistung. <\/li>\n<li>Und \u00fcber die Stolpersteine, die dort verborgen sind. <\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/57da0c6aa4bb62e1beb77160f5aee7e7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns mit einer einfachen Frage beginnen. Wir w\u00e4hlen eine Zeile aus der Tabelle nach dem Prim\u00e4rschl\u00fcssel aus. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/d5fcb1172aef402a87064498bf8a5218.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die Datenbank befindet sich auf dem gleichen Host. Und all das dauert 20 Millisekunden.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/103b7f9736b82a3313491b2f0d2a5824.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Diese 20 Millisekunden sind sehr viel. Wenn Sie 100 solcher Anfragen haben, verschwenden Sie Zeit pro Sekunde, um diese Anfragen abzuwickeln, d. h. wir verschwenden Zeit.<\/p>\n<p><\/p>\n<p>Das machen wir nicht gerne und schauen, was uns die Datenbank daf\u00fcr anbietet. Die Datenbank bietet uns zwei M\u00f6glichkeiten zur Ausf\u00fchrung von Abfragen an. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b467e86a0f8c4c32cea182fe27a28e6e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die erste M\u00f6glichkeit ist eine einfache Abfrage. Was ist daran gut? Dass wir sie nehmen und senden, und nichts weiter. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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>Die Datenbank hat auch eine erweiterte Abfrage, die trickreicher, aber funktionaler ist. Man kann die Anfrage zum Parsen, Ausf\u00fchren, Binden von Variablen usw. separat senden. <\/p>\n<p><\/p>\n<p>Super erweiterte Abfrage - das werden wir im aktuellen Bericht nicht behandeln. Vielleicht m\u00f6chten wir etwas von der Datenbank und es gibt eine Wunschliste, die in gewisser Weise formuliert ist, d. h. das ist, was wir wollen, aber jetzt und im kommenden Jahr nicht m\u00f6glich ist. Daher haben wir es einfach aufgeschrieben und werden die Hauptverantwortlichen konsultieren.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/19094729074fd28c5b9f236833a9a266.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Was wir tun k\u00f6nnen, sind die einfache Abfrage und die erweiterte Abfrage.<\/p>\n<p><\/p>\n<p>Was ist das Besondere an jedem Ansatz? <\/p>\n<p><\/p>\n<p>Die einfache Abfrage eignet sich gut f\u00fcr einmalige Ausf\u00fchrungen. Einmal ausgef\u00fchrt und vergessen. Das Problem ist, dass sie das bin\u00e4re Datenformat nicht unterst\u00fctzt, d. h. f\u00fcr einige Hochleistungsysteme eignet sie sich nicht.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/062c0e45cefd91ece331a306a7651bde.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Erweiterte Abfragen erm\u00f6glichen es, Zeit beim Parsen zu sparen. Das haben wir gemacht und angefangen zu verwenden. Das hat uns extrem geholfen. Es gibt nicht nur Einsparungen beim Parsen. Es gibt auch Einsparungen bei der Daten\u00fcbertragung. Daten im bin\u00e4ren Format zu \u00fcbertragen, ist viel effizienter. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/6df83a2f3736568668cf9d74de5d9758.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Kommen wir zur Praxis. So sieht eine typische Anwendung aus. Das kann Java usw. sein. <\/p>\n<p><\/p>\n<p>Wir haben ein Statement erstellt. Den Befehl ausgef\u00fchrt. Ein Close erstellt. Wo liegt hier der Fehler? Was ist das Problem? Es gibt keine Probleme. So steht es in allen B\u00fcchern. So muss man schreiben. Wenn Sie maximale Leistung m\u00f6chten, schreiben Sie so. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/6923293946dec45b3fd508235728ab95.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Aber die Praxis hat gezeigt, dass das nicht funktioniert. Warum? Weil wir die Methode \u201eclose\u201c haben. Und wenn wir es so machen, ist es aus Sicht der Datenbank wie die Arbeit eines Rauchers mit der Datenbank. Wir haben \u201ePARSE EXECUTE DEALLOCATE\u201c gesagt.<\/p>\n<p><\/p>\n<p>Warum diese \u00fcberfl\u00fcssigen Erstellungen und das Entladen von Statements? Die braucht niemand. Aber normalerweise ist es im PreparedStatement so, wenn wir sie schlie\u00dfen, schlie\u00dfen sie alles in der Datenbank. Das ist nicht das, was wir wollen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5a357a209e414024c437d042f600f251.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir wollen, dass wir wie gesunde Menschen mit der Datenbank arbeiten. Einmal haben wir unser Statement vorbereitet, dann f\u00fchren wir es viele Male aus. Tats\u00e4chlich viele Male \u2013 das ist einmal im Laufe der gesamten Lebensdauer der Anwendung geparst. Und f\u00fcr unterschiedliche REST verwenden wir die gleiche Statement-ID. Das ist unser Ziel. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/ac4aed702a624ad9f9addc4362daf760.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wie erreichen wir das? <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/0f101d890d1a2af8e99f8a3a8a06fd5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ganz einfach \u2013 man muss die Statements nicht schlie\u00dfen. Wir schreiben so: \u201eprepare\u201c \u201eexecute\u201c. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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 und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/724646bfc55b5e2b611b1584ca8ba6aa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wenn wir das ausf\u00fchren, ist klar, dass irgendwo etwas \u00fcberlaufen wird. Wenn das unklar ist, kann man es messen. Lassen Sie uns einen Benchmark schreiben, in dem wir eine so einfache Methode haben. Wir erstellen ein Statement. F\u00fchren es mit einer bestimmten Treiberversion aus und stellen fest, dass es ziemlich schnell mit dem Verlust des gesamten Speichers, den wir haben, abst\u00fcrzt. <\/p>\n<p><\/p>\n<p>Es ist klar, dass solche Fehler leicht zu beheben sind. Ich werde nicht dar\u00fcber sprechen. Aber ich kann sagen, dass es in der neuen Version viel schneller funktioniert. Die Methode ist sinnlos, aber dennoch. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b232a6205e708c70f52be0024bbe9980.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wie arbeitet man richtig? Was m\u00fcssen wir daf\u00fcr tun?<\/p>\n<p><\/p>\n<p>In der Realit\u00e4t schlie\u00dfen Anwendungen immer Statements. In allen B\u00fcchern steht, dass man sie schlie\u00dfen soll, sonst gibt es Speicherlecks. <\/p>\n<p><\/p>\n<p>Und PostgreSQL kann keine Abfragen cachen. Jede Sitzung muss diesen Cache selbst f\u00fcr sich erstellen. <\/p>\n<p><\/p>\n<p>Und Zeit f\u00fcr Parsing wollen wir auch nicht verschwenden. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/85b9574938aa6e6bf59c956e3728bdc3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wie gewohnt haben wir zwei M\u00f6glichkeiten. <\/p>\n<p><\/p>\n<p>Die erste Variante \u2013 wir nehmen und sagen, dass wir alles in PgSQL verpacken. Dort gibt es einen Cache. Er cached alles. Das wird gro\u00dfartig. Das haben wir uns angeschaut. Wir haben 100500 Anfragen. Es funktioniert nicht. Wir sind nicht einverstanden, Anfragen manuell in Prozeduren umzuwandeln. Nein, nein. <\/p>\n<p><\/p>\n<p>Wir haben eine zweite Variante \u2013 selbst etwas entwickeln. Wir \u00f6ffnen den Quellcode und fangen an zu programmieren. Wir programmieren und programmieren. Es stellte sich heraus, dass es gar nicht so kompliziert ist. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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>Das erschien im August 2015. Jetzt gibt es bereits eine modernere Version. Und alles l\u00e4uft gro\u00dfartig. Es funktioniert so gut, dass wir nichts in der Anwendung \u00e4ndern. Und wir haben sogar aufgeh\u00f6rt, in die Richtung PgSQL zu denken, das hei\u00dft, es hat uns v\u00f6llig gereicht, um alle Nebenkosten praktisch auf null zu senken. <\/p>\n<p><\/p>\n<p>Entsprechend werden Server-prepared statements beim f\u00fcnften Ausf\u00fchren aktiviert, um zu vermeiden, dass Speicher in der Datenbank f\u00fcr jede einmalige Anfrage verbraucht wird. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/8ee95e71f92187941989ad0c18ece0c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Man kann fragen \u2013 wo sind die Zahlen? Was bekommt ihr? Und hier kann ich keine Zahlen geben, weil jeder Anfrage ihre eigenen hat.<\/p>\n<p><\/p>\n<p>Unsere Anfragen waren so, dass wir bei OLTP-Anfragen ungef\u00e4hr 20 Millisekunden f\u00fcr das Parsen ben\u00f6tigt haben. Dort waren es 0,5 Millisekunden f\u00fcr die Ausf\u00fchrung, 20 Millisekunden f\u00fcr das Parsen. Anfrage \u2013 10 KiB Text, 170 Zeilen Plan. Das ist eine OLTP-Anfrage. Sie fragt 1, 5, 10 Zeilen ab, manchmal mehr. <\/p>\n<p><\/p>\n<p>Aber wir wollten \u00fcberhaupt keine 20 Millisekunden verschwenden. Wir haben es auf 0 reduziert. Alles l\u00e4uft gro\u00dfartig. <\/p>\n<p><\/p>\n<p>Was k\u00f6nnt ihr daraus mitnehmen? Wenn ihr Java habt, nehmt ihr die aktuelle Version des Treibers und seid gl\u00fccklich. <\/p>\n<p><\/p>\n<p>Wenn ihr eine andere Sprache habt, dann \u00fcberlegt \u2013 braucht ihr das vielleicht auch? Denn aus der Sicht der Zielsprache, wenn ihr PL 8 oder LibPQ habt, ist euch nicht offensichtlich, dass ihr Zeit nicht f\u00fcr die Ausf\u00fchrung, sondern f\u00fcr das Parsen aufwendet und das sollte \u00fcberpr\u00fcft werden. Wie? Alles kostenlos. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/fda6b1c1b126cb85c970e42335e1dc24.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mit Ausnahme der Fehler, einiger Besonderheiten. Und genau dar\u00fcber werden wir jetzt sprechen. Der gr\u00f6\u00dfte Teil wird \u00fcber industrielle Arch\u00e4ologie sein, dar\u00fcber, was wir gefunden haben, worauf wir gesto\u00dfen sind. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/9a3b383f83c5e227cd02952c5825b64f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wenn die Anfrage dynamisch generiert wird. Das kommt vor. Jemand verkettet Strings, und es entsteht eine SQL-Anfrage.<\/p>\n<p><\/p>\n<p>Warum ist das schlecht? Es ist schlecht, weil wir jedes Mal am Ende eine unterschiedliche Zeile erhalten.<\/p>\n<p><\/p>\n<p>Und bei dieser variierenden Zeile muss der hashCode erneut berechnet werden. Es ist tats\u00e4chlich eine CPU-Aufgabe \u2013 einen langen Anfrage-Text im vorhandenen hash zu finden, ist nicht so einfach. Daher die einfache Empfehlung \u2013 generieren Sie keine Anfragen. Speichern Sie sie in einer einzigen Variable. Und freuen Sie sich.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/bcd4f729204cecdb572a98f357bcc33f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Das n\u00e4chste Problem. Datentypen sind wichtig. Es gibt ORM, die sagen, dass es egal ist, welcher NULL-Wert, lassen Sie irgendeinen sein. Wenn es ein Int ist, sagen wir setInt. Und wenn NULL, dann soll es immer VARCHAR sein. Und was macht das schon f\u00fcr einen Unterschied am Ende? Die Datenbank wird es schon verstehen. Und so ein Bild funktioniert nicht. <\/p>\n<p><\/p>\n<p>In der Praxis ist es der Datenbank v\u00f6llig egal. <strong>Wenn Sie beim ersten Mal gesagt haben, dass es sich um eine Zahl handelt, und beim zweiten Mal gesagt haben, dass es VARCHAR ist, kann man Server-prepared statements nicht wiederverwenden. In diesem Fall muss unser statement neu erstellt werden.<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/552d9c2e25cf9abbdfef12c98d027740.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wenn Sie die gleiche Anfrage mehrmals ausf\u00fchren, achten Sie darauf, dass die Datentypen in der Spalte nicht durcheinandergeraten. Man muss auf NULL achten. Das ist ein h\u00e4ufiger Fehler, der aufgetreten ist, nachdem wir begonnen haben, PreparedStatements zu verwenden.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5d73c8f5dfa281c3bda962fc3e5edd84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Gut, eingeschaltet. Vielleicht haben wir einen Treiber verwendet. Und die Leistung ist gesunken. Alles wurde schlecht. <\/p>\n<p><\/p>\n<p>Wie kann das passieren? Ist das ein Bug oder ein Feature? Leider konnte ich nicht verstehen \u2013 ist es ein Bug oder ein Feature. Aber es gibt ein recht einfaches Szenario zur Reproduktion dieses Problems. Es hat uns v\u00f6llig unerwartet erwischt. Und es besteht darin, dass wir buchst\u00e4blich aus einer einzigen Tabelle ausw\u00e4hlen. Nat\u00fcrlich hatten wir mehr solcher Anfragen. Diese beinhalteten in der Regel zwei bis drei Tabellen, aber es gibt so ein Reproduktionsszenario. Nehmen Sie Ihre Datenbank in jeder Version und reproduzieren Sie es.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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>Der Sinn ist, dass wir zwei Spalten haben, von denen jede indiziert ist. In einer Spalte stehen eine Million Zeilen mit dem Wert NULL. In der anderen Spalte sind es nur 20 Zeilen. Wenn wir ohne gebundene Variablen ausf\u00fchren, funktioniert alles gut. <\/p>\n<p><\/p>\n<p>Wenn wir anfangen, mit gebundenen Variablen auszuf\u00fchren, d. h. wir setzen ein Zeichen \u201e?\u201c oder \u201e$1\u201c f\u00fcr unsere Anfrage, was erhalten wir schlie\u00dflich?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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>Erste Ausf\u00fchrung \u2013 wie gewohnt. Zweite \u2013 etwas schneller. Etwas wurde zwischengespeichert. Dritte-vierte-f\u00fcnfte. Dann zack \u2013 und so ist es. Und das Schlimmste ist, dass das beim sechsten Ausf\u00fchren geschieht. Wer h\u00e4tte gewusst, dass man genau sechs Ausf\u00fchrungen machen muss, um zu verstehen, welcher Plan dort wirklich verwendet wird?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/893c955bc1e2e15a6986690e516f72a9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wer ist schuld? Was ist passiert? Die Datenbank enth\u00e4lt eine Optimierung. Und sie ist irgendwie f\u00fcr den generischen Fall optimiert. Dementsprechend wechselt sie ab einer bestimmten Anzahl an Durchl\u00e4ufen auf den generischen Plan, der leider anders ausfallen kann. Er kann gleich sein, aber auch anders. Und es gibt einen bestimmten Schwellenwert, der zu diesem Verhalten f\u00fchrt. <\/p>\n<p><\/p>\n<p>Was kann man damit machen? Hier ist es nat\u00fcrlich schwieriger, etwas zu vermuten. Es gibt eine einfache L\u00f6sung, die wir verwenden. Das ist +0, OFFSET 0. Sicher kennen Sie solche L\u00f6sungen. Wir f\u00fcgen einfach \u201e+0\u201c zu der Anfrage hinzu und alles ist gut. Ich zeige es sp\u00e4ter. <\/p>\n<p><\/p>\n<p>Und es gibt noch eine M\u00f6glichkeit \u2013 die Pl\u00e4ne genauer zu betrachten. Der Entwickler muss nicht nur die Anfrage schreiben, sondern auch 6 Mal \u201eexplain analyze\u201c sagen. Wenn es nur 5 Mal ist, funktioniert es nicht. <\/p>\n<p><\/p>\n<p>Und es gibt noch eine dritte M\u00f6glichkeit \u2013 einen Brief an pgsql-hackers zu schreiben. Ich habe geschrieben, aber es ist bisher unklar, ob es ein Bug oder ein Feature ist.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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>W\u00e4hrend wir dar\u00fcber nachdenken, ob es ein Bug oder ein Feature ist, lassen Sie uns das Problem beheben. Nehmen wir unsere Anfrage und f\u00fcgen wir \u201e+0\u201c hinzu. Alles ist gut. Zwei Zeichen und man muss nicht dar\u00fcber nachdenken, wie das dort l\u00e4uft. Sehr einfach. Wir haben der Datenbank einfach verboten, den Index dieser Spalte zu verwenden. Wir haben keinen Index f\u00fcr die Spalte \u201e+0\u201c, also verwendet die Datenbank keinen Index, alles gut. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5261d80d084786a15b9764f6367014cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Das ist das Regelwerk der 6 Erkl\u00e4rungen. In den aktuellen Versionen muss man 6 Mal erkl\u00e4ren, wenn Sie verwandte Variablen haben. Wenn Sie keine verwandten Variablen haben, tun wir es einfach so. Und letztendlich schl\u00e4gt genau diese Anfrage fehl. Es ist nichts kompliziertes.<\/p>\n<p><\/p>\n<p>Es schien, wie lange kann das dauern? Hier ein Bug, dort ein Bug. Tats\u00e4chlich gibt es \u00fcberall Bugs. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/674dc7e7b4624baa5c79eee9e1e89db0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns noch einmal schauen. Zum Beispiel haben wir zwei Schemas. Schema A mit Tabelle Y und Schema B mit Tabelle Y. Die Anfrage \u2013 w\u00e4hlen Sie Daten aus der Tabelle aus. Was haben wir dann? Wir haben einen Fehler. Wir haben alles, was oben erw\u00e4hnt wurde. Die Regel ist \u2013 \u00fcberall Bugs, wir haben alles, was oben erw\u00e4hnt wurde.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/dd6f544bb9e930c39e36523c9afe6003.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jetzt die Frage: \u201eWarum?\u201c. Es schien, dass es eine Dokumentation gibt, dass es, wenn wir ein Schema haben, eine Variable \u201esearch_path\u201c gibt, die angibt, wo die Tabelle gesucht werden soll. Es scheint, dass die Variable vorhanden ist.<\/p>\n<p><\/p>\n<p>Was ist das Problem? Das Problem ist, dass server-prepared statements nicht ahnen, dass search_path von jemandem ge\u00e4ndert werden kann. Dieser Wert bleibt wie eine Konstante f\u00fcr die Datenbank. Und einige Teile k\u00f6nnten die neuen Werte nicht \u00fcbernehmen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/f738301606d84d72bfa7cc5b568c0df8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nat\u00fcrlich h\u00e4ngt das von der Version ab, auf der Sie testen. Es h\u00e4ngt davon ab, wie stark Ihre Tabellen unterschiedlich sind. Und Version 9.1 f\u00fchrt einfach die alten Abfragen aus. Neuere Versionen k\u00f6nnen einen Trick erkennen und sagen, dass Sie einen Fehler haben.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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 \/>\nDer zwischengespeicherte Plan darf den Ergebnistyp nicht \u00e4ndern<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Wie behandelt man das? Es gibt ein einfaches Rezept \u2013 machen Sie es nicht so. \u00c4ndern Sie den search_path nicht w\u00e4hrend der Ausf\u00fchrung der Anwendung. Wenn Sie ihn \u00e4ndern, sollten Sie besser eine neue Verbindung herstellen.<\/p>\n<p><\/p>\n<p>Wir k\u00f6nnen diskutieren, das hei\u00dft, \u00f6ffnen, diskutieren, erg\u00e4nzen. Vielleicht \u00fcberzeugen wir die Datenbankentwickler, dass die Datenbank dem Client sagen sollte: \u201eSchauen Sie, hier hat sich ein Wert ge\u00e4ndert. Vielleicht sollten Sie die Statements zur\u00fccksetzen oder neu erstellen?\u201c Zurzeit verh\u00e4lt sich die Datenbank heimlich und informiert nicht dar\u00fcber, dass sich irgendwo intern die Statements ge\u00e4ndert haben. <\/p>\n<p><\/p>\n<p>Und ich m\u00f6chte nochmals betonen \u2013 das ist nicht typisch f\u00fcr Java. Das gleiche werden wir in PL\/pgSQL eins zu eins sehen. Aber dort wird es reproduzierbar sein.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/a4e7b201d0c0aa4a0092d2b6ff152df7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns noch einmal versuchen, Daten auszuw\u00e4hlen. Wir w\u00e4hlen, w\u00e4hlen. Wir haben eine Tabelle mit einer Million Zeilen. Jede Zeile hat ein Kilobyte. Etwa ein Gigabyte an Daten. Und wir haben 128 Megabyte Arbeitsspeicher in der Java-Maschine. <\/p>\n<p><\/p>\n<p>Wie in allen B\u00fcchern empfohlen, verwenden wir die Streaming-Verarbeitung. Das hei\u00dft, wir \u00f6ffnen das resultSet und lesen die Daten nach und nach. Wird das funktionieren? Wird es nicht zu einem Speicher\u00fcberlauf kommen? Wird es wenig lesen? Lassen Sie uns an die Datenbank glauben, an Postgres glauben. Glauben wir nicht. Werden wir OutOfMemory-Fehler bekommen? Wer hat OutOfMemory-Fehler bekommen? Und wer konnte das danach reparieren? Jemand konnte es reparieren. <\/p>\n<p><\/p>\n<p>Wenn Sie eine Million Zeilen haben, k\u00f6nnen Sie nicht einfach so ausw\u00e4hlen. Sie m\u00fcssen immer OFFSET\/LIMIT verwenden. Wer ist f\u00fcr diese Option? Und wer sagt, dass man mit autoCommit spielen sollte? <\/p>\n<p><\/p>\n<p>Hier ist, wie \u00fcblich, die unerwartetste Option die richtige. Und wenn Sie pl\u00f6tzlich autoCommit ausschalten, wird es helfen. Warum so? Die Wissenschaft wei\u00df es nicht. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2e5d9d2a8450a9069155de86a8ec387a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Aber standardm\u00e4\u00dfig w\u00e4hlen alle Clients, die sich mit der Postgres-Datenbank verbinden, die Daten vollst\u00e4ndig aus. PgJDBC ist in dieser Hinsicht keine Ausnahme und w\u00e4hlt alle Zeilen aus.<\/p>\n<p><\/p>\n<p>Es gibt eine Variation zum Thema FetchSize, das hei\u00dft, Sie k\u00f6nnen auf Ebene eines einzelnen Statements sagen, dass hier bitte die Daten in Gruppen von 10, 50 ausgew\u00e4hlt werden sollen. Aber das funktioniert nicht, solange Sie autoCommit nicht ausschalten. Wenn Sie autoCommit ausschalten \u2013 f\u00e4ngt es an zu funktionieren. <\/p>\n<p><\/p>\n<p>Es ist unpraktisch, im Code \u00fcberall setFetchSize zu setzen. Deshalb haben wir eine Einstellung vorgenommen, die den Standardwert f\u00fcr die gesamte Verbindung festlegt.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/c9ac8e483dc6bfdbbc300972ee1ab1e0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir haben das gesagt. Den Parameter eingestellt. Und was haben wir nun? Wenn wir in kleinen Mengen ausw\u00e4hlen, zum Beispiel 10 Zeilen, haben wir ziemlich hohe Overheadkosten. Daher sollten wir diesen Wert auf etwa 100 setzen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/073e7a4af63688396eefe332ba8261d6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Idealerweise sollten wir nat\u00fcrlich auch lernen, in Bytes zu limitieren, aber die Rezeptur ist folgende: wir setzen defaultRowFetchSize auf \u00fcber 100 und freuen uns. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e3078faf2078fcaec10d6d99872a9990.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns zur Dateninstituierung \u00fcbergehen. Die Einf\u00fcgung \u2013 einfach, es gibt verschiedene Varianten. Zum Beispiel, INSERT, VALUES. Das ist eine gute Option. Man kann auch \u201aINSERT SELECT\u2018 sagen. In der Praxis ist das dasselbe. Es gibt keinen Unterschied in der Leistungsf\u00e4higkeit. <\/p>\n<p><\/p>\n<p>B\u00fccher sagen, dass man Batch-Statements ausf\u00fchren sollte, B\u00fccher sagen, dass man komplexere Befehle mit mehreren Klammern ausf\u00fchren kann. Und in Postgres gibt es eine wunderbare Funktion \u2013 man kann COPY machen, d.h. es schneller erledigen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4d281c9515bdf8de7fbf1e9922d192d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wenn man es misst, kann man wieder einige interessante Entdeckungen machen. Wie wollen wir, dass das funktioniert? Wir wollen nicht parsen und keine \u00fcberfl\u00fcssigen Befehle ausf\u00fchren. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/7b2e4343c51dab3ddb6919bf0f117c4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>In der Praxis erlaubt uns TCP so etwas nicht. Wenn der Client mit dem Senden einer Anfrage besch\u00e4ftigt ist, liest die Datenbank in dem Versuch, uns Antworten zu senden, die Anfragen nicht. Am Ende wartet der Client auf die Datenbank, w\u00e4hrend diese auf den Client wartet, bis er die Antwort liest. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/17a062c30ddd83cb890775ec1722044f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Und deshalb muss der Client regelm\u00e4\u00dfig ein Synchronisationspaket senden. \u00dcberfl\u00fcssige Netzinteraktionen, \u00fcberfl\u00fcssiger Zeitverlust.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/ae7a4f32e2949c1d0550da051937de02.jpg\" style=\"display:block;margin: 0 auto;\" \/>Je mehr wir sie hinzuf\u00fcgen, desto schlechter wird es. Der Treiber ist ziemlich pessimistisch und f\u00fcgt sie ziemlich h\u00e4ufig hinzu, etwa alle 200 Zeilen, abh\u00e4ngig von der Gr\u00f6\u00dfe der Zeilen usw. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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>Manchmal korrigiert man nur eine Zeile und alles wird zehnmal schneller. Solche Dinge passieren. Warum? Wie gewohnt wurde irgendwo bereits eine solche Konstante verwendet. Und der Wert \u201e128\u201c bedeutete \u2013 kein Batching zu verwenden.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir 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-Mikrobearbeitungsumgebung<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Es ist gut, dass dies nicht in die offizielle Version gelangte. Wir entdeckten es, bevor wir mit der Ver\u00f6ffentlichung des Releases begannen. Alle Werte, die ich nenne, basieren auf modernen Versionen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28c82d9e11f6bdaf3cf62b49fc074d68.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns messen. Wir messen das einfache InsertBatch. Wir messen das mehrfache InsertBatch, d.h. dasselbe, aber mit vielen values. Ein raffinierter Trick. Nicht jeder kann das, aber es ist ein einfacher Weg, viel einfacher als COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/72f7cf6c3b9d9175d3e5a6d5410fe209.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Man kann COPY machen.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2b0d8bbcc50f1293c9a8a1eb8eb70d2e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Und man kann das auf Strukturen machen. User Default Type deklarieren, ein Array \u00fcbergeben und direkt in die Tabelle INSERT. <\/p>\n<p><\/p>\n<p>Wenn Sie den Link \u00f6ffnen: pgjdbc\/ubenchmsrk\/InsertBatch.java, dann finden Sie diesen Code auf GitHub. Sie k\u00f6nnen sehen, welche Abfragen dort generiert werden. Das ist nicht so wichtig.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28dede211fe401286685ca62081453de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir haben gestartet. Und das Erste, was wir verstanden haben, ist, dass man Batch nicht weglassen sollte \u2013 das geht einfach nicht. Alle Batch-Optionen sind gleich Null, d. h. die Ausf\u00fchrungszeit ist praktisch Null im Vergleich zu einer einmaligen Ausf\u00fchrung. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4ea68f712bbdeadd35f5baa4ddaeff33.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir f\u00fcgen Daten ein. Es ist eine ziemlich einfache Tabelle. Drei Spalten. Und was sehen wir hier? Wir sehen, dass all diese drei Optionen ungef\u00e4hr vergleichbar sind. Und COPY ist nat\u00fcrlich besser.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2686f1d7eea347a839ac23aef2a27e53.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Das ist, wenn wir st\u00fcckchenweise einf\u00fcgen. Wenn wir gesagt haben, ein Wert mit VALUES, zwei Werte mit VALUES, drei Werte mit VALUES oder wir haben dort 10 mit Kommas angegeben. Das ist jetzt quer. 1, 2, 4, 128. Man sieht, dass Batch Insert, das in Blau dargestellt ist, dadurch erheblich erleichtert wird. D. h. wenn Sie eins einzuf\u00fcgen oder sogar vier einzuf\u00fcgen, wird es doppelt so gut, einfach weil wir in VALUES etwas mehr reingepackt haben. Weniger EXECUTE-Operationen.<\/p>\n<p><\/p>\n<p>COPY f\u00fcr kleine Datenmengen zu nutzen, ist extrem unvorteilhaft. Ich habe bei den ersten beiden nicht einmal gezeichnet. Sie gehen in die H\u00f6he, d. h. diese gr\u00fcnen Zahlen f\u00fcr COPY.<\/p>\n<p><\/p>\n<p>COPY sollte verwendet werden, wenn Sie mindestens mehr als hundert Zeilen an Daten haben. Die Nebenkosten f\u00fcr das \u00d6ffnen dieser Verbindung sind hoch. Und ehrlich gesagt, ich habe in diese Richtung nicht weiter gegraben. Batch habe ich optimiert, COPY nicht. <\/p>\n<p><\/p>\n<p>Was machen wir als N\u00e4chstes? Wir haben gemessen. Wir verstehen, dass wir entweder Strukturen oder eine raffinierte Batch-Methode verwenden m\u00fcssen, die mehrere Werte kombiniert. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC, wir sch\u00f6pfen alles aus. Wladimir Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e02fa2574e2d1b678382db15064610d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Was m\u00fcssen wir aus dem heutigen Vortrag mitnehmen?<\/p>\n<p><\/p>\n<ul>\n<li>PreparedStatement \u2013 das ist unser Allesk\u00f6nner. Es bringt viel f\u00fcr die Leistung. Es bringt ein gro\u00dfes Fass voll Nachteile. <\/li>\n<li>Und man muss EXPLAIN ANALYZE 6 Mal durchf\u00fchren.<\/li>\n<li>Und man muss OFFSET 0 auflockern und mit Tricks wie +0 arbeiten, um den restlichen Prozentsatz unserer problematischen Abfragen zu verbessern.<\/li>\n<\/ul>\n<p>Quelle: <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\/de\/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=\"de_DE\" \/>\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\/de\/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 und JDBC \u2013 wir pressen jeden Tropfen heraus. Wladimir Sitnikov | ProHoster","description":"Ich lade Sie ein, sich mit der Entschl\u00fcsselung des Berichts von Anfang 2016 von Wladimir Sitnikov \"PostgreSQL und JDBC - wir sch\u00f6pfen alles aus\" vertraut zu machen.","canonical_url":"https:\/\/prohoster.info\/de\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"de_DE","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\/de\/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\/de\/wp-json\/wp\/v2\/posts\/79893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/comments?post=79893"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/posts\/79893\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media\/79894"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/media?parent=79893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/categories?post=79893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/de\/wp-json\/wp\/v2\/tags?post=79893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}