Vaše izvješće završava za oko sekundu na hostu baze podataka i za četiri preko VPN-a, a broj koji psql ispisuje pokazuje ravno na bazu podataka. Taj broj je štoperica na strani klijenta, a ne mjerenje poslužitelja. Pokriva naredbu na putu van, čekanje na odgovor i retke na putu natrag. Razdvajanje tih triju stvari jedini je način da saznate koju stranu podesiti.

Što psql tajmer zapravo mjeri

\timing on pokreće štopericu neposredno prije nego što klijent upiše naredbu u socket i zaustavlja je kada se pročita posljednji redak tog rezultata. Izvršavanje samog poslužitelja nalazi se negdje unutar tog prozora, kao i svaki milisekund koji paketi provedu u tunelu. Ništa u izlazu ih ne razdvaja. EXPLAIN također ispisuje broj, ali njega proizvodi poslužitelj i isključuje čekanje klijenta. Dva broja, dva različita pitanja.

Pokrenite jednu sesiju, ostavite mjerenje uključeno i izvršite naredbu koja vas zanima iz nje. Ponovno korištenje sesije drži cijenu povezivanja izvan prvog mjerenja.

psql "host=db.internal port=5432 dbname=appdb user=app" \
  -c "\timing on" \
  -c "select count(*) from orders where created_at > now() - interval '1 day';"

Broj i dalje uključuje prijenos rezultata, što je ovdje ono što želite. Više ne uključuje razmjenu povezivanja i provjere identiteta koju bi novi poziv dodao.

Usporedite jednu naredbu na tri puta

Izvršite istu naredbu preko Unix socketa, preko LAN adrese i preko VPN imena hosta, pa usporedite tri. Ako je samo posljednji spor, upit nije varijabla.

Svaki poziv prvo se povezuje i tek onda mjeri naredbu, pa cijena povezivanja ostaje izvan broja dok prijenos ostaje unutra. Držite WHERE klauzulu, broj redaka i klijentsko računalo istima, inače uspoređujete radna opterećenja, a ne putove.

psql -h /var/run/postgresql -U app -d appdb -c "\timing on" -c "select count(*) from orders;"
psql -h 10.40.0.12 -U app -d appdb -c "\timing on" -c "select count(*) from orders;"
psql -h db.vpn.internal -U app -d appdb -c "\timing on" -c "select count(*) from orders;"

Na LAN-u je razlika obično dovoljno mala da se zanemari; preko VPN-a često nije. Sortiranje triju rezultata tada vam kaže na koje čekanje napasti.

Spor lokalni socket znači rad poslužitelja ili planiranje. Spor LAN rezultat znači upit ili obujam koji prenosi. Samo spor VPN rezultat znači latenciju, MTU ili dostupnu propusnost na putu, i nikakve promjene indeksa to ne ispravljaju.

Izvucite vlastito vrijeme izvršavanja poslužitelja

Poslužitelj može izvijestiti koliko je vremena potrošio, a upravo taj broj treba držati uz klijentski. EXPLAIN s ANALYZE izvršava naredbu i ispisuje vrijeme planiranja i izvršavanja unutar plana, dok BUFFERS dodaje ponašanje predmemorije koje često objašnjava topao izlaz nasuprot hladnom. Ne usmjeravajte ga na naredbe pisanja osim ako namjeravate pisati. Na replici samo za čitanje to je najsigurnije mjesto za početak, i psql referenca dokumentira opseg tajmera na strani klijenta.

Za naredbe koje aplikacija već izvršava, pg_stat_statements sažima izvršavanje na strani poslužitelja bez ponovnog pokretanja bilo čega. Retci su po normaliziranom upitu, pa jedan unos može pokriti nekoliko mjesta poziva.

select calls, round(mean_exec_time::numeric, 2) as mean_ms, rows
from pg_stat_statements
order by mean_exec_time desc
limit 5;

Nazivi stupaca razlikuju se po izdanju: PostgreSQL 13 i noviji izlažu mean_exec_time, a starije verzije ga zovu mean_time, pa provjerite s \d pg_stat_statements, ako ta naredba vrati pogrešku. Ove vrijednosti isključuju planiranje i isključuju put do vašeg klijenta. Upravo to isključivanje čini ih korisnima za usporedbu.

Brojite povratne runde, ne samo milisekunde

Izvješće sastavljeno od dvjesto malih zavisnih naredbi množi bilo koju latenciju koju put dodaje s dvjesto. Jedna naredba koja vraća milijun redaka plaća tu latenciju jednom i zatim dugo plaća propusnost. Oba izgledaju isto u klijentskom tajmeru, a traže suprotne popravke.

Izmjerite broj naredbi umjesto da ga procjenjujete. Pročitajte ukupno, pokrenite izvješće jednom, pa pročitajte ponovno i oduzmite.

select sum(calls) as calls from pg_stat_statements;
-- run the report here
select sum(calls) as calls from pg_stat_statements;

Ako je delta velika i svaka naredba je jeftina na poslužitelju, grupiranje je popravak koji se isplati. Kombinirajte zavisne upite, koristite jednu naredbu zasnovanu na skupu ili vodite petlju iz kursora. U interaktivnom psql-u, \set FETCH_COUNT 1000 čini da veliki rezultat stiže u serijama i mijenja ponašanje prijenosa i klijentske memorije. Ne smanjuje rad poslužitelja, pa neće spasiti spor upit.

Provjerite tunel na ponovne prijenose i zastoje MTU-a

Kada su male naredbe brze, a veliki rezultati stanu na pola puta, upit je obično u redu, a put nije. Tunel čiji je MTU niži od osnovne veze, u kombinaciji s blokiranim ICMP-om, proizvodi upravo takav obrazac: prvi paketi stignu, veliki nestanu, a prijenos visi dok ponovno ne pokuša. Brojači ponovnih prijenosa na strani klijenta to pokazuju.

Gledajte socket i brojače jezgre dok traje namjerno spor prijenos. Ukupni ponovni prijenosi su kumulativni, pa ih zabilježite prije i poslije umjesto da čitate jedan uzorak.

ss -tin dst 10.40.0.12
netstat -s | grep -iE "retransmit|segments retransmitted"
ping -M do -s 1400 -c 3 10.40.0.12

Ponovni prijenosi koji rastu tijekom prijenosa, zajedno s pingom koji uspijeva na 1400 bajtova ali ne uspijeva na 1472, ukazuju na problem s MTU-om, a ne s bazom podataka. Snižavanje MTU-a sučelja tunela ili stezanje MSS-a na gatewayu uobičajena je popravka, a iste tri naredbe to potvrđuju kasnije. Ako ponovni prijenosi ostanu ravni dok je prijenos još spor, gledajte propusnost dopuštenu tunelu, a ne gubitak.

Kratak slijed pokriva sljedeće sporo izvješće: usporedite jednu naredbu lokalno, na LAN-u i preko VPN-a; pročitajte izvršavanje poslužitelja iz EXPLAIN ili pg_stat_statements; prebrojite pozive prije i poslije jednog pokretanja; i gledajte ponovne prijenose dok se veliki rezultat kreće. Svaki korak isključuje jednu stranu puta. Nijedan ne zahtijeva nagađanje koju komponentu restartati.

Povezani članci