{"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 alle M\u00f6glichkeiten aus. Vladislav Sitnikov","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>\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;<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 alle M\u00f6glichkeiten aus. Vladislav 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 besch\u00e4ftige mich haupts\u00e4chlich mit Leistung. Alles, was mit Java und SQL zu tun hat, ist meine Leidenschaft. <\/p>\n<p><\/p>\n<p>Heute werde ich dar\u00fcber sprechen, auf welche Herausforderungen wir gesto\u00dfen sind, als wir PostgreSQL als Datenbankserver einsetzten. Wir arbeiten haupts\u00e4chlich mit Java, aber das, was ich heute bespreche, betrifft nicht nur Java. Die Praxis hat gezeigt, dass dies auch in anderen Programmiersprachen auftritt. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b01a214d782c7e32797a6c6466457979.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir werden Folgendes besprechen:<\/p>\n<p><\/p>\n<ul>\n<li>Datenabfragen. <\/li>\n<li>Datenspeicherung. <\/li>\n<li>Und auch \u00fcber die Leistung. <\/li>\n<li>Und die versteckten Fallen, die dabei lauern. <\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 einer Tabelle nach dem Prim\u00e4rschl\u00fcssel aus. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 demselben Host. Und all das ben\u00f6tigt 20 Millisekunden.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 ziemlich viel. Wenn Sie 100 solcher Anfragen haben, verschwenden Sie eine Sekunde nur damit, um diese Anfragen zu verarbeiten.<\/p>\n<p><\/p>\n<p>Wir machen das nicht gerne und schauen, was uns die Datenbank daf\u00fcr anbietet. Die Datenbank bietet uns zwei Optionen zur Ausf\u00fchrung von Anfragen an. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/b467e86a0f8c4c32cea182fe27a28e6e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die erste Option ist eine einfache Anfrage. Was ist daran gut? Dass wir sie einfach \u00fcbernehmen und absenden k\u00f6nnen, ohne mehr zu tun. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 Anfrage, die cleverer, aber funktioneller ist. Man kann Anfragen f\u00fcr Parsing, Ausf\u00fchrung, Verkn\u00fcpfung von Variablen usw. separat senden. <\/p>\n<p><\/p>\n<p>Super erweiterte Anfrage \u2013 das werden wir im aktuellen Bericht nicht abdecken. Vielleicht haben wir bestimmte W\u00fcnsche an die Datenbank, und es gibt eine Wunschliste, die in irgendeiner Form erstellt wurde, d.h. das sind W\u00fcnsche, die wir haben, die jedoch aktuell und im n\u00e4chsten Jahr nicht m\u00f6glich sind. Daher haben wir sie einfach aufgeschrieben und werden die Hauptverantwortlichen darum bitten.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 einfache Anfragen und erweiterte Anfragen.<\/p>\n<p><\/p>\n<p>Was ist das Besondere an jedem Ansatz? <\/p>\n<p><\/p>\n<p>Einfache Anfragen sind gut f\u00fcr eine einmalige Ausf\u00fchrung. Einmal ausgef\u00fchrt und vergessen. Das Problem ist, dass sie kein bin\u00e4res Datenformat unterst\u00fctzen, das hei\u00dft, f\u00fcr hochleistungsf\u00e4hige Systeme sind sie nicht geeignet.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/062c0e45cefd91ece331a306a7651bde.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Die erweiterte Abfrage erm\u00f6glicht eine Zeitersparnis beim Parsen. Das haben wir umgesetzt und begonnen, es zu verwenden. Es hat uns enorm geholfen. Dabei geht es nicht nur um Einsparungen beim Parsen, sondern auch um die Reduzierung des Datenverkehrs. Daten im Bin\u00e4rformat zu \u00fcbertragen ist viel effizienter. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 der Fehler? Was ist das Problem? Es gibt keine Probleme. So steht es in allen B\u00fcchern. So sollte man schreiben. Wenn Sie maximale Leistung erzielen wollen, schreiben Sie es so. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 das so machen, sieht es aus Sicht der Datenbank so aus \u2013 das ist wie die Arbeit eines Rauchers mit der Datenbank. Wir haben \u201ePARSE EXECUTE DEALLOCATE\u201c gesagt.<\/p>\n<p><\/p>\n<p>Warum diese unn\u00f6tigen Erstellungen und das Entladen der Statements? Sie sind niemandem von Nutzen. Aber normalerweise geschieht das bei PreparedStatements, wenn wir sie schlie\u00dfen, wird alles in der Datenbank geschlossen. Das ist nicht das, was wir wollen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5a357a209e414024c437d042f600f251.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir wollen, wie gesunde Menschen, mit der Datenbank arbeiten. Einmal haben wir unser statement vorbereitet, und dann f\u00fchren wir es viele Male aus. Tats\u00e4chlich ist 'viele Male' in diesem Fall einmal f\u00fcr die gesamte Lebensdauer der Anwendung, die geparst wurde. Bei verschiedenen REST-Calls 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 alle M\u00f6glichkeiten aus. Vladislav 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 alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/0f101d890d1a2af8e99f8a3a8a06fd5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ganz einfach \u2013 wir schlie\u00dfen die statements nicht. Wir schreiben einfach: \u201eprepare\u201c \u201eexecute\u201c. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/724646bfc55b5e2b611b1584ca8ba6aa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wenn wir so etwas ausf\u00fchren, ist klar, dass irgendwo etwas \u00fcberlaufen wird. Sollte das nicht klar sein, k\u00f6nnen wir es messen. Lassen Sie uns einen Benchmark schreiben, der so eine einfache Methode enth\u00e4lt. Wir erstellen ein statement, f\u00fchren es auf einer bestimmten Treiberversion aus und stellen fest, dass es ziemlich schnell mit dem Verlust des gesamten Speichers, den wir hatten, abst\u00fcrzt. <\/p>\n<p><\/p>\n<p>Es ist klar, dass solche Fehler leicht behoben werden k\u00f6nnen. Dar\u00fcber werde ich nicht sprechen. Aber ich werde sagen, dass es in der neuen Version viel schneller funktioniert. Die Methode ist nicht optimal, aber trotzdem. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 die statements. In allen B\u00fcchern steht, dass sie geschlossen werden sollten, sonst tritt ein Speicherleck auf. <\/p>\n<p><\/p>\n<p>Und PostgreSQL kann keine Abfragen cachen. Jede Sitzung muss ihren eigenen Cache erstellen. <\/p>\n<p><\/p>\n<p>Auch wir m\u00f6chten keine Zeit mit dem Parsen verschwenden. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 Optionen. <\/p>\n<p><\/p>\n<p>Die erste Option ist, dass wir einfach sagen: Lassen Sie uns alles in PgSQL packen. Das hat einen Cache, der alles speichert. Das wird gro\u00dfartig werden. Allerdings haben wir uns das angesehen. Bei 100.500 Anfragen funktioniert das nicht. Wir sind nicht einverstanden, die Anfragen manuell in Prozeduren umzuwandeln. Nein, nein. <\/p>\n<p><\/p>\n<p>Die zweite Option ist, dass wir es selbst entwickeln. Wir \u00f6ffnen den Quellcode und beginnen mit den Anpassungen. Wir arbeiten daran. Es stellte sich heraus, dass es nicht so kompliziert ist. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 begann im August 2015. Mittlerweile gibt es eine modernere Version. Und es funktioniert so gut, dass wir im Anwendungsbereich keine \u00c4nderungen vornehmen. Wir haben sogar aufgeh\u00f6rt, \u00fcber PgSQL nachzudenken; das hat uns vollkommen gereicht, um alle Nebenkosten praktisch auf null zu reduzieren. <\/p>\n<p><\/p>\n<p>Dementsprechend wird das Server-prepared Statement beim f\u00fcnften Durchlauf aktiviert, um den Speicher in der Datenbank f\u00fcr jede einmalige Anfrage zu schonen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/8ee95e71f92187941989ad0c18ece0c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Man k\u00f6nnte fragen: Wo sind die Zahlen? Was bekommen Sie? Dabei kann ich hier keine Zahlen liefern, da jede Anfrage ihre eigenen Werte hat.<\/p>\n<p><\/p>\n<p>Wir hatten Anfragen, bei denen wir f\u00fcr OLTP-Anfragen etwa 20 Millisekunden f\u00fcr das Parsen aufwendeten. Es gab 0,5 Millisekunden f\u00fcr die Ausf\u00fchrung und 20 Millisekunden f\u00fcr das Parsen. Die Anfrage bestand aus 10 KiB Text und 170 Zeilen des Plans. Das ist eine OLTP-Anfrage. Sie fragt 1, 5, 10 Zeilen an, manchmal mehr. <\/p>\n<p><\/p>\n<p>Aber wir wollten \u00fcberhaupt keine 20 Millisekunden verschwenden. Wir haben es auf 0 gesenkt. Alles lief gro\u00dfartig. <\/p>\n<p><\/p>\n<p>Was k\u00f6nnen Sie daraus lernen? Wenn Sie Java verwenden, nehmen Sie die moderne Version des Treibers und freuen sich. <\/p>\n<p><\/p>\n<p>Wenn Sie eine andere Sprache haben, \u00fcberlegen Sie, ob das auch f\u00fcr Sie relevant sein k\u00f6nnte. Denn aus der Perspektive der Zielsprache, zum Beispiel wenn Sie PL 8 oder LibPQ verwenden, ist Ihnen m\u00f6glicherweise nicht klar, dass Sie Zeit f\u00fcr das Parsen und nicht f\u00fcr die Ausf\u00fchrung verschwenden, 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 alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/fda6b1c1b126cb85c970e42335e1dc24.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Es sei denn, es gibt Fehler oder besondere Eigenschaften. Dar\u00fcber werden wir jetzt sprechen. Der Gro\u00dfteil wird sich mit industrieller Arch\u00e4ologie befassen, mit dem, was wir entdeckt haben und wor\u00fcber wir gestolpert sind. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 f\u00fcgt die Zeilen zusammen und es entsteht eine SQL-Anfrage.<\/p>\n<p><\/p>\n<p>Was ist daran schlecht? Es ist schlecht, weil wir jedes Mal eine andere Zeile erhalten.<\/p>\n<p><\/p>\n<p>Und bei dieser vielf\u00e4ltigen Zeile muss der hashCode neu berechnet werden. Das ist wirklich eine CPU-intensive Aufgabe \u2013 es ist nicht einfach, in einem bestehenden Hash einen langen Anfrage-Text zu finden. Daher ist die einfache Empfehlung: Generieren Sie keine Anfragen. Bewahren Sie sie in einer einzigen Variable auf. Und freuen Sie sich dar\u00fcber.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 ORMs, die behaupten, es sei egal, welcher NULL-Typ verwendet wird, irgendein NULL reicht aus. Wenn es ein Int ist, verwenden wir setInt. Und wenn es NULL ist, dann sei es immer VARCHAR. Und was spielt am Ende schon f\u00fcr einen Unterschied, welcher NULL-Typ dort steht? Die Datenbank wird alles verstehen. <\/p>\n<p><\/p>\n<p>In der Praxis ist es der Datenbank jedoch 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, k\u00f6nnen die Server-prepared statements nicht wiederverwendet werden. In einem solchen Fall m\u00fcssen wir unser Statement neu erstellen.<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/552d9c2e25cf9abbdfef12c98d027740.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wenn Sie dieselbe Anfrage mehrfach ausf\u00fchren, stellen Sie sicher, dass die Datentypen in der Spalte nicht durcheinander geraten. Achten Sie auf NULL. Das ist ein h\u00e4ufiges Problem, das wir hatten, nachdem wir mit PreparedStatements angefangen haben.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5d73c8f5dfa281c3bda962fc3e5edd84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Gut, wir haben es aktiviert. Vielleicht haben wir einen Treiber verwendet. Und die Leistung ist gefallen. Alles wurde schlecht. <\/p>\n<p><\/p>\n<p>Wie kann das sein? Ist das ein Bug oder ein Feature? Es war leider nicht m\u00f6glich zu verstehen, ob es sich um einen Bug oder ein Feature handelt. Aber es gibt ein ganz einfaches Szenario zur Reproduktion dieses Problems. Es hat uns v\u00f6llig \u00fcberraschend erwischt. Es bezieht sich auf die Abfrage aus buchst\u00e4blich einer Tabelle. Nat\u00fcrlich hatten wir mehr solcher Abfragen. Diese umfassten in der Regel zwei bis drei Tabellen, aber es gibt auch dieses Szenario zur Reproduktion. Nehmen Sie Ihre beliebige Version der Datenbank und reproduzieren Sie es.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 Kern der Sache ist, dass wir zwei Spalten haben, von denen jede indiziert ist. In einer Spalte gibt es eine Million Zeilen mit dem Wert NULL. In der anderen Spalte sind es nur 20 Zeilen. Wenn wir ohne verkn\u00fcpfte Variablen ausf\u00fchren, funktioniert alles gut. <\/p>\n<p><\/p>\n<p>Wenn wir beginnen, mit verkn\u00fcpften Variablen auszuf\u00fchren, d. h. wir f\u00fchren das Zeichen \u201e?\u201c oder \u201e$1\u201c f\u00fcr unsere Abfrage aus, welches Ergebnis erhalten wir letztendlich?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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>Die erste Ausf\u00fchrung \u2013 wie es sein sollte. Die zweite \u2013 etwas schneller. Etwas wurde zwischengespeichert. Die dritte, vierte, f\u00fcnfte. Dann kommt der Knack \u2013 und so passiert es. Und das Schlimmste ist, dass dies bei der sechsten Ausf\u00fchrung geschieht. Wer h\u00e4tte gedacht, dass man genau sechs Ausf\u00fchrungen machen muss, um den tats\u00e4chlichen Ausf\u00fchrungsplan zu verstehen?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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, die f\u00fcr einen generischen Fall optimiert ist. Entsprechend wechselt sie ab einem bestimmten Punkt auf einen generischen Plan, der leider anders sein kann. Er kann gleich sein oder auch anders. Es gibt einen bestimmten Schwellenwert, der zu diesem Verhalten f\u00fchrt. <\/p>\n<p><\/p>\n<p>Was kann man dagegen tun? Hier ist es nat\u00fcrlich schwieriger, Vermutungen anzustellen. Es gibt eine einfache L\u00f6sung, die wir verwenden: +0, OFFSET 0. Sicherlich kennen Sie solche L\u00f6sungen. Wir f\u00fcgen einfach \u00ab+0\u00bb zur Anfrage hinzu, und alles ist gut. Das zeige ich sp\u00e4ter. <\/p>\n<p><\/p>\n<p>Eine weitere M\u00f6glichkeit ist, die Pl\u00e4ne genauer zu betrachten. Der Entwickler sollte nicht nur die Anfrage schreiben, sondern auch sechsmal \u00abexplain analyze\u00bb sagen. Wenn es nur f\u00fcnf Mal ist, passt 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 das getan, aber es ist noch 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 alle M\u00f6glichkeiten aus. Vladislav 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 ein \u201e+0\u201c hinzu. Alles gut. Zwei Zeichen und wir m\u00fcssen uns nicht einmal \u00fcberlegen, wie es funktioniert. Es ist ganz einfach. Wir haben der Datenbank einfach untersagt, einen Index f\u00fcr diese Spalte zu verwenden. Wir haben keinen Index f\u00fcr die Spalte \u201e+0\u201c, und die Datenbank verwendet keinen Index, alles ist in Ordnung. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/5261d80d084786a15b9764f6367014cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Das ist die Regel der sechs Erkl\u00e4rungen. In den aktuellen Versionen m\u00fcssen Sie es sechs Mal machen, wenn Sie verbundene Variablen haben. Wenn Sie keine verbundenen Variablen haben, machen wir es so. Und letztendlich schl\u00e4gt genau diese Anfrage fehl. Es ist kein kompliziertes Problem.<\/p>\n<p><\/p>\n<p>Es scheint, als k\u00f6nnte es nicht schlimmer werden. 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 alle M\u00f6glichkeiten aus. Vladislav 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. Nehmen wir an, wir haben zwei Schemata. Schema A mit Tabelle Y und Schema B mit Tabelle Y. Die Anfrage lautet: Daten aus der Tabelle ausw\u00e4hlen. Was wird dabei passieren? Wir erhalten einen Fehler. Alles, was wir zuvor erw\u00e4hnt haben, wird stattfinden. Die Regel ist: \u00fcberall ein Bug, es wird alles oben Genannte auftreten.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 scheint, es gibt eine Dokumentation, die besagt, dass, wenn wir ein Schema haben, die Variable \u201esearch_path\u201c angibt, wo wir nach der Tabelle suchen m\u00fcssen. Es scheint, die Variable existiert.<\/p>\n<p><\/p>\n<p>Was ist das Problem? Das Problem ist, dass server-prepared statements nicht erwarten, dass jemand den search_path \u00e4ndern k\u00f6nnte. Dieser Wert bleibt quasi konstant f\u00fcr die Datenbank. Einige Teile k\u00f6nnen die neuen Werte m\u00f6glicherweise nicht \u00fcbernehmen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 sich Ihre Tabellen unterscheiden. Die Version 9.1 f\u00fchrt einfach die alten Abfragen aus. Neuere Versionen k\u00f6nnen einen Fehler erkennen und sagen, dass Sie einen Fehler haben.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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\">Setzen Sie search_path + server-prepared statements =<br \/>\nDer zwischengespeicherte Plan darf den Ergebnistyp nicht \u00e4ndern<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Wie kann man das beheben? Es gibt ein einfaches Rezept \u2013 tun Sie dies nicht. \u00c4ndern Sie den search_path nicht im laufenden Betrieb der Anwendung. Wenn Sie \u00c4nderungen vornehmen, ist es besser, eine neue Verbindung zu erstellen.<\/p>\n<p><\/p>\n<p>Wir k\u00f6nnen dar\u00fcber diskutieren, d. h. es \u00f6ffnen, besprechen, erg\u00e4nzen. Vielleicht \u00fcberzeugen wir die Datenbankentwickler, dass die Datenbank dem Client mitteilen sollte, wenn jemand den Wert \u00e4ndert: \u201eAchten Sie darauf, dass sich dieser Wert ge\u00e4ndert hat. Vielleicht sollten Sie die statements zur\u00fccksetzen oder neu erstellen?\u201c. Momentan verh\u00e4lt sich die Datenbank heimlich und gibt keine Informationen dar\u00fcber, dass sich irgendwo innerhalb der statements etwas ge\u00e4ndert hat. <\/p>\n<p><\/p>\n<p>Ich m\u00f6chte erneut betonen \u2013 das ist nicht typisch f\u00fcr Java. Das Gleiche werden wir eins zu eins in PL\/pgSQL sehen. Aber dort wird es reproduziert.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/a4e7b201d0c0aa4a0092d2b6ff152df7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns versuchen, weitere Daten auszuw\u00e4hlen. Wir w\u00e4hlen und w\u00e4hlen. Wir haben eine Tabelle mit einer Million Zeilen. Jede Zeile hat ein Kilobyte. Ungef\u00e4hr ein Gigabyte Daten. Und wir haben 128 Megabyte Arbeitspeicher in der Java-Maschine. <\/p>\n<p><\/p>\n<p>Wie in allen B\u00fcchern empfohlen, nutzen wir Streaming-Verarbeitung. Das hei\u00dft, wir \u00f6ffnen das ResultSet und lesen die Daten nach und nach. Funktioniert das? Wird es wegen des Speichers abst\u00fcrzen? Liest es nach ein paar Daten? Lassen Sie uns daran glauben, glauben wir an die Datenbank, an Postgres. Wir glauben nicht. St\u00fcrzen wir mit OutOfMemory ab? Wer hatte OutOfMemory? Wer konnte es danach reparieren? Jemand hat es geschafft, es zu reparieren. <\/p>\n<p><\/p>\n<p>Wenn Sie eine Million Zeilen haben, k\u00f6nnen Sie nicht einfach ausw\u00e4hlen. OFFSET\/LIMIT sind unbedingt erforderlich. Wer ist f\u00fcr diese Variante? Und wer ist daf\u00fcr, dass man mit autoCommit spielen sollte? <\/p>\n<p><\/p>\n<p>Hier erweist sich wie gewohnt die unerwartetste Variante als die richtige. Und wenn Sie pl\u00f6tzlich autoCommit ausschalten, wird es helfen. Warum ist das so? Die Wissenschaft kennt die Antwort darauf nicht. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 bildet hier keine Ausnahme und w\u00e4hlt alle Zeilen aus.<\/p>\n<p><\/p>\n<p>Es gibt eine Variation zum Thema FetchSize, d.h. man kann auf Ebene eines einzelnen Statements ausw\u00e4hlen, dass hier bitte Daten in Schritten von 10 oder 50 ausgew\u00e4hlt werden. Aber das funktioniert nur, wenn Sie autoCommit deaktivieren. Wenn autoCommit deaktiviert ist, funktioniert es. <\/p>\n<p><\/p>\n<p>Aber im Code \u00fcberall setFetchSize zu setzen ist unpraktisch. Deshalb haben wir eine Einstellung geschaffen, die f\u00fcr die gesamte Verbindung einen Standardwert vorgibt.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/c9ac8e483dc6bfdbbc300972ee1ab1e0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Das haben wir gesagt. Den Parameter eingestellt. Und was haben wir erreicht? Wenn wir in kleinen Schritten ausw\u00e4hlen, z.B. 10 Zeilen, haben wir ziemlich hohe Overheads. Daher sollte dieser Wert etwa bei hundert liegen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/073e7a4af63688396eefe332ba8261d6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Idealerweise sollten wir das auch in Byte noch lernen zu begrenzen, aber die Empfehlung ist: setze defaultRowFetchSize auf \u00fcber hundert und freue dich. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e3078faf2078fcaec10d6d99872a9990.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lassen Sie uns zum Einf\u00fcgen von Daten \u00fcbergehen. Das Einf\u00fcgen ist einfacher, da es verschiedene Varianten gibt. Zum Beispiel INSERT, VALUES. Das ist eine gute Variante. Man kann auch \u201eINSERT SELECT\u201c verwenden. In der Praxis ist das das Gleiche. Es gibt keinen Unterschied in der Leistung. <\/p>\n<p><\/p>\n<p>B\u00fccher behaupten, dass Batch-Befehle ausgef\u00fchrt werden sollten, und sie sagen, dass es m\u00f6glich ist, komplexere Befehle mit mehreren Klammern auszuf\u00fchren. In Postgres gibt es eine gro\u00dfartige Funktion \u2013 man kann COPY verwenden, das hei\u00dft, man kann es schneller machen. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4d281c9515bdf8de7fbf1e9922d192d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wenn man misst, kann man wieder mehrere interessante Entdeckungen machen. Wie wollen wir, dass das funktioniert? Wir wollen nicht parsen und keine unn\u00f6tigen Befehle ausf\u00fchren. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 das nicht. Wenn der Client besch\u00e4ftigt ist, eine Anfrage zu senden, liest die Datenbank die Anfragen nicht, w\u00e4hrend sie versucht, uns die Antworten zu senden. Letztendlich wartet der Client auf die Datenbank, bis sie die Anfrage gelesen hat, w\u00e4hrend die Datenbank auf den Client wartet, bis dieser die Antwort gelesen hat. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/17a062c30ddd83cb890775ec1722044f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Deshalb ist der Client gezwungen, regelm\u00e4\u00dfig Synchronisationspakete zu senden. \u00dcberfl\u00fcssige Netzinteraktionen, verlorene Zeit.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/ae7a4f32e2949c1d0550da051937de02.jpg\" style=\"display:block;margin: 0 auto;\" \/>Je mehr wir hinzuf\u00fcgen, desto schlimmer wird es. Der Treiber ist ziemlich pessimistisch und f\u00fcgt sie oft hinzu, etwa alle 200 Zeilen, abh\u00e4ngig von der L\u00e4nge der Zeilen, und so weiter. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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>Es kann vorkommen, dass eine einzige Zeile angepasst wird und alles sich zehnmal schneller anf\u00fchlt. So etwas passiert. Warum? Wie \u00fcblich wurde eine Konstante irgendwo bereits verwendet. Und der Wert '128' bedeutete \u2013 kein Batching zu verwenden.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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-Mikrobenchmark-Harness<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Gut, dass das nicht in die offizielle Version gelangt ist. Wir haben es entdeckt, bevor wir mit der Ver\u00f6ffentlichung des Releases begonnen haben. 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 alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/28c82d9e11f6bdaf3cf62b49fc074d68.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Lass uns messen. Wir messen InsertBatch einfach. Wir messen InsertBatch mehrfach, also dasselbe, aber mit vielen values. Ein schlauer Zug. Nicht jeder kann das, aber es ist ein einfacher Schritt, viel einfacher als COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/72f7cf6c3b9d9175d3e5a6d5410fe209.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Man kann COPY verwenden.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/2b0d8bbcc50f1293c9a8a1eb8eb70d2e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Und das kann man auch in Strukturen tun. Den User-Standardtyp deklarieren, ein Array \u00fcbergeben und direkt in die Tabelle einf\u00fcgen. <\/p>\n<p><\/p>\n<p>Wenn Sie den Link \u00f6ffnen: pgjdbc\/ubenchmsrk\/InsertBatch.java, dann finden Sie diesen Code auf GitHub. So kann man sehen, welche spezifischen Anfragen dort generiert werden. Das ist nebens\u00e4chlich.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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 festgestellt haben, ist, dass man Batch nicht meiden kann \u2013 das ist einfach nicht m\u00f6glich. Alle Batching-Varianten sind gleich null, das hei\u00dft, 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 alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/4ea68f712bbdeadd35f5baa4ddaeff33.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wir f\u00fcgen die Daten ein. Es ist eine recht einfache Tabelle. Drei Spalten. Und was sehen wir hier? Wir sehen, dass alle drei Varianten ungef\u00e4hr vergleichbar sind. Und COPY ist nat\u00fcrlich die bessere Wahl.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav 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\u00fcckweise einf\u00fcgen. Wenn wir gesagt haben, dass ein Wert VALUES, zwei Werte VALUES, drei Werte VALUES oder wir sie dort durch Kommas getrennt angegeben haben. Das ist tats\u00e4chlich jetzt horizontal. 1, 2, 4, 128. Man sieht, dass der Batch Insert, der blau gezeichnet ist, deutlich profitieren kann. Das hei\u00dft, wenn Sie einzeln einf\u00fcgen oder sogar wenn Sie vier gleichzeitig einf\u00fcgen, verbessert sich die Leistung um das Doppelte, nur weil wir ein bisschen mehr in VALUES gepackt haben. Weniger EXECUTE-Operationen.<\/p>\n<p><\/p>\n<p>COPY f\u00fcr kleine Datenmengen zu verwenden, ist \u00e4u\u00dferst unvorteilhaft. Ich habe die ersten beiden nicht einmal dargestellt. Sie gehen in die H\u00f6he, d.h. diese gr\u00fcnen Zahlen stehen f\u00fcr COPY.<\/p>\n<p><\/p>\n<p>COPY sollte verwendet werden, wenn Ihr Datenvolumen mindestens mehr als hundert Zeilen betr\u00e4gt. Die Overheadkosten f\u00fcr das \u00d6ffnen dieser Verbindung sind hoch. Und ehrlich gesagt, ich habe in diese Richtung nicht tiefer gegraben. Batch habe ich optimiert, COPY \u2013 nicht. <\/p>\n<p><\/p>\n<p>Was machen wir als N\u00e4chstes? Wir haben gemessen. Wir verstehen, dass wir entweder Strukturen oder ein ausgekl\u00fcgeltes Batch verwenden m\u00fcssen, das mehrere Werte kombiniert. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL und JDBC: Wir sch\u00f6pfen alle M\u00f6glichkeiten aus. Vladislav Sitnikov\" src=\"\/wp-content\/uploads\/2020\/05\/e02fa2574e2d1b678382db15064610d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Was sollte aus dem heutigen Bericht mitgenommen werden?<\/p>\n<p><\/p>\n<ul>\n<li>PreparedStatement \u2013 das ist unser Alleinstellungsmerkmal. Es verbessert die Leistung erheblich. Es gibt einen gro\u00dfen Nachteil. <\/li>\n<li>Und man sollte EXPLAIN ANALYZE 6 Mal durchf\u00fchren.<\/li>\n<li>Zudem sollte man OFFSET 0 sowie Tricks wie +0 verwenden, um den verbleibenden Prozentsatz unserer problematischen Anfragen zu optimieren.<\/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 4.9.10 - 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; \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\" \/>\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) 4.9.10\" \/>\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; \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\" \/>\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 alles auspressen. Vladimir Sitnikov | ProHoster","description":"Ich lade Sie ein, sich die Auswertung des Berichts von Anfang 2016 von Vladimir Sitnikov \"PostgreSQL und JDBC \u2013 alles auspressen\" anzusehen. Guten Tag! Mein Name ist Vladimir Sitnikov. Ich arbeite seit 10 Jahren bei NetCracker. Mein Hauptfokus liegt auf der Leistung. Alles, was mit Java und SQL zu tun hat, ist meine Leidenschaft. Und heute werde ich erz\u00e4hlen,","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; \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","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"},"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}]}}