{"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\/pl\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","title":{"rendered":"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><strong>Zach\u0119cam do zapoznania si\u0119 z t\u0142umaczeniem raportu z pocz\u0105tku 2016 roku autorstwa W\u0142adimira Sitnikowa \"PostgreSQL i JDBC - wyciskamy maksymalne mo\u017cliwo\u015bci\"<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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 i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/030666abda53aa388b1cb1d0c46a7524.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dzie\u0144 dobry! Nazywam si\u0119 W\u0142adimir Sitnikow. Pracuj\u0119 od 10 lat w firmie NetCracker. G\u0142\u00f3wnie zajmuj\u0119 si\u0119 wydajno\u015bci\u0105. Wszystko, co zwi\u0105zane z Jav\u0105, wszystko, co zwi\u0105zane z SQL \u2013 to, co kocham. <\/p>\n<p><\/p>\n<p>Dzi\u015b opowiem o tym, z czym si\u0119 spotkali\u015bmy w firmie, gdy zacz\u0119li\u015bmy u\u017cywa\u0107 PostgreSQL jako serwera baz danych. G\u0142\u00f3wnie pracujemy z Jav\u0105. Ale to, co dzisiaj przedstawi\u0119, dotyczy nie tylko Javy. Jak pokaza\u0142a praktyka, zdarza si\u0119 to tak\u017ce w innych j\u0119zykach. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/b01a214d782c7e32797a6c6466457979.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>B\u0119dziemy m\u00f3wi\u0107 o:<\/p>\n<p><\/p>\n<ul>\n<li>wybiera\u0107 dane. <\/li>\n<li>o zapisywaniu danych. <\/li>\n<li>a tak\u017ce o wydajno\u015bci. <\/li>\n<li>I o pu\u0142apkach, kt\u00f3re tam s\u0105 ukryte. <\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/57da0c6aa4bb62e1beb77160f5aee7e7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Zacznijmy od prostego pytania. Wybieramy jeden wiersz z tabeli wed\u0142ug klucza podstawowego. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/d5fcb1172aef402a87064498bf8a5218.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Baza znajduje si\u0119 na tym samym ho\u015bcie. I ca\u0142e to zajmuje 20 milisekund.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/103b7f9736b82a3313491b2f0d2a5824.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Te 20 milisekund \u2013 to bardzo du\u017co. Je\u015bli masz takich 100 zapyta\u0144, to tracisz czas na to, aby te zapytania przej\u015b\u0107, czyli w wirtualny spos\u00f3b tracisz czas.<\/p>\n<p><\/p>\n<p>Nie lubimy tego robi\u0107, wi\u0119c patrzymy, co baza nam oferuje. Baza oferuje nam dwie opcje wykonania zapyta\u0144. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/b467e86a0f8c4c32cea182fe27a28e6e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Pierwsza opcja \u2013 to proste zapytanie. Dlaczego jest dobre? Poniewa\u017c bierzemy je i wysy\u0142amy, i nic wi\u0119cej. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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>Baza ma tak\u017ce rozszerzone zapytanie, kt\u00f3re jest bardziej przebieg\u0142e, ale bardziej funkcjonalne. Mo\u017cna osobno wys\u0142a\u0107 zapytanie do parsowania, wykonywania, wi\u0105zania zmiennych itp. <\/p>\n<p><\/p>\n<p>Super rozszerzone zapytanie \u2013 to, czego nie b\u0119dziemy omawia\u0107 w tej prezentacji. Mo\u017ce mamy co\u015b do wyci\u0105gni\u0119cia od bazy danych i istnieje taki katalog \u017cycze\u0144, kt\u00f3ry jest w jakiej\u015b formie sformu\u0142owany, czyli to, co chcemy, ale obecnie jest to niemo\u017cliwe i w najbli\u017cszym roku. Dlatego po prostu zapisali\u015bmy to i b\u0119dziemy chodzi\u0107, trz\u0105s\u0105c podstawowymi lud\u017ami.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/19094729074fd28c5b9f236833a9a266.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>A to, co mo\u017cemy zrobi\u0107, to proste zapytanie i rozszerzone zapytanie.<\/p>\n<p><\/p>\n<p>Jaka jest specyfika ka\u017cdego podej\u015bcia? <\/p>\n<p><\/p>\n<p>Proste zapytanie dobrze u\u017cywa\u0107 do jednorazowego wykonania. Raz wykonane i zapomniane. I problem w tym, \u017ce nie obs\u0142uguje binarnego formatu danych, czyli do jakich\u015b system\u00f3w o wysokiej wydajno\u015bci si\u0119 nie nadaje.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/062c0e45cefd91ece331a306a7651bde.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Rozszerzone zapytanie \u2013 pozwala zaoszcz\u0119dzi\u0107 czas podczas parsowania. To jest to, co zrobili\u015bmy i zacz\u0119li\u015bmy u\u017cywa\u0107. To bardzo, bardzo nam pomog\u0142o. Istnieje nie tylko oszcz\u0119dno\u015b\u0107 na parsowaniu. Jest tak\u017ce oszcz\u0119dno\u015b\u0107 na przesy\u0142aniu danych. Przesy\u0142anie danych w formacie binarnym jest znacznie bardziej efektywne. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/6df83a2f3736568668cf9d74de5d9758.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Przejd\u017amy do praktyki. Tak wygl\u0105da typowa aplikacja. To mo\u017ce by\u0107 Java i tak dalej. <\/p>\n<p><\/p>\n<p>Stworzyli\u015bmy statement. Wykonali\u015bmy polecenie. Stworzyli\u015bmy close. Gdzie jest b\u0142\u0105d? Jaki jest problem? Nie ma problem\u00f3w. Tak jest napisane we wszystkich ksi\u0105\u017ckach. Tak trzeba pisa\u0107. Je\u015bli chcesz maksymalnej wydajno\u015bci, pisz tak. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/6923293946dec45b3fd508235728ab95.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jednak praktyka pokaza\u0142a, \u017ce to nie dzia\u0142a. Dlaczego? Poniewa\u017c mamy metod\u0119 \u201eclose\u201d. I gdy to robimy, z perspektywy bazy danych wygl\u0105da to jak praca palacza z baz\u0105 danych. Powiedzieli\u015bmy \u201ePARSE EXECUTE DEALLOCATE\u201d.<\/p>\n<p><\/p>\n<p>Po co te zb\u0119dne tworzenie i \u0142adowanie statements? Nikt ich nie potrzebuje. Ale zwykle w PreparedStatement tak w\u0142a\u015bnie jest, kiedy je zamykamy, zamykaj\u0105 wszystko na bazie danych. To nie jest to, czego chcemy. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/5a357a209e414024c437d042f600f251.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Chcemy, jak zdrowi ludzie, pracowa\u0107 z baz\u0105. Raz bierzemy i przygotowujemy nasz statement, a potem wykonujemy go wiele razy. W rzeczywisto\u015bci wiele razy \u2013 to jeden raz na ca\u0142e \u017cycie aplikacji. I na r\u00f3\u017cnych REST u\u017cywamy tego samego id statementa. To jest nasz cel. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/ac4aed702a624ad9f9addc4362daf760.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jak mo\u017cemy to osi\u0105gn\u0105\u0107? <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/0f101d890d1a2af8e99f8a3a8a06fd5a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Bardzo prosto \u2013 nie trzeba zamyka\u0107 statements. Pisze si\u0119 tak: \u201eprepare\u201d \u201eexecute\u201d. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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 i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/724646bfc55b5e2b611b1584ca8ba6aa.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Je\u015bli uruchomimy co\u015b takiego, to jasne, \u017ce gdzie\u015b co\u015b si\u0119 przepe\u0142ni. Je\u015bli nie jest jasne, to mo\u017cna zmierzy\u0107. We\u017amy i napiszmy benchmark, w kt\u00f3rym taki prosty spos\u00f3b. Tworzymy statement. Uruchamiamy na jakiej\u015b wersji sterownika i widzimy, \u017ce do\u015b\u0107 szybko sypie z utrat\u0105 ca\u0142ej pami\u0119ci, kt\u00f3ra nam zosta\u0142a. <\/p>\n<p><\/p>\n<p>Jasne, \u017ce takie b\u0142\u0119dy \u0142atwo naprawi\u0107. Nie b\u0119d\u0119 o nich m\u00f3wi\u0107. Ale powiem, \u017ce w nowej wersji dzia\u0142a znacznie szybciej. Metoda nieprzydatna, ale mimo to. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/b232a6205e708c70f52be0024bbe9980.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jak pracowa\u0107 poprawnie? Co musimy w tym celu zrobi\u0107?<\/p>\n<p><\/p>\n<p>W rzeczywisto\u015bci aplikacje zawsze zamykaj\u0105 statements. We wszystkich ksi\u0105\u017ckach pisz\u0105, aby je zamyka\u0107, w przeciwnym razie dojdzie do wyciek\u00f3w pami\u0119ci. <\/p>\n<p><\/p>\n<p>I PostgreSQL nie potrafi cache'owa\u0107 zapyta\u0144. Ka\u017cda sesja musi dla siebie sama tworzy\u0107 t\u0119 pami\u0119\u0107 podr\u0119czn\u0105. <\/p>\n<p><\/p>\n<p>I r\u00f3wnie\u017c nie chcemy traci\u0107 czasu na parsowanie. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/85b9574938aa6e6bf59c956e3728bdc3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>I jak zwykle mamy dwa warianty. <\/p>\n<p><\/p>\n<p>Pierwsza opcja \u2013 bierzemy i m\u00f3wimy, \u017ce zr\u00f3bmy wszystko w PgSQL. Tam jest pami\u0119\u0107 podr\u0119czna. Wszystko si\u0119 cache'uje. B\u0119dzie znakomicie. To przetestowali\u015bmy. Mamy 100500 zapyta\u0144. Nie dzia\u0142a. Nie zgadzamy si\u0119 na r\u0119czne przekszta\u0142canie zapyta\u0144 w procedury. Nie, nie. <\/p>\n<p><\/p>\n<p>Mamy drug\u0105 opcj\u0119 \u2013 wzi\u0105\u0107 i sami to zrealizowa\u0107. Otwieramy \u017ar\u00f3d\u0142a, zaczynamy dzia\u0142a\u0107. Dzia\u0142amy. Okaza\u0142o si\u0119, \u017ce nie jest to tak trudne do zrobienia. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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>To si\u0119 pojawi\u0142o w sierpniu 2015 roku. Teraz mamy ju\u017c bardziej nowoczesn\u0105 wersj\u0119. I wszystko dzia\u0142a doskonale. Dzia\u0142a tak dobrze, \u017ce nie wprowadzamy \u017cadnych zmian w aplikacji. Nawet przestali\u015bmy rozwa\u017ca\u0107 PgSQL, tj. to nam wystarczy\u0142o, aby zredukowa\u0107 wszystkie koszty do prawie zera. <\/p>\n<p><\/p>\n<p>Odpowiednio, przygotowane zapytania serwera aktywuj\u0105 si\u0119 przy pi\u0105tym wykonaniu, aby nie marnowa\u0107 pami\u0119ci w bazie danych na ka\u017cde jednorazowe zapytanie. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/8ee95e71f92187941989ad0c18ece0c0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mo\u017cna zapyta\u0107 \u2013 gdzie liczby? Co otrzymujecie? I tutaj nie podam liczb, bo dla ka\u017cdego zapytania s\u0105 inne.<\/p>\n<p><\/p>\n<p>Nasze zapytania wygl\u0105da\u0142y tak, \u017ce na zapytaniach OLTP sp\u0119dzali\u015bmy oko\u0142o 20 milisekund na parsowanie. Tam by\u0142o 0,5 milisekundy na wykonanie, 20 milisekund na parsowanie. Zapytanie \u2013 10 KiB tekstu, 170 rz\u0119d\u00f3w planu. To zapytanie OLTP. Pyta o 1, 5, 10 rz\u0119d\u00f3w, czasem wi\u0119cej. <\/p>\n<p><\/p>\n<p>Ale zupe\u0142nie nie chcieli\u015bmy traci\u0107 20 milisekund. Zredukowali\u015bmy to do zera. Wszystko jest \u015bwietnie. <\/p>\n<p><\/p>\n<p>Co mo\u017cecie z tego wynie\u015b\u0107? Je\u015bli korzystacie z Javy, to bierzecie nowoczesn\u0105 wersj\u0119 sterownika i cieszycie si\u0119. <\/p>\n<p><\/p>\n<p>Je\u015bli korzystacie z jakiego\u015b innego j\u0119zyka, to pomy\u015blcie \u2013 mo\u017ce to r\u00f3wnie\u017c jest dla was? Poniewa\u017c z punktu widzenia ko\u0144cowego j\u0119zyka, na przyk\u0142ad, je\u015bli PL 8 lub macie LibPQ, to nie jest dla was oczywiste, \u017ce tracicie czas nie na wykonanie, a na parsowanie i warto to sprawdzi\u0107. Jak? Wszystko jest darmowe. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/fda6b1c1b126cb85c970e42335e1dc24.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Z wyj\u0105tkiem tego, \u017ce s\u0105 b\u0142\u0119dy, jakie\u015b szczeg\u00f3lne przypadki. O tym w\u0142a\u015bnie teraz b\u0119dziemy m\u00f3wi\u0107. Wi\u0119kszo\u015b\u0107 b\u0119dzie o archeologii przemys\u0142owej, o tym, co znale\u017ali\u015bmy, na co natrafili\u015bmy. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/9a3b383f83c5e227cd02952c5825b64f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Je\u015bli zapytanie jest generowane dynamicznie. Tak si\u0119 zdarza. Kto\u015b \u0142\u0105czy ci\u0105gi, w rezultacie otrzymuje zapytanie SQL.<\/p>\n<p><\/p>\n<p>Czym to jest z\u0142e? Jest z\u0142e tym, \u017ce za ka\u017cdym razem otrzymujemy inn\u0105 lini\u0119.<\/p>\n<p><\/p>\n<p>I ta r\u00f3\u017cna linia musi ponownie obliczy\u0107 hashCode. To naprawd\u0119 jest zadanie CPU \u2013 znalezienie d\u0142ugiego tekstu zapytania w istniej\u0105cym hashu nie jest takie proste. Dlatego prosty wniosek \u2013 nie generujcie zapyta\u0144. Przechowujcie je w jakiej\u015b jednej zmiennej. I cieszcie si\u0119.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/bcd4f729204cecdb572a98f357bcc33f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Nast\u0119pny problem. Typy danych s\u0105 wa\u017cne. S\u0105 ORM-y, kt\u00f3re m\u00f3wi\u0105, \u017ce nie ma znaczenia, jaki NULL, niech b\u0119dzie jaki\u015b. Je\u015bli to Int, m\u00f3wimy setInt. A je\u015bli NULL, to niech zawsze b\u0119dzie VARCHAR. I co za r\u00f3\u017cnica, jaki tam tak naprawd\u0119 NULL? Baza danych sama wszystko zrozumie. A taka wizja nie dzia\u0142a. <\/p>\n<p><\/p>\n<p>W praktyce bazie danych nie jest wszystko jedno. <strong>Je\u015bli za pierwszym razem powiedzia\u0142e\u015b, \u017ce to liczba, a za drugim, \u017ce to VARCHAR, to niemo\u017cliwe jest ponowne u\u017cycie przygotowanych polece\u0144 serwera. I w takim przypadku musimy na nowo stworzy\u0107 nasze polecenie.<\/strong><\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/552d9c2e25cf9abbdfef12c98d027740.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Je\u015bli wykonujesz to samo zapytanie, uwa\u017caj, aby typy danych w kolumnie si\u0119 nie myli\u0142y. Nale\u017cy zwraca\u0107 uwag\u0119 na NULL. To cz\u0119sty b\u0142\u0105d, kt\u00f3ry mieli\u015bmy po tym, jak zacz\u0119li\u015bmy u\u017cywa\u0107 PreparedStatements.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/5d73c8f5dfa281c3bda962fc3e5edd84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dobrze, w\u0142\u0105czono. Mo\u017ce wzi\u0119li\u015bmy jaki\u015b sterownik. I wydajno\u015b\u0107 spad\u0142a. Wszystko sta\u0142o si\u0119 z\u0142e. <\/p>\n<p><\/p>\n<p>Jak to si\u0119 dzieje? To b\u0142\u0105d czy funkcjonalno\u015b\u0107? Niestety, nie uda\u0142o si\u0119 ustali\u0107 \u2013 to b\u0142\u0105d czy funkcjonalno\u015b\u0107. Ale jest ca\u0142kiem prosty scenariusz, aby odtworzy\u0107 ten problem. Ca\u0142kowicie niespodziewanie nas zaskoczy\u0142. Polega na zapytaniu dos\u0142ownie z jednej tabeli. Oczywi\u015bcie mieli\u015bmy wi\u0119cej takich zapyta\u0144. Zazwyczaj obejmowa\u0142y dwie lub trzy tabele, ale jest taki scenariusz odtworzenia. We\u017a swoj\u0105 baz\u0119 dowolnej wersji i reprodukuj.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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>Chodzi o to, \u017ce mamy dwie kolumny, z kt\u00f3rych ka\u017cda jest zaindeksowana. W jednej kolumnie znajduje si\u0119 milion wierszy z warto\u015bci\u0105 NULL. A w drugiej kolumnie tylko 20 wierszy. Kiedy wykonujemy bez powi\u0105zanych zmiennych, wszystko dzia\u0142a dobrze. <\/p>\n<p><\/p>\n<p>Je\u015bli zaczniemy wykonywa\u0107 ze zwi\u0105zanymi zmiennymi, czyli wykonujemy znak '?' lub '$1' dla naszego zapytania, to co finalnie otrzymujemy?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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>Pierwsze wykonanie \u2013 jak nale\u017cy. Drugie \u2013 troch\u0119 szybciej. Co\u015b si\u0119 zbuforowa\u0142o. Trzecie-czwarte-pi\u0105te. Potem bum \u2013 i tak jako\u015b. I najgorsze jest to, \u017ce dzieje si\u0119 to przy sz\u00f3stym wykonaniu. Kto by pomy\u015bla\u0142, \u017ce trzeba zrobi\u0107 w\u0142a\u015bnie sze\u015b\u0107 wykona\u0144, aby zrozumie\u0107, jaki tam naprawd\u0119 jest plan wykonania?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/893c955bc1e2e15a6986690e516f72a9.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Kto jest winny? Co si\u0119 sta\u0142o? Baza danych zawiera optymalizacj\u0119. I jest ona jakby zoptymalizowana pod og\u00f3lny przypadek. I, odpowiednio, od jakiego\u015b momentu przechodzi na og\u00f3lny plan, kt\u00f3ry, niestety, mo\u017ce okaza\u0107 si\u0119 innym. Mo\u017ce by\u0107 taki sam, a mo\u017ce by\u0107 inny. I istnieje jakie\u015b warto\u015b\u0107 progowa, kt\u00f3ra prowadzi do takiego zachowania. <\/p>\n<p><\/p>\n<p>Co mo\u017cna z tym zrobi\u0107? Tutaj, oczywi\u015bcie, trudniej co\u015b przypuszcza\u0107. Jest proste rozwi\u0105zanie, kt\u00f3rego u\u017cywamy. To +0, OFFSET 0. Pewnie znacie takie rozwi\u0105zania. Po prostu bierzemy i dodajemy do zapytania \u201e+0\u201d i wszystko jest w porz\u0105dku. Poka\u017c\u0119 p\u00f3\u017aniej. <\/p>\n<p><\/p>\n<p>I jest jeszcze jedna opcja \u2013 uwa\u017ca\u0107 na plany. Programista powinien nie tylko napisa\u0107 zapytanie, ale tak\u017ce 6 razy powiedzie\u0107 \u201eexplain analyze\u201d. Je\u015bli 5, to nie b\u0119dzie odpowiednie. <\/p>\n<p><\/p>\n<p>I jest jeszcze trzecia opcja \u2013 napisa\u0107 list do pgsql-hackers. Napisa\u0142em, ale na razie nie jest jasne \u2013 to b\u0142\u0105d czy cecha.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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>A podczas gdy my my\u015blimy \u2013 czy to b\u0142\u0105d czy cecha, naprawmy to. We\u017amy nasze zapytanie i dodajmy \u201e+0\u201d. Wszystko jest w porz\u0105dku. Dwa znaki i nawet nie musimy my\u015ble\u0107, jak tam to wygl\u0105da. Bardzo prosto. Po prostu zabronili\u015bmy bazie danych u\u017cywa\u0107 indeksu za t\u0105 kolumn\u0105. Nie mamy indeksu za kolumn\u0105 \u201e+0\u201d i wszystko, baza danych nie u\u017cywa indeksu, wszystko jest w porz\u0105dku. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/5261d80d084786a15b9764f6367014cd.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Oto zasada 6 explain\u00f3w. W aktualnych wersjach nale\u017cy to robi\u0107 6 razy, je\u015bli macie powi\u0105zane zmienne. Je\u015bli nie macie powi\u0105zanych zmiennych, to robimy to tak. I w ko\u0144cu to w\u0142a\u015bnie to zapytanie zawodzi. Sprawa nie jest trudna.<\/p>\n<p><\/p>\n<p>Wydawa\u0142oby si\u0119, ile mo\u017cna? Gdzie\u015b b\u0142\u0105d, tam b\u0142\u0105d. Naprawd\u0119 b\u0142\u0105d jest wsz\u0119dzie. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/674dc7e7b4624baa5c79eee9e1e89db0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Sp\u00f3jrzmy jeszcze raz. Na przyk\u0142ad mamy dwa schematy. Schemat A z tabel\u0105 \u042b i schemat B z tabel\u0105 \u042b. Zapytanie \u2013 wybra\u0107 dane z tabeli. Co w takim razie b\u0119dzie? B\u0119dzie b\u0142\u0105d. B\u0119dzie wszystko, co wy\u017cej wymienione. Zasada jest taka \u2013 b\u0142\u0105d wsz\u0119dzie, b\u0119dziemy mie\u0107 wszystko wymienione powy\u017cej.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/dd6f544bb9e930c39e36523c9afe6003.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Teraz pytanie: \u201eDlaczego?\u201d. Wydawa\u0142oby si\u0119, \u017ce jest dokumentacja, kt\u00f3ra m\u00f3wi, \u017ce je\u015bli mamy schemat, to jest zmienna \u201esearch_path\u201d, kt\u00f3ra m\u00f3wi, gdzie szuka\u0107 tabeli. Wydawa\u0142oby si\u0119, \u017ce zmienna istnieje.<\/p>\n<p><\/p>\n<p>Jaki jest problem? Problem w tym, \u017ce server-prepared statements nie podejrzewaj\u0105, \u017ce search_path mo\u017ce by\u0107 zmieniane przez kogo\u015b. Ta warto\u015b\u0107 pozostaje jakby sta\u0142a dla bazy danych. I niekt\u00f3re cz\u0119\u015bci mog\u0105 nie przechwyci\u0107 nowych warto\u015bci. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/f738301606d84d72bfa7cc5b568c0df8.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Oczywi\u015bcie, zale\u017cy to od wersji, na kt\u00f3rej testujesz. Zale\u017cy od tego, jak bardzo r\u00f3\u017cni\u0105 si\u0119 twoje tabele. A wersja 9.1 po prostu wykona stare zapytania. Nowe wersje mog\u0105 wykry\u0107 oszustwo i powiedzie\u0107, \u017ce masz b\u0142\u0105d.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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 + przygotowane instrukcje serwera =<br \/>\nplan w pami\u0119ci podr\u0119cznej nie mo\u017ce zmienia\u0107 typu wyniku<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Jak to naprawi\u0107? Jest prosty przepis \u2013 nie r\u00f3b tak. Nie zmieniaj search_path podczas dzia\u0142ania aplikacji. Je\u015bli zmieniasz, to lepiej utworzy\u0107 nowe po\u0142\u0105czenie.<\/p>\n<p><\/p>\n<p>Mo\u017cna to przedyskutowa\u0107, tzn. otworzy\u0107, om\u00f3wi\u0107, doda\u0107. Mo\u017ce przekonamy tw\u00f3rc\u00f3w bazy danych, \u017ce w przypadku, gdy kto\u015b zmienia warto\u015b\u0107, baza danych powinna informowa\u0107 klienta: \u201eZobacz, tutaj warto\u015b\u0107 zosta\u0142a zaktualizowana. Mo\u017ce powiniene\u015b zresetowa\u0107 instrukcje, odtworzy\u0107 je?\u201d. Obecnie baza danych zachowuje si\u0119 w ukryty spos\u00f3b i w \u017caden spos\u00f3b nie informuje, \u017ce gdzie\u015b wewn\u0119trznie instrukcje si\u0119 zmieni\u0142y. <\/p>\n<p><\/p>\n<p>I znowu podkre\u015bl\u0119 \u2013 to, co nie jest typowe dla Javy. To samo zobaczymy w PL\/pgSQL jeden do jednego. Ale tam to zostanie odwzorowane.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/a4e7b201d0c0aa4a0092d2b6ff152df7.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Spr\u00f3bujmy jeszcze raz wybra\u0107 dane. Wybieramy, wybieramy. Mamy tabel\u0119 z milionem wierszy. Ka\u017cdy wiersz ma oko\u0142o kilobajta. Oko\u0142o gigabajta danych. A mamy robocz\u0105 pami\u0119\u0107 w maszyny Java wynosz\u0105c\u0105 128 megabajt\u00f3w. <\/p>\n<p><\/p>\n<p>Jak zwykle, korzystamy z przetwarzania strumieniowego, jak zalecaj\u0105 wszystkie ksi\u0105\u017cki. Tzn. otwieramy resultSet i czytamy stamt\u0105d dane stopniowo. Czy to zadzia\u0142a? Czy nie padnie z powodu braku pami\u0119ci? Czy b\u0119dzie czyta\u0107 po trochu? Zaufajmy bazie, zaufajmy Postgresowi. Nie wierzymy. Padniemy OutOfMemory? Kto mia\u0142 OutOfMemory? A kto uda\u0142o si\u0119 po tym naprawi\u0107? Kto\u015b uda\u0142o si\u0119 naprawi\u0107. <\/p>\n<p><\/p>\n<p>Je\u015bli masz milion wierszy, nie mo\u017cna po prostu tak wybiera\u0107. Konieczne jest OFFSET\/LIMIT. Kto jest za takim rozwi\u0105zaniem? A kto jest za tym, \u017ce trzeba bawi\u0107 si\u0119 w autoCommit? <\/p>\n<p><\/p>\n<p>Tutaj jak zwykle, najbardziej nieoczekiwane rozwi\u0105zanie okazuje si\u0119 by\u0107 poprawne. I je\u015bli nagle wy\u0142\u0105czysz autoCommit, to pomo\u017ce. Dlaczego tak? Nauce tego nie wiadomo. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/2e5d9d2a8450a9069155de86a8ec387a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Ale domy\u015blnie wszyscy klienci \u0142\u0105cz\u0105cy si\u0119 z baz\u0105 danych Postgres wybieraj\u0105 dane w ca\u0142o\u015bci. PgJDBC w tym przypadku nie jest wyj\u0105tkiem, wybiera wszystkie wiersze.<\/p>\n<p><\/p>\n<p>Istnieje wariant w temacie FetchSize, tzn. mo\u017cna na poziomie pojedynczej instrukcji powiedzie\u0107, \u017ce tutaj, prosz\u0119, wybieraj dane po 10, 50. Ale to nie dzia\u0142a, dop\u00f3ki nie wy\u0142\u0105czysz autoCommit. Wy\u0142\u0105czy\u0142e\u015b autoCommit \u2013 zaczyna dzia\u0142a\u0107. <\/p>\n<p><\/p>\n<p>Jednak przechodzenie przez kod i wsz\u0119dzie ustawianie setFetchSize jest niewygodne. Dlatego zrobili\u015bmy tak\u0105 konfiguracj\u0119, kt\u00f3ra dla ca\u0142ego po\u0142\u0105czenia ustala warto\u015b\u0107 domy\u015bln\u0105.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/c9ac8e483dc6bfdbbc300972ee1ab1e0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>To powiedzieli\u015bmy. Ustawili\u015bmy parametr. I co nam z tego wysz\u0142o? Je\u015bli wybieramy po ma\u0142ej ilo\u015bci, na przyk\u0142ad po 10 wierszy, to mamy do\u015b\u0107 du\u017ce obci\u0105\u017cenia. Dlatego trzeba ustawia\u0107 t\u0119 warto\u015b\u0107 na oko\u0142o setki. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/073e7a4af63688396eefe332ba8261d6.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>W idealnym przypadku, oczywi\u015bcie, warto by nauczy\u0107 si\u0119 ogranicza\u0107 tak\u017ce w bajtach, ale przepis jest taki: ustawiamy defaultRowFetchSize na wi\u0119cej ni\u017c sto i cieszymy si\u0119. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/e3078faf2078fcaec10d6d99872a9990.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Przejd\u017amy teraz do wstawiania danych. Wstawianie jest prostsze, s\u0105 r\u00f3\u017cne opcje. Na przyk\u0142ad, INSERT, VALUES. To dobry wyb\u00f3r. Mo\u017cna te\u017c m\u00f3wi\u0107 \u201eINSERT SELECT\u201d. W praktyce to to samo. Nie ma \u017cadnej r\u00f3\u017cnicy w wydajno\u015bci. <\/p>\n<p><\/p>\n<p>Ksi\u0105\u017cki m\u00f3wi\u0105, \u017ce nale\u017cy wykonywa\u0107 Batch statement, ksi\u0105\u017cki m\u00f3wi\u0105, \u017ce mo\u017cna wykonywa\u0107 bardziej z\u0142o\u017cone komendy z wieloma nawiasami. A w Postgres jest \u015bwietna funkcja \u2013 mo\u017cna robi\u0107 COPY, tzn. robi\u0107 to szybciej. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/4d281c9515bdf8de7fbf1e9922d192d0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Je\u015bli zmierzymy, mo\u017cna ponownie odkry\u0107 kilka interesuj\u0105cych rzeczy. Jak chcemy, aby to dzia\u0142a\u0142o? Chcemy nie parsowa\u0107 i nie wykonywa\u0107 zb\u0119dnych komend. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/7b2e4343c51dab3ddb6919bf0f117c4d.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>W praktyce TCP na to nie pozwala. Je\u015bli klient jest zaj\u0119ty wysy\u0142aniem zapytania, to baza danych w swoich pr\u00f3bach wys\u0142ania nam odpowiedzi, nie odczytuje zapyta\u0144. W efekcie klient czeka na baz\u0119 danych, a\u017c ona odczyta zapytanie, a baza danych czeka na klienta, a\u017c on odczyta odpowied\u017a. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/17a062c30ddd83cb890775ec1722044f.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Dlatego klient zmuszony jest okresowo wysy\u0142a\u0107 pakiet synchronizacji. Zb\u0119dne interakcje sieciowe, zb\u0119dna utrata czasu.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/ae7a4f32e2949c1d0550da051937de02.jpg\" style=\"display:block;margin: 0 auto;\" \/>Im wi\u0119cej ich dodajemy, tym gorzej si\u0119 to robi. Sterownik jest do\u015b\u0107 pesymistyczny i dodaje je do\u015b\u0107 cz\u0119sto, oko\u0142o co 200 wierszy, w zale\u017cno\u015bci od rozmiaru wierszy itd. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" 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>Czasami wystarczy poprawi\u0107 jeden wiersz, a wszystko przyspiesza dziesi\u0119ciokrotnie. Tak bywa. Dlaczego? Jak zazwyczaj, gdzie\u015b taka sta\u0142a by\u0142a ju\u017c u\u017cywana. A warto\u015b\u0107 \"128\" oznacza\u0142a \u2013 nie u\u017cywa\u0107 batchingu.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/7b6bcd95591b36441037b4c4631d2ad0.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"http:\/\/openjdk.java.net\/projects\/code-tools\/jmh\/\">Java microbenchmark harness<\/a><\/noindex><\/p>\n<p><\/p>\n<p>Dobrze, \u017ce to nie trafi\u0142o do oficjalnej wersji. Odkryli\u015bmy to, zanim zacz\u0119li\u015bmy wydawa\u0107 wersj\u0119. Wszystkie warto\u015bci, kt\u00f3re podaj\u0119, opieraj\u0105 si\u0119 na nowoczesnych wersjach. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/28c82d9e11f6bdaf3cf62b49fc074d68.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Zmie\u0144my parametry. Mierzymy InsertBatch prosty. Mierzymy InsertBatch wielokrotny, tzn. to samo, ale z wieloma warto\u015bciami. Sprytny ruch. Nie ka\u017cdy umie to zrobi\u0107, ale to do\u015b\u0107 prosty spos\u00f3b, znacznie \u0142atwiejszy ni\u017c COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/72f7cf6c3b9d9175d3e5a6d5410fe209.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mo\u017cna robi\u0107 COPY.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/2b0d8bbcc50f1293c9a8a1eb8eb70d2e.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Mo\u017cna to robi\u0107 na strukturach. Og\u0142o\u015b domy\u015blny typ u\u017cytkownika, przeka\u017c tablic\u0119 i wstaw bezpo\u015brednio do tabeli. <\/p>\n<p><\/p>\n<p>Je\u015bli otworzysz link: pgjdbc\/ubenchmsrk\/InsertBatch.java, znajdziesz ten kod na GitHubie. Mo\u017cesz zobaczy\u0107, jakie konkretne zapytania s\u0105 generowane. Nie jest to kluczowe.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/28dede211fe401286685ca62081453de.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Uruchomili\u015bmy to. I pierwsze, co zrozumieli\u015bmy, to to, \u017ce nie mo\u017cna nie u\u017cywa\u0107 batch \u2013 to po prostu niemo\u017cliwe. Wszystkie warianty batchingu s\u0105 r\u00f3wne zeru, to znaczy czas wykonania jest praktycznie r\u00f3wny zeru w por\u00f3wnaniu do jednorazowego wykonania. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/4ea68f712bbdeadd35f5baa4ddaeff33.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wstawiamy dane. To do\u015b\u0107 prosta tabela. Trzy kolumny. I co tutaj widzimy? Widzimy, \u017ce wszystkie te trzy warianty s\u0105 mniej wi\u0119cej por\u00f3wnywalne. A COPY jest oczywi\u015bcie lepsze.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/2686f1d7eea347a839ac23aef2a27e53.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>To wtedy, gdy wstawiamy kawa\u0142kami. Kiedy m\u00f3wili\u015bmy, \u017ce jedno warto\u015b\u0107 VALUES, dwa warto\u015b\u0107 VALUES, trzy warto\u015b\u0107 VALUES lub podali\u015bmy je 10 przez przecinek. To obecnie w poziomie. 1, 2, 4, 128. Wida\u0107, \u017ce Batch Insert, kt\u00f3ry jest zaznaczony na niebiesko, znacznie u\u0142atwia sytuacj\u0119. To znaczy, \u017ce gdy wstawiasz po jednym lub nawet po cztery, sytuacja poprawia si\u0119 dwukrotnie, tylko dlatego, \u017ce w VALUES troch\u0119 wi\u0119cej wci\u015bn\u0119li\u015bmy. Mniej operacji EXECUTE.<\/p>\n<p><\/p>\n<p>U\u017cywanie COPY przy ma\u0142ych obj\u0119to\u015bciach jest ekstremalnie nieperspektywiczne. Nawet nie zaznaczy\u0142em pierwszych dw\u00f3ch. Id\u0105 w niebo, to znaczy te zielone liczby dla COPY.<\/p>\n<p><\/p>\n<p>COPY nale\u017cy u\u017cywa\u0107, gdy masz obj\u0119to\u015b\u0107 danych wi\u0119ksz\u0105 ni\u017c sto wierszy. Koszty zwi\u0105zane z otwarciem tego po\u0142\u0105czenia s\u0105 du\u017ce. I, szczerze m\u00f3wi\u0105c, w t\u0119 stron\u0119 nie kopa\u0142em. Optymalizowa\u0142em batch, COPY \u2013 nie. <\/p>\n<p><\/p>\n<p>Co robimy dalej? Mierzymy. Rozumiemy, \u017ce trzeba u\u017cywa\u0107 albo struktur, albo sprytnego batchu, \u0142\u0105cz\u0105cego kilka warto\u015bci. <\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"PostgreSQL i JDBC wyciskamy z nich wszystko, co najlepsze. W\u0142odzimierz Sitnikow\" src=\"\/wp-content\/uploads\/2020\/05\/e02fa2574e2d1b678382db15064610d3.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Co nale\u017cy wynie\u015b\u0107 z dzisiejszego referatu?<\/p>\n<p><\/p>\n<ul>\n<li>PreparedStatement \u2013 to nasze wszystko. Znacz\u0105co poprawia wydajno\u015b\u0107. To daje du\u017c\u0105 beczk\u0119 dziegciu. <\/li>\n<li>I trzeba robi\u0107 EXPLAIN ANALYZE 6 razy.<\/li>\n<li>I nale\u017cy rozcie\u0144cza\u0107 OFFSET 0, oraz stosowa\u0107 sztuczki takie jak +0, aby naprawi\u0107 pozosta\u0142y procent naszych problematycznych zapyta\u0144.<\/li>\n<\/ul>\n<p>\u0179r\u00f3d\u0142o: <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\/pl\/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=\"pl_PL\" \/>\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\/pl\/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\udd47 PostgreSQL i JDBC wyciskaj\u0105 z nas wszystkie soki. W\u0142odzimierz Sitnik | ProHoster","description":"Zach\u0119cam do zapoznania si\u0119 z transkrypcj\u0105 raportu z pocz\u0105tku 2016 roku autorstwa W\u0142adimira Sitnikowa \"PostgreSQL i JDBC \u2013 wyci\u015bnijmy z nich wszystko\"","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/postgresql-i-jdbc-vyzhimaem-vse-soki-vladimir-sitnikov","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","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\/pl\/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\/pl\/wp-json\/wp\/v2\/posts\/79893","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=79893"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/79893\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/79894"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=79893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=79893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=79893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}