Справката ви завършва за около секунда на хоста на базата данни и за четири през VPN, а числото, което psql отпечатва, сочи право към базата данни. Това число е хронометър от страна на клиента, а не измерване на сървъра. То покрива израза по пътя навън, чакането за отговор и редовете по пътя обратно. Разделянето на тези три е единственият начин да разберете коя страна да настроите.

Какво всъщност измерва таймерът на psql

\timing on стартира хронометър малко преди клиентът да запише израза в сокета и спира, когато последният ред от този резултат бъде прочетен. Собственото изпълнение на сървъра се намира някъде в този прозорец, както и всяка милисекунда, която пакетите прекарват в тунела. Нищо в изхода не ги разделя. EXPLAIN също отпечатва число, но него го произвежда сървърът и то изключва чакането на клиента. Две числа, два различни въпроса.

Стартирайте една сесия, оставете измерването включено и изпълнете израза, който ви интересува, отвътре в нея. Повторното използване на сесия държи цената на свързването извън първото измерване.

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

Числото все още включва преноса на резултата, което тук е желаното. То вече не включва обмена на свързване и удостоверяване, който ново извикване би добавило.

Сравнете един израз по три пътя

Изпълнете същия израз през Unix сокета, през LAN адреса и през името на VPN хоста и сравнете трите. Ако само последният е бавен, заявката не е променливата.

Всяко извикване първо се свързва и едва после измерва израза, така че цената на свързването остава извън числото, докато преносът остава вътре. Дръжте клаузата WHERE, броя редове и клиентската машина еднакви, иначе сравнявате работни натоварвания, а не пътища.

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 разликата обикновено е достатъчно малка, за да се пренебрегне; през VPN често не е. Подреждането на трите резултата после ви казва срещу кое чакане да тръгнете.

Бавният локален сокет означава работа на сървъра или планиране. Бавен LAN резултат означава заявката или обемът, който тя мести. Само бавният VPN резултат означава закъснение, MTU или налична честотна лента по пътя, и никакви промени в индексите не помагат.

Извадете собственото време за изпълнение на сървъра

Сървърът може да отчете колко време е прекарал и точно това число трябва да сравнявате с клиентското. EXPLAIN с ANALYZE изпълнява израза и отпечатва времето за планиране и изпълнение вътре в плана, а BUFFERS добавя поведение на кеша, което често обяснява топъл цикъл срещу студен. Не го насочвайте към изрази за запис, освен ако не възнамерявате да пишете. На реплика само за четене това е най-безопасното място за начало, и справката за psql документира обхвата на таймера от страна на клиента.

За изрази, които приложението вече изпълнява, pg_stat_statements обобщава изпълнението от страна на сървъра, без да пуска нищо отново. Редовете са за нормализирана заявка, така че един запис може да покрива няколко места на извикване.

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

Имената на колоните се различават според версията: PostgreSQL 13 и по-нови показват mean_exec_time, а по-старите версии го наричат mean_time, затова проверете с \d pg_stat_statements, ако тази заявка даде грешка. Тези стойности изключват планирането и изключват пътуването до вашия клиент. Именно това изключване ги прави полезни за сравнението.

Бройте обиколките, не само милисекундите

Справка, изградена от двеста малки зависими израза, умножава каквото закъснение добавя пътят по двеста. Един израз, който връща милион реда, плаща това закъснение веднъж и после плаща честотна лента дълго. И двете изглеждат еднакво в клиентски таймер и се нуждаят от противоположни поправки.

Измерете броя изрази, вместо да го оценявате. Прочетете общата стойност, пуснете справката веднъж, после я прочетете отново и извадете.

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

Ако делтата е голяма и всеки израз е евтин на сървъра, групирането е поправката, която се изплаща. Комбинирайте зависими заявки, използвайте един израз върху множества или водете цикъла от курсор. В интерактивния psql, \set FETCH_COUNT 1000 кара големия резултат да пристига на партиди и променя поведението на преноса и клиентската памет. Той не намалява работата на сървъра, затова няма да спаси бавна заявка.

Проверете тунела за повторни предавания и спирания на MTU

Когато малките изрази са бързи, а големите резултати спират по средата, заявката обикновено е наред, а пътят не е. Тунел, чието MTU е по-ниско от основната връзка, съчетан с блокиран ICMP, дава точно такъв модел: първите пакети пристигат, големите изчезват и преносът виси, докато не опита отново. Броячите за повторни предавания от страна на клиента го показват.

Наблюдавайте сокета и броячите на ядрото, докато тече умишлено бавен пренос. Сумите за повторни предавания са кумулативни, затова ги запишете преди и след, вместо да четете една проба.

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

Повторните предавания, които растат по време на преноса, заедно с ping, който успява при 1400 байта, но се проваля при 1472, сочат проблем с MTU, а не с базата данни. Намаляването на MTU на интерфейса на тунела или затягането на MSS на шлюза е обичайната поправка и същите три команди я потвърждават след това. Ако повторните предавания остават плоски, докато преносът все още е бавен, гледайте честотната лента, разрешена на тунела, а не загубите.

Кратка последователност покрива следващата бавна справка: сравнете един израз локално, в LAN и през VPN; прочетете изпълнението на сървъра от EXPLAIN или pg_stat_statements; пребройте извикванията преди и след едно пускане; и наблюдавайте повторните предавания, докато големият резултат се движи. Всяка стъпка изключва една страна на пътя. Никоя от тях не изисква да гадаете кой компонент да рестартирате.

Подобни статии