Raporttisi valmistuu noin sekunnissa tietokantaisännällä ja neljässä VPN:n yli, ja luku, jonka psql tulostaa, osoittaa suoraan tietokantaan. Se luku on asiakaspuolen sekuntikello, ei palvelimen mittaus. Se kattaa lauseen matkalla ulos, vastauksen odotuksen ja rivit matkalla takaisin. Näiden kolmen erottaminen on ainoa tapa tietää, kumpaa puolta virittää.

Mitä psql:n ajastin todella mittaa

\timing on käynnistää sekuntikellon juuri ennen kuin asiakas kirjoittaa lauseen liitäntään, ja pysäyttää sen, kun kyseisen tuloksen viimeinen rivi on luettu. Palvelimen oma suoritus on jossain tämän ikkunan sisällä, samoin kuin jokainen millisekunti, jonka paketit viettävät tunnelissa. Mikään tulosteessa ei erota niitä. EXPLAIN tulostaa myös luvun, mutta sen tuottaa palvelin ja se jättää asiakkaan odotuksen pois. Kaksi lukua, kaksi eri kysymystä.

Aloita yksi istunto, jätä ajoitus päälle ja suorita kiinnostava lause sen sisältä. Istunnon uudelleenkäyttö pitää yhdistämiskulun poissa ensimmäisestä mittauksesta.

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

Luku sisältää edelleen tuloksen siirron, mikä on tässä se, mitä haluat. Se ei enää sisällä yhdistämis- ja todennusvaihtoa, jonka uusi kutsu lisäisi.

Vertaa yhtä lausetta kolmella reitillä

Suorita sama lause Unix-liitännän, LAN-osoitteen ja VPN-isäntänimen kautta ja vertaa kolmea. Jos vain viimeinen on hidas, kysely ei ole muuttuja.

Jokainen kutsu yhdistää ensin ja mittaa lauseen vasta sitten, joten yhdistämiskulu pysyy luvun ulkopuolella ja siirto sen sisällä. Pidä WHERE-lauseke, rivimäärä ja asiakaskone samana, muuten vertaat työkuormia etkä reittejä.

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

LAN:ssa ero on yleensä tarpeeksi pieni sivuutettavaksi; VPN:n yli usein ei. Kolmen tuloksen lajittelu kertoo sitten, mitä odotusta vastaan hyökätä.

Hidas paikallinen liitäntä tarkoittaa palvelimen työtä tai suunnittelua. Hidas LAN-tulos tarkoittaa kyselyä tai sen siirtämää määrää. Vain hidas VPN-tulos tarkoittaa viivettä, MTU:ta tai käytettävissä olevaa kaistanleveyttä reitillä, eikä mikään indeksimuutos korjaa sitä.

Ota palvelimen oma suoritusaika esiin

Palvelin voi ilmoittaa, paljonko aikaa se käytti, ja juuri sitä lukua on verrattava asiakkaan lukuun. EXPLAIN yhdessä ANALYZE:n kanssa suorittaa lauseen ja tulostaa suunnittelu- ja suoritusajan suunnitelman sisään, kun taas BUFFERS lisää välimuistikäyttäytymisen, joka usein selittää lämpimän ajon kylmää vastaan. Älä kohdista sitä kirjoittaviin lauseisiin, ellet aio kirjoittaa. Vain lukuun käytettävässä replikassa se on turvallisin aloituspaikka, ja psql-viite dokumentoi ajastimen asiakaspuolen laajuuden.

Lauseille, joita sovellus jo suorittaa, pg_stat_statements kokoaa palvelinpuolen suorituksen suorittamatta mitään uudelleen. Rivit ovat normalisoitua kyselyä kohti, joten yksi merkintä voi kattaa useita kutsukohtia.

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

Sarakkeiden nimet vaihtelevat julkaisun mukaan: PostgreSQL 13 ja uudemmat näyttävät mean_exec_time, kun taas vanhemmat versiot kutsuvat sitä mean_time, joten tarkista \d pg_stat_statements, jos tämä kysely antaa virheen. Nämä arvot jättävät suunnittelun pois ja jättävät matkan asiakkaallesi pois. Juuri tuo poisjättö tekee niistä hyödyllisiä vertailulle.

Laske edestakaiset matkat, älä vain millisekunteja

Kahdestasadasta pienestä riippuvasta lauseesta rakennettu raportti kertoo reitin lisäämän viiveen kahdellasadalla. Yksi lause, joka palauttaa miljoona riviä, maksaa viiveen kerran ja maksaa sitten kaistanleveyttä pitkään. Molemmat näyttävät samalta asiakkaan ajastimessa, ja ne tarvitsevat vastakkaiset korjaukset.

Mittaa lauseiden määrä arvaamisen sijaan. Lue kokonaismäärä, aja raportti kerran, lue se sitten uudelleen ja vähennä.

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

Jos delta on suuri ja jokainen lause on palvelimella halpa, eräajo on korjaus, joka kannattaa. Yhdistä riippuvat kyselyt, käytä yhtä joukkopohjaista lausetta tai aja silmukka kursorista. Interaktiivisessa psql:ssä, \set FETCH_COUNT 1000 saa suuren tuloksen saapumaan erissä ja muuttaa siirron ja asiakkaan muistin käyttäytymistä. Se ei vähennä palvelimen työtä, joten se ei pelasta hidasta kyselyä.

Tarkista tunneli uudelleenlähetyksistä ja MTU-pysähdyksistä

Kun pienet lauseet ovat nopeita ja suuret tulokset pysähtyvät puoliväliin, kysely on yleensä kunnossa ja reitti ei. Tunneli, jonka MTU on alempi kuin alla oleva linkki, yhdessä estetyn ICMP:n kanssa, tuottaa juuri sellaisen kuvion: ensimmäiset paketit saapuvat, suuret katoavat ja siirto roikkuu, kunnes se yrittää uudelleen. Asiakaspuolen uudelleenlähetyslaskurit näyttävät sen.

Seuraa liitäntää ja ytimen laskureita, kun tahallisen hidas siirto on käynnissä. Uudelleenlähetysten summat ovat kumulatiivisia, joten kirjaa ne ennen ja jälkeen sen sijaan että lukisit yhden näytteen.

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

Siirron aikana kasvavat uudelleenlähetykset yhdessä pingin kanssa, joka onnistuu 1400 tavulla mutta epäonnistuu 1472:lla, viittaavat MTU-ongelmaan eikä tietokantaongelmaan. Tunneliliitännän MTU:n laskeminen tai MSS:n kiristäminen yhdyskäytävässä on tavallinen korjaus, ja samat kolme komentoa vahvistavat sen jälkeen. Jos uudelleenlähetykset pysyvät tasaisina siirron ollessa yhä hidas, katso tunnelille sallittua kaistanleveyttä äläkä häviötä.

Lyhyt sarja kattaa seuraavan hitaan raportin: vertaa yhtä lausetta paikallisesti, LAN:ssa ja VPN:n yli; lue palvelimen suoritus EXPLAIN:sta tai pg_stat_statements:sta; laske kutsut ennen yhtä ajoa ja sen jälkeen; ja seuraa uudelleenlähetyksiä suuren tuloksen liikkuessa. Jokainen vaihe sulkee pois yhden puolen reitistä. Mikään niistä ei vaadi arvaamista, mikä komponentti käynnistetään uudelleen.

Aiheeseen liittyvät artikkelit