Vaše poročilo se konča v približno eni sekundi na gostitelju baze podatkov in v štirih prek VPN, število, ki ga izpiše psql, pa kaže naravnost na bazo podatkov. To število je štoparica na strani odjemalca, ne meritev strežnika. Zajema stavek na poti ven, čakanje na odgovor in vrstice na poti nazaj. Ločiti te tri je edini način, da veste, katero stran prilagoditi.

Kaj časovnik psql v resnici meri

\timing on zažene štoparico tik preden odjemalec zapiše stavek v vtičnico, in jo ustavi, ko je prebrana zadnja vrstica tega rezultata. Izvajanje samega strežnika je nekje znotraj tega okna, prav tako vsaka milisekunda, ki jo paketi preživijo v tunelu. Nič v izhodu jih ne ločuje. EXPLAIN prav tako izpiše število, a to ustvari strežnik in izključuje čakanje odjemalca. Dve števili, dve različni vprašanji.

Zaženite eno sejo, pustite merjenje časa vključeno in izvedite stavek, ki vas zanima, od znotraj nje. Ponovna uporaba seje drži stroške povezave zunaj prve meritve.

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';"

Število še vedno vključuje prenos rezultata, kar je tu želeno. Ne vključuje več izmenjave povezave in preverjanja pristnosti, ki bi jo dodal nov klic.

Primerjajte en stavek na treh poteh

Zaženite isti stavek prek vtičnice Unix, prek naslova LAN in prek imena gostitelja VPN ter primerjajte vse tri. Če je počasna le zadnja, poizvedba ni spremenljivka.

Vsak klic se najprej poveže in šele nato meri stavek, zato stroški povezave ostanejo zunaj števila, medtem ko prenos ostane znotraj. Ohranite enake pogoj WHERE, število vrstic in odjemalčev stroj, sicer primerjate delovne obremenitve in ne poti.

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;"

V LAN je razlika običajno dovolj majhna, da jo zanemarimo; prek VPN pogosto ni. Razvrščanje treh rezultatov vam nato pove, na katero čakanje napasti.

Počasna lokalna vtičnica pomeni delo strežnika ali načrtovanje. Počasen rezultat LAN pomeni poizvedbo ali obseg, ki ga prenaša. Samo počasen rezultat VPN pomeni zakasnitev, MTU ali razpoložljivo pasovno širino na poti, in nobena sprememba indeksa tega ne odpravi.

Izvlecite lasten čas izvajanja strežnika

Strežnik lahko poroča, koliko časa je porabil, in prav to število je treba primerjati z odjemalčevim. EXPLAIN z ANALYZE izvede stavek in izpiše čas načrtovanja in izvajanja v načrtu, medtem ko BUFFERS doda vedenje predpomnilnika, ki pogosto pojasni topel tek proti hladnemu. Ne usmerjajte ga na stavke za pisanje, razen če nameravate pisati. Na repliki samo za branje je to najvarnejše mesto za začetek, in referenca psql dokumentira obseg časovnika na strani odjemalca.

Za stavke, ki jih aplikacija že izvaja, pg_stat_statements združuje izvajanje na strani strežnika, ne da bi karkoli znova zagnal. Vrstice so na normalizirano poizvedbo, zato lahko en vnos pokrije več mest klicev.

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

Imena stolpcev se razlikujejo glede na izdajo: PostgreSQL 13 in novejši izpostavljajo mean_exec_time, starejše različice pa ga imenujejo mean_time, zato preverite z \d pg_stat_statements, če ta poizvedba vrne napako. Te vrednosti izključujejo načrtovanje in izključujejo pot do vašega odjemalca. Prav ta izključitev jih naredi uporabne za primerjavo.

Štejte poti tja in nazaj, ne le milisekund

Poročilo, sestavljeno iz dvesto majhnih odvisnih stavkov, pomnoži vsako zakasnitev, ki jo doda pot, z dvesto. En sam stavek, ki vrne milijon vrstic, plača to zakasnitev enkrat in nato dolgo plačuje pasovno širino. Oba sta v odjemalčevem časovniku videti enako in potrebujeta nasprotni popravek.

Izmerite število stavkov, namesto da bi ga ocenili. Preberite vsoto, enkrat zaženite poročilo, nato ga preberite znova in odštejte.

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

Če je delta velika in je vsak stavek na strežniku poceni, je paketna obdelava popravek, ki se izplača. Združite odvisne poizvedbe, uporabite en stavek, ki temelji na množicah, ali vodite zanko iz kazalca. V interaktivnem psql, \set FETCH_COUNT 1000 velik rezultat prihaja v paketih in spremeni vedenje prenosa in odjemalčevega pomnilnika. Ne zmanjša dela strežnika, zato ne bo rešil počasne poizvedbe.

Preverite tunel za ponovne prenose in zastoje MTU

Ko so majhni stavki hitri, veliki rezultati pa se ustavijo na pol poti, je poizvedba običajno v redu in pot ne. Tunel z nižjim MTU od osnovne povezave, skupaj z blokiranim ICMP, ustvari natanko tak vzorec: prvi paketi prispejo, veliki izginejo, prenos pa visi, dokler ne poskusi znova. Števci ponovnih prenosov na strani odjemalca to kažejo.

Opazujte vtičnico in števce jedra, medtem ko teče namerno počasen prenos. Vsote ponovnih prenosov so kumulativne, zato si jih zapišite pred in po, namesto da berete en vzorec.

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 prenosi, ki med prenosom naraščajo, skupaj s pingom, ki uspe pri 1400 bajtih, a spodleti pri 1472, kažejo na težavo z MTU in ne z bazo podatkov. Znižanje MTU vmesnika tunela ali stiskanje MSS na prehodu je običajni popravek in isti trije ukazi to pozneje potrdijo. Če ponovni prenosi ostanejo ravni, medtem ko je prenos še vedno počasen, poglejte pasovno širino, dovoljeno tunelu, in ne izgube.

Kratko zaporedje pokrije naslednje počasno poročilo: primerjajte en stavek lokalno, v LAN in prek VPN; preberite izvajanje strežnika iz EXPLAIN ali pg_stat_statements; preštejte klice pred eno izvedbo in po njej; in opazujte ponovne prenose, medtem ko se velik rezultat premika. Vsak korak izključi eno stran poti. Nobeden ne zahteva ugibanja, katero komponento znova zagnati.

Sorodni članki