თქვენი ანგარიში დაახლოებით ერთ წამში სრულდება მონაცემთა ბაზის ჰოსტზე და ოთხში 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-იდან; დათვალეთ გამოძახებები ერთი გაშვების წინ და შემდეგ; და უყურეთ განმეორებით გაგზავნებს, სანამ დიდი შედეგი მოძრაობს. ყოველი ნაბიჯი გზის ერთ მხარეს გამორიცხავს. არც ერთი მათგანი არ მოითხოვს გამოცნობას, რომელი კომპონენტი გადატვირთოთ.

დაკავშირებული სტატიები