Inżynier - przetłumaczony z łaciny - inspirowany.
Inżynier może zrobić wszystko. (c) R. Diesel.
Epigrafy.
Albo opowieść o tym, dlaczego administrator bazy danych musi pamiętać o swojej programistycznej przeszłości.
Przedmowa
Wszystkie imiona zostały zmienione. Dopasowania są losowe. Materiał jest wyłącznie prywatną opinią autora.
Zrzeczenie się gwarancji: w planowanej serii artykułów zabraknie szczegółowego i dokładnego opisu zastosowanych tabel i skryptów. Materiały nie mogą być natychmiast używane „tak jak są”.
Po pierwsze, ze względu na dużą ilość materiału,
po drugie, ze względu na ostrość z bazą produkcyjną prawdziwego klienta.
Dlatego w artykułach podane zostaną tylko idee i opisy w najbardziej ogólnej formie.
Może w przyszłości system urośnie do poziomu publikowania na GitHubie, a może nie. Czas pokaże.
Początek historii-
Co się w rezultacie stało, mówiąc najogólniej…
Po co mi to wszystko?
Cóż, po pierwsze, aby nie zapomnieć o sobie, pamiętając chwalebne dni na emeryturze.
Po drugie, aby usystematyzować to, co zostało napisane. Już sam czasem zaczynam się gubić i zapominać o poszczególnych częściach.
No i co najważniejsze - nagle może się komuś przydać i pomóc nie wymyślać koła na nowo i nie zbierać prowizji. Innymi słowy, popraw swoją karmę (nie Chabrowskiego). Bo najcenniejszą rzeczą na tym świecie są idee. Najważniejsze to znaleźć pomysł. A przełożenie pomysłu na rzeczywistość jest już kwestią czysto techniczną.
Więc zacznijmy powoli...
Sformułowanie problemu.
Do dyspozycji:
PostgreSQL(10.5), obciążenie mieszane (OLTP+DSS), obciążenie średnie do małego, hostowane w chmurze AWS.
Nie ma monitoringu bazy danych, monitoring infrastruktury jest przedstawiony jako standardowe narzędzia AWS w minimalnej konfiguracji.
Wymagane:
Monitoruj wydajność i stan bazy danych, znajduj i otrzymuj wstępne informacje w celu optymalizacji ciężkich zapytań do bazy danych.
Krótkie wprowadzenie lub analiza rozwiązań
Na początek spróbujmy przeanalizować możliwości rozwiązania problemu z punktu widzenia analizy porównawczej korzyści i kłopotów dla inżyniera, a korzyściami i stratami niech zajmą się ci, którzy mają być na liście pracowników zarządzania.
Wariant 1 – „Praca na żądanie”
Zostawiamy wszystko tak jak jest. Jeśli klient nie jest zadowolony z czegoś w stanie, wydajności bazy danych lub aplikacji, powiadomi o tym inżynierów DBA e-mailem lub tworząc incydent w kasie biletowej.
Inżynier, otrzymawszy zgłoszenie, zrozumie problem, zaproponuje rozwiązanie lub odłoży problem na półkę, mając nadzieję, że wszystko samo się rozwiąże, a poza tym wszystko wkrótce pójdzie w niepamięć.
Pierniki i pączki, siniaki i guzyPierniki i pączki:
1. Nic więcej do roboty
2. Zawsze istnieje możliwość wyjścia i ubrudzenia się.
3. Dużo czasu, który możesz poświęcić samemu.
Siniaki i guzy:
1. Prędzej czy później klient zastanowi się nad istotą bytu i powszechnej sprawiedliwości na tym świecie i po raz kolejny zada sobie pytanie - po co im płacę swoje pieniądze? Konsekwencja jest zawsze taka sama – pytanie tylko, kiedy klient się znudzi i pomacha na pożegnanie. A podajnik jest pusty. To jest smutne.
2. Rozwój inżyniera jest zerowy.
3. Trudności w harmonogramowaniu pracy i załadunków
Opcja 2 - „Zatańcz z tamburynami, załóż i załóż buty”
Ustęp 1-Dlaczego potrzebujemy systemu monitoringu, otrzymamy wszystkie zapytania. Uruchamiamy szereg różnego rodzaju zapytań do słownika danych i widoków dynamicznych, włączamy wszelkiego rodzaju liczniki, umieszczamy wszystko w tabelach, okresowo analizujemy listy i tabele, jak gdyby. W rezultacie mamy piękne lub niezbyt ładne wykresy, tabele, raporty. Najważniejsze - to byłoby więcej, więcej.
Ustęp 2-Generuj aktywność - uruchom analizę tego wszystkiego.
Ustęp 3- Przygotowujemy pewien dokument, nazywamy ten dokument po prostu - "jak wyposażamy bazę danych".
Ustęp 4- Klient, widząc całą tę wspaniałość wykresów i cyfr, jest w dziecinnej naiwnej ufności - teraz wszystko będzie działać dla nas, wkrótce. I łatwo i bezboleśnie rozstać się ze swoimi zasobami finansowymi. Kierownictwo jest również pewne, że nasi inżynierowie ciężko pracują. Maksymalne ładowanie.
Ustęp 5- Regularnie powtarzaj krok 1.
Pierniki i pączki, siniaki i guzyPierniki i pączki:
1. Życie menedżerów i inżynierów jest proste, przewidywalne i pełne aktywności. Wszystko tętni życiem, wszyscy są zajęci.
2. Życie klienta też nie jest złe - zawsze jest pewien, że trzeba trochę uzbroić się w cierpliwość i wszystko się ułoży. Nie coraz lepiej, no, no - ten świat jest niesprawiedliwy, w następnym życiu - szczęście.
Siniaki i guzy:
1. Prędzej czy później pojawi się sprytniejszy dostawca podobnej usługi, który zrobi to samo, ale trochę taniej. A jeśli wynik jest taki sam, po co płacić więcej. Co znowu doprowadzi do zniknięcia podajnika.
2. To nudne. Jak nudna jest każda mało znacząca czynność.
3. Tak jak w poprzedniej wersji - brak rozbudowy. Ale dla inżyniera minusem jest to, że w przeciwieństwie do pierwszej opcji tutaj musisz stale generować IDB. A to wymaga czasu. Które można wydać na rzecz bliskiej osoby. Ponieważ nie możesz zadbać o siebie, wszyscy troszczą się o ciebie.
Wariant 3 – Nie trzeba wymyślać roweru, trzeba go kupić i jeździć.
Inżynierowie z innych firm świadomie jedzą pizzę z piwem (ach, te wspaniałe czasy Petersburga lat 90.). Korzystajmy z systemów monitoringu, które są zrobione, debugowane i działają i ogólnie rzecz biorąc, przynoszą korzyści (no, przynajmniej ich twórcom).
Pierniki i pączki, siniaki i guzyPierniki i pączki:
1. Nie trzeba tracić czasu na wymyślanie tego, co już zostało wynalezione. Bierz i używaj.
2. Systemy monitorowania nie są pisane przez głupców i oczywiście są przydatne.
3. Działające systemy monitorowania zazwyczaj dostarczają użytecznych przefiltrowanych informacji.
Siniaki i guzy:
1. Inżynier w tym przypadku nie jest inżynierem, a jedynie użytkownikiem czyjegoś produktu.
2. Klient musi być przekonany o konieczności zakupu czegoś, czego generalnie nie chce rozumieć, a nie powinien, a generalnie budżet na dany rok został zatwierdzony i nie ulegnie zmianie. Następnie musisz przydzielić osobny zasób, skonfigurować go dla określonego systemu. Te. Najpierw trzeba zapłacić, zapłacić i jeszcze raz zapłacić. A klient jest skąpy. To jest norma tego życia.
Co robić, Czernyszewski? Twoje pytanie jest bardzo trafne. (Z)
W tym konkretnym przypadku i obecnej sytuacji można zrobić trochę inaczej - zróbmy własny system monitoringu.
Cóż, nie system, oczywiście w pełnym tego słowa znaczeniu, jest to zbyt głośne i zarozumiałe, ale przynajmniej jakoś ułatwić sobie i zebrać więcej informacji, aby rozwiązać incydenty wydajnościowe. Aby nie znaleźć się w sytuacji - „idź tam, nie wiem gdzie, znajdź to, nie wiem co”.
Jakie są plusy i minusy tej opcji:
Plusy:
1. To interesujące. Cóż, przynajmniej bardziej interesujące niż ciągłe „zmniejszanie pliku danych, zmiana obszaru tabel itp.”
2. Są to nowe umiejętności i nowy rozwój. Które w przyszłości prędzej czy później dadzą zasłużone pierniki i pączki.
Wady:
1. Muszę pracować. Dużo pracować.
2. Będziesz musiał regularnie wyjaśniać znaczenie i perspektywy wszystkich działań.
3. Coś trzeba będzie poświęcić, ponieważ jedyny zasób dostępny dla inżyniera - czas - jest ograniczony przez Wszechświat.
4. Najgorszy i najbardziej nieprzyjemny - w rezultacie mogą pojawić się śmieci typu „Nie mysz, nie żaba, ale nieznane małe zwierzątko”.
Kto nie ryzykuje, ten nie pije szampana.
A więc zaczyna się zabawa.
Ogólny pomysł - schematyczny
(Ilustracja zaczerpnięta z artykułu «
Objaśnienie:
- Docelowa baza danych jest instalowana ze standardowym rozszerzeniem PostgreSQL „pg_stat_statements”.
- W bazie danych monitoringu tworzymy zestaw tabel serwisowych do przechowywania historii pg_stat_statements na początkowym etapie oraz do konfiguracji metryk i monitoringu w przyszłości
- Na hoście monitorującym tworzymy zestaw skryptów bash, w tym do generowania incydentów w systemie ticketowym.
Stoły serwisowe
Na początek schematycznie uproszczony ERD, co się w końcu stało:
Krótki opis tabelPunkt końcowy - host, punkt połączenia z instancją
baza danych - opcje bazy danych
pg_stat_historia - tabela historyczna do przechowywania tymczasowych migawek widoku pg_stat_statements docelowej bazy danych
metryka_glosariusz - Słownik wskaźników wydajności
konfiguracja_metryki - konfiguracja poszczególnych metryk
metryczny - konkretna metryka dla żądania, które jest monitorowane
metryka_historia_alert - historia ostrzeżeń dotyczących wydajności
zapytanie_dziennika - tabela usług do przechowywania przeanalizowanych rekordów z pliku dziennika PostgreSQL pobranego z AWS
bazowy - parametry okresu czasu stanowiącego podstawę
punkt kontrolny - konfiguracja metryk do sprawdzania stanu bazy danych
checkpoint_alert_historia - historia ostrzeżeń o metrykach sprawdzania stanu bazy danych
pg_stat_db_queries — tabela usług aktywnych żądań
Dziennik aktywności — tabela usługi dziennika aktywności
trap_oid - tabela serwisowa konfiguracji pułapek
Etap 1 - zbieraj statystyki wydajności i otrzymuj raporty
Tabela służy do przechowywania informacji statystycznych. pg_stat_historia
struktura tabeli pg_stat_history
Tabela "public.pg_stat_history" Kolumna | wpisz | Modyfikatory ----------------------------------+------ --+---- -------------------------------- identyfikator | liczba całkowita | nie null domyślnie nextval('pg_stat_history_id_seq'::regclass) snapshot_timestamp | znacznik czasu bez strefy czasowej | identyfikator_bazy danych | liczba całkowita | dbid | oid | identyfikator użytkownika | oid | identyfikator zapytania | bigint | zapytanie | tekst | rozmowy | bigint | całkowity_czas | podwójna precyzja | min_czas | podwójna precyzja | maksymalny_czas | podwójna precyzja | średni_czas | podwójna precyzja | stddev_czas | podwójna precyzja | rzędy | bigint | wspólne_blks_hit | bigint | shared_blks_read | bigint | shared_blks_dirtied | bigint | udostępnione_blks_pisane | bigint | local_blks_hit | bigint | local_blks_read | bigint | local_blks_dirtyed | bigint | local_blks_pisane | bigint | temp_blks_read | bigint | temp_blks_pisane | bigint | blk_read_time | podwójna precyzja | blk_write_time | podwójna precyzja | identyfikator_linii bazowej | liczba całkowita | Indeksy: "pg_stat_history_pkey" PRIMARY KEY, btree (id) "database_idx" btree (database_id) "queryid_idx" btree (queryid) "snapshot_timestamp_idx" btree (snapshot_timestamp) Ograniczenia klucza obcego: "database_id_fk" KLUCZ OBCY (database_id) REFERENCJE baza danych (id ) NA USUŃ KASKADĘ
Jak widać, tabela to tylko zbiorcze dane widoku pg_stat_statements w docelowej bazie danych.
Korzystanie z tej tabeli jest bardzo proste.
pg_stat_historia będzie reprezentować skumulowane statystyki wykonania zapytania dla każdej godziny. Na początku każdej godziny, po wypełnieniu tabeli, statystyki pg_stat_statements zresetować za pomocą pg_stat_statements_reset().
Uwaga: statystyki są zbierane dla żądań o czasie trwania dłuższym niż 1 sekunda.
Zapełnianie tabeli pg_stat_history
--pg_stat_history.sql
CREATE OR REPLACE FUNCTION pg_stat_history( ) RETURNS boolean AS $$
DECLARE
endpoint_rec record ;
database_rec record ;
pg_stat_snapshot record ;
current_snapshot_timestamp timestamp without time zone;
BEGIN
current_snapshot_timestamp = date_trunc('minute',now());
FOR endpoint_rec IN SELECT * FROM endpoint
LOOP
FOR database_rec IN SELECT * FROM database WHERE endpoint_id = endpoint_rec.id
LOOP
RAISE NOTICE 'NEW SHAPSHOT IS CREATING';
--Connect to the target DB
EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||endpoint_rec.host||' dbname='||database_rec.name||' user=USER password=PASSWORD '')';
RAISE NOTICE 'host % and dbname % ',endpoint_rec.host,database_rec.name;
RAISE NOTICE 'Creating snapshot of pg_stat_statements for database %',database_rec.name;
SELECT
*
INTO
pg_stat_snapshot
FROM dblink('LINK1',
'SELECT
dbid , SUM(calls),SUM(total_time),SUM(rows) ,SUM(shared_blks_hit) ,SUM(shared_blks_read) ,SUM(shared_blks_dirtied) ,SUM(shared_blks_written) ,
SUM(local_blks_hit) , SUM(local_blks_read) , SUM(local_blks_dirtied) , SUM(local_blks_written) , SUM(temp_blks_read) , SUM(temp_blks_written) , SUM(blk_read_time) , SUM(blk_write_time)
FROM pg_stat_statements WHERE dbid=(SELECT oid from pg_database where datname=current_database() )
GROUP BY dbid
'
)
AS t
( dbid oid , calls bigint ,
total_time double precision ,
rows bigint , shared_blks_hit bigint , shared_blks_read bigint ,shared_blks_dirtied bigint ,shared_blks_written bigint ,
local_blks_hit bigint ,local_blks_read bigint , local_blks_dirtied bigint ,local_blks_written bigint ,
temp_blks_read bigint ,temp_blks_written bigint ,
blk_read_time double precision , blk_write_time double precision
);
INSERT INTO pg_stat_history
(
snapshot_timestamp ,database_id ,
dbid , calls ,total_time ,
rows ,shared_blks_hit ,shared_blks_read ,shared_blks_dirtied ,shared_blks_written ,local_blks_hit ,
local_blks_read,local_blks_dirtied,local_blks_written,temp_blks_read,temp_blks_written,
blk_read_time, blk_write_time
)
VALUES
(
current_snapshot_timestamp ,
database_rec.id ,
pg_stat_snapshot.dbid ,pg_stat_snapshot.calls,
pg_stat_snapshot.total_time,
pg_stat_snapshot.rows ,pg_stat_snapshot.shared_blks_hit ,pg_stat_snapshot.shared_blks_read ,pg_stat_snapshot.shared_blks_dirtied ,pg_stat_snapshot.shared_blks_written ,
pg_stat_snapshot.local_blks_hit , pg_stat_snapshot.local_blks_read , pg_stat_snapshot.local_blks_dirtied , pg_stat_snapshot.local_blks_written ,
pg_stat_snapshot.temp_blks_read , pg_stat_snapshot.temp_blks_written , pg_stat_snapshot.blk_read_time , pg_stat_snapshot.blk_write_time
);
RAISE NOTICE 'Creating snapshot of pg_stat_statements for queries with min_time more than 1000ms';
FOR pg_stat_snapshot IN
--All queries with max_time greater than 1000 ms
SELECT
*
FROM dblink('LINK1',
'SELECT
dbid , userid ,queryid,query,calls,total_time,min_time ,max_time,mean_time, stddev_time ,rows ,shared_blks_hit ,
shared_blks_read ,shared_blks_dirtied ,shared_blks_written ,
local_blks_hit , local_blks_read , local_blks_dirtied ,
local_blks_written , temp_blks_read , temp_blks_written , blk_read_time ,
blk_write_time
FROM pg_stat_statements
WHERE dbid=(SELECT oid from pg_database where datname=current_database() AND min_time >= 1000 )
'
)
AS t
( dbid oid , userid oid , queryid bigint ,query text , calls bigint ,
total_time double precision ,min_time double precision ,max_time double precision , mean_time double precision , stddev_time double precision ,
rows bigint , shared_blks_hit bigint , shared_blks_read bigint ,shared_blks_dirtied bigint ,shared_blks_written bigint ,
local_blks_hit bigint ,local_blks_read bigint , local_blks_dirtied bigint ,local_blks_written bigint ,
temp_blks_read bigint ,temp_blks_written bigint ,
blk_read_time double precision , blk_write_time double precision
)
LOOP
INSERT INTO pg_stat_history
(
snapshot_timestamp ,database_id ,
dbid ,userid , queryid , query , calls ,total_time ,min_time ,max_time ,mean_time ,stddev_time ,
rows ,shared_blks_hit ,shared_blks_read ,shared_blks_dirtied ,shared_blks_written ,local_blks_hit ,
local_blks_read,local_blks_dirtied,local_blks_written,temp_blks_read,temp_blks_written,
blk_read_time, blk_write_time
)
VALUES
(
current_snapshot_timestamp ,
database_rec.id ,
pg_stat_snapshot.dbid ,pg_stat_snapshot.userid ,pg_stat_snapshot.queryid,pg_stat_snapshot.query,pg_stat_snapshot.calls,
pg_stat_snapshot.total_time,pg_stat_snapshot.min_time ,pg_stat_snapshot.max_time,pg_stat_snapshot.mean_time, pg_stat_snapshot.stddev_time ,
pg_stat_snapshot.rows ,pg_stat_snapshot.shared_blks_hit ,pg_stat_snapshot.shared_blks_read ,pg_stat_snapshot.shared_blks_dirtied ,pg_stat_snapshot.shared_blks_written ,
pg_stat_snapshot.local_blks_hit , pg_stat_snapshot.local_blks_read , pg_stat_snapshot.local_blks_dirtied , pg_stat_snapshot.local_blks_written ,
pg_stat_snapshot.temp_blks_read , pg_stat_snapshot.temp_blks_written , pg_stat_snapshot.blk_read_time , pg_stat_snapshot.blk_write_time
);
END LOOP;
PERFORM dblink_disconnect('LINK1');
END LOOP ;--FOR database_rec IN SELECT * FROM database WHERE endpoint_id = endpoint_rec.id
END LOOP;
RETURN TRUE;
END
$$ LANGUAGE plpgsql;
W rezultacie po pewnym czasie w tabeli pg_stat_historia będziemy mieli zestaw migawek zawartości tabeli pg_stat_statements docelowa baza danych.
Właściwie raportowanie
Za pomocą prostych zapytań można uzyskać całkiem przydatne i ciekawe raporty.
Dane zagregowane za dany okres czasu
Zapytanie
SELECT
database_id ,
SUM(calls) AS calls ,SUM(total_time) AS total_time ,
SUM(rows) AS rows , SUM(shared_blks_hit) AS shared_blks_hit,
SUM(shared_blks_read) AS shared_blks_read ,
SUM(shared_blks_dirtied) AS shared_blks_dirtied,
SUM(shared_blks_written) AS shared_blks_written ,
SUM(local_blks_hit) AS local_blks_hit ,
SUM(local_blks_read) AS local_blks_read ,
SUM(local_blks_dirtied) AS local_blks_dirtied ,
SUM(local_blks_written) AS local_blks_written,
SUM(temp_blks_read) AS temp_blks_read,
SUM(temp_blks_written) temp_blks_written ,
SUM(blk_read_time) AS blk_read_time ,
SUM(blk_write_time) AS blk_write_time
FROM
pg_stat_history
WHERE
queryid IS NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY database_id ;
Czas DB
to_char(interwał '1 milisekunda' * pg_total_stat_history_rec.total_time, 'HH24:MI:SS.MS')
Czas wejścia/wyjścia
to_char(interval '1 milisekunda' * ( pg_total_stat_history_rec.blk_read_time + pg_total_stat_history_rec.blk_write_time ), 'HH24:MI:SS.MS')
TOP10 SQL według total_time
Zapytanie
SELECT
queryid ,
SUM(calls) AS calls ,
SUM(total_time) AS total_time
FROM
pg_stat_history
WHERE
queryid IS NOT NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY queryid
ORDER BY 3 DESC
LIMIT 10
-------------------------------------------------- ------------------------------------ | TOP10 SQL WEDŁUG CAŁKOWITEGO CZASU WYKONANIA | #| zapytanie| połączenia| wzywa %| całkowity_czas (ms) | czas baz danych % +----+------------+-----------+-----------+------ -----+---------- | 1| 821760255| 2| .00001|00:03:23.141(203141.681 ms.)| 5.42 | 2| 4152624390| 2| .00001|00:03:13.929(193929.215 ms.)| 5.17 | 3| 1484454471| 4| .00001|00:02:09.129( 129129.057 ms.)| 3.44 | 4| 655729273| 1| .00000|00:02:01.869( 121869.981 ms.)| 3.25 | 5| 2460318461| 1| .00000|00:01:33.113(93113.835 ms.)| 2.48 | 6| 2194493487| 4| .00001|00:00:17.377( 17377.868 ms.)| .46 | 7| 1053044345| 1| .00000|00:00:06.156(6156.352 ms.)| .16 | 8| 3644780286| 1| .00000|00:00:01.063( 1063.830 ms.)| 03
TOP10 SQL według całkowitego czasu we/wy
Zapytanie
SELECT
queryid ,
SUM(calls) AS calls ,
SUM(blk_read_time + blk_write_time) AS io_time
FROM
pg_stat_history
WHERE
queryid IS NOT NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY queryid
ORDER BY 3 DESC
LIMIT 10
-------------------------------------------------- -------------------------------------- | TOP10 SQL WEDŁUG CAŁKOWITEGO CZASU WE/WY | #| zapytanie| połączenia| wzywa %| Czas we/wy (ms)|db Czas we/wy % +----+------------+-----------+------ -----+--------------------------------+----------- -- | 1| 4152624390| 2| .00001|00:08:31.616(511616.592 ms.)| 31.06 czerwca | 2| 821760255| 2| .00001|00:08:27.099(507099.036 ms.)| 30.78 | 3| 655729273| 1| .00000|00:05:02.209(302209.137 ms.)| 18.35 | 4| 2460318461| 1| .00000|00:04:05.981(245981.117 ms.)| 14.93 | 5| 1484454471| 4| .00001|00:00:39.144(39144.221 ms.)| 2.38 | 6| 2194493487| 4| .00001|00:00:18.182( 18182.816 ms.)| 1.10 | 7| 1053044345| 1| .00000|00:00:16.611( 16611.722 ms.)| 1.01 | 8| 3644780286| 1| .00000|00:00:00.436( 436.205 ms.)| 03
TOP10 SQL według maksymalnego czasu wykonania
Zapytanie
SELECT
id AS snapshotid ,
queryid ,
snapshot_timestamp ,
max_time
FROM
pg_stat_history
WHERE
queryid IS NOT NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
ORDER BY 4 DESC
LIMIT 10
-------------------------------------------------- ------------------------------------ | TOP10 SQL WEDŁUG MAKSYMALNEGO CZASU WYKONANIA | #| migawka| identyfikator migawki| zapytanie| maks_czas (ms) +--------+-----------+--------- --+------------------------------------------------------- | 1| 05.04.2019 01:03| 4169| 655729273| 00:02:01.869(121869.981 ms) | 2| 04.04.2019 17:00| 4153| 821760255| 00:01:41.570(101570.841 ms.) | 3| 04.04.2019 16:00| 4146| 821760255| 00:01:41.570(101570.841 ms.) | 4| 04.04.2019 16:00| 4144| 4152624390| 00:01:36.964(96964.607 ms) | 5| 04.04.2019 17:00| 4151| 4152624390| 00:01:36.964(96964.607 ms) | 6| 05.04.2019 10:00 | 4188| 1484454471| 00:01:33.452(93452.150 ms.) | 7| 04.04.2019 17:00| 4150| 2460318461| 00:01:33.113(93113.835 ms.) | 8| 04.04.2019 15:00| 4140| 1484454471| 00:00:11.892(11892.302 ms) | 9| 04.04.2019 16:00| 4145| 1484454471| 00:00:11.892(11892.302 ms) | 10| 04.04.2019 17:00| 4152| 1484454471| 00:00:11.892( 11892.302 ms.)
TOP10 SQL przez bufor SHARED odczyt/zapis
Zapytanie
SELECT
id AS snapshotid ,
queryid ,
snapshot_timestamp ,
shared_blks_read ,
shared_blks_written
FROM
pg_stat_history
WHERE
queryid IS NOT NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND
( shared_blks_read > 0 OR shared_blks_written > 0 )
ORDER BY 4 DESC , 5 DESC
LIMIT 10
-------------------------------------------------- ------------------------------------ | TOP10 SQL PRZEZ WSPÓLNY BUFOR ODCZYT/ZAPIS | #| migawka| identyfikator migawki| zapytanie| wspólne bloki odczyt| wspólne bloki napisz +----+----+-----------+---------- -+---------------------+--------------------- | 1| 04.04.2019 17:00| 4153| 821760255| 797308| 0 | 2| 04.04.2019 16:00| 4146| 821760255| 797308| 0 | 3| 05.04.2019 01:03| 4169| 655729273| 797158| 0 | 4| 04.04.2019 16:00| 4144| 4152624390| 756514| 0 | 5| 04.04.2019 17:00| 4151| 4152624390| 756514| 0 | 6| 04.04.2019 17:00| 4150| 2460318461| 734117| 0 | 7| 04.04.2019 17:00| 4155| 3644780286| 52973| 0 | 8| 05.04.2019 01:03| 4168| 1053044345| 52818| 0 | 9| 04.04.2019 15:00| 4141| 2194493487| 52813| 0 | 10| 04.04.2019 16:00| 4147| 2194493487| 52813| 0 -------------------------------------------------- --------------------------------------------------
Histogram rozkładu zapytań według maksymalnego czasu wykonania
wnioski
SELECT
MIN(max_time) AS hist_min ,
MAX(max_time) AS hist_max ,
(( MAX(max_time) - MIN(min_time) ) / hist_columns ) as hist_width
FROM
pg_stat_history
WHERE
queryid IS NOT NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT ;
SELECT
SUM(calls) AS calls
FROM
pg_stat_history
WHERE
queryid IS NOT NULL AND
database_id =DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND
( max_time >= hist_current_min AND max_time < hist_current_max ) ;
|------------------------------------------------- ---------------------------------------- | HISTOGRAM MAX_TIME | CAŁKOWITA POŁĄCZENIA : 33851920 | MIN. CZAS : 00:00:01.063 | MAKSYMALNY CZAS : 00:02:01.869 ---------------------------------- -------- ---------------------------- | minimalny czas trwania| maksymalny czas trwania| dzwoni +-------------------------------------------------+------------- ------+---------- | 00:00:01.063( 1063.830 ms.) | 00:00:13.144( 13144.445 ms.) | 9 | 00:00:13.144( 13144.445 ms.) | 00:00:25.225( 25225.060 ms.) | 0 | 00:00:25.225( 25225.060 ms.) | 00:00:37.305(37305.675 ms) | 0 | 00:00:37.305(37305.675 ms) | 00:00:49.386(49386.290 ms.) | 0 | 00:00:49.386(49386.290 ms.) | 00:01:01.466(61466.906 ms.) | 0 | 00:01:01.466(61466.906 ms.) | 00:01:13.547(73547.521 ms) | 0 | 00:01:13.547(73547.521 ms) | 00:01:25.628( 85628.136 ms.) | 0 | 00:01:25.628( 85628.136 ms.) | 00:01:37.708(97708.751 ms) | 4 | 00:01:37.708(97708.751 ms) | 00:01:49.789(109789.366 ms) | 2 | 00:01:49.789(109789.366 ms) | 00:02:01.869(121869.981 ms) | 0
TOP10 migawek według zapytań na sekundę
wnioski
--pg_qps.sql
--Calculate Query Per Second
CREATE OR REPLACE FUNCTION pg_qps( pg_stat_history_id integer ) RETURNS double precision AS $$
DECLARE
pg_stat_history_rec record ;
prev_pg_stat_history_id integer ;
prev_pg_stat_history_rec record;
total_seconds double precision ;
result double precision;
BEGIN
result = 0 ;
SELECT *
INTO pg_stat_history_rec
FROM
pg_stat_history
WHERE id = pg_stat_history_id ;
IF pg_stat_history_rec.snapshot_timestamp IS NULL
THEN
RAISE EXCEPTION 'ERROR - Not found pg_stat_history for id = %',pg_stat_history_id;
END IF ;
--RAISE NOTICE 'pg_stat_history_id = % , snapshot_timestamp = %', pg_stat_history_id ,
pg_stat_history_rec.snapshot_timestamp ;
SELECT
MAX(id)
INTO
prev_pg_stat_history_id
FROM
pg_stat_history
WHERE
database_id = pg_stat_history_rec.database_id AND
queryid IS NULL AND
id < pg_stat_history_rec.id ;
IF prev_pg_stat_history_id IS NULL
THEN
RAISE NOTICE 'Not found previous pg_stat_history shapshot for id = %',pg_stat_history_id;
RETURN NULL ;
END IF;
SELECT *
INTO prev_pg_stat_history_rec
FROM
pg_stat_history
WHERE id = prev_pg_stat_history_id ;
--RAISE NOTICE 'prev_pg_stat_history_id = % , prev_snapshot_timestamp = %', prev_pg_stat_history_id , prev_pg_stat_history_rec.snapshot_timestamp ;
total_seconds = extract(epoch from ( pg_stat_history_rec.snapshot_timestamp - prev_pg_stat_history_rec.snapshot_timestamp ));
--RAISE NOTICE 'total_seconds = % ', total_seconds ;
--RAISE NOTICE 'calls = % ', pg_stat_history_rec.calls ;
IF total_seconds > 0
THEN
result = pg_stat_history_rec.calls / total_seconds ;
ELSE
result = 0 ;
END IF;
RETURN result ;
END
$$ LANGUAGE plpgsql;
SELECT
id ,
snapshot_timestamp ,
calls ,
total_time ,
( select pg_qps( id )) AS QPS ,
blk_read_time ,
blk_write_time
FROM
pg_stat_history
WHERE
queryid IS NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT AND
( select pg_qps( id )) IS NOT NULL
ORDER BY 5 DESC
LIMIT 10
|------------------------------------------------- ---------------------------------------- | TOP10 migawek uporządkowanych według liczb QueryPerSeconds -------------------------------------- ------ -------------------------------------------- ------ ---------------------------------------------------------- | #| migawka| identyfikator migawki| połączenia| całkowity czas db| QPS | czas we/wy | Czas we/wy % +-----+---+-----------+------- ----+-------------------------------------------------+---------- -+-------------------+----------- | 1| 04.04.2019 20:04| 4161| 5758631| 00:06:30.513(390513.926 ms.)| 1573.396| 00:00:01.470( 1470.110 ms.)| 376 | 2| 04.04.2019 17:00| 4149| 3529197| 00:11:48.830(708830.618 ms.)| 980.332| 00:12:47.834(767834.052 ms.)| 108.324 | 3| 04.04.2019 16:00| 4143| 3525360| 00:10:13.492(613492.351 ms.)| 979.267| 00:08:41.396(521396.555 ms.)| 84.988 | 4| 04.04.2019 21:03| 4163| 2781536| 00:03:06.470(186470.979 ms.)| 785.745| 00:00:00.249( 249.865 ms.)| 134 | 5| 04.04.2019 19:03| 4159| 2890362| 00:03:16.784( 196784.755 ms.)| 776.979| 00:00:01.441( 1441.386 ms)| 732 | 6| 04.04.2019 14:00| 4137| 2397326| 00:04:43.033(283033.854 ms)| 665.924| 00:00:00.024( 24.505 ms.)| 009 | 7| 04.04.2019 15:00| 4139| 2394416| 00:04:51.435(291435.010 ms.)| 665.116| 00:00:12.025( 12025.895 ms)| 4.126 | 8| 04.04.2019 13:00| 4135| 2373043| 00:04:26.791(266791.988 ms)| 659.179| 00:00:00.064(64.261 ms.)| 024 | 9| 05.04.2019 01:03| 4167| 4387191| 00:06:51.380(411380.293 ms.)| 609.332| 00:05:18.847(318847.407 ms)| 77.507 | 10| 04.04.2019 18:01| 4157| 1145596| 00:01:19.217(79217.372 ms)| 313.004| 00:00:01.319( 1319.676 ms.)| 1.666
Godzinowa historia wykonania z parametrami QueryPerSeconds i czasem we/wy
Zapytanie
SELECT
id ,
snapshot_timestamp ,
calls ,
total_time ,
( select pg_qps( id )) AS QPS ,
blk_read_time ,
blk_write_time
FROM
pg_stat_history
WHERE
queryid IS NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
ORDER BY 2
|----------------------------------------------------------------------------------------------- | HOURLY EXECUTION HISTORY WITH QueryPerSeconds and I/O Time ----------------------------------------------------------------------------------------------------------------------------------------------- | QUERY PER SECOND HISTORY | #| snapshot| snapshotID| calls| total dbtime| QPS| I/O time| I/O time % +-----+------------------+-----------+-----------+----------------------------------+-----------+----------------------------------+----------- | 1| 04.04.2019 11:00| 4131| 3747| 00:00:00.835( 835.374 ms.)| 1.041| 00:00:00.000( .000 ms.)| .000 | 2| 04.04.2019 12:00| 4133| 1002722| 00:01:52.419( 112419.376 ms.)| 278.534| 00:00:00.149( 149.105 ms.)| .133 | 3| 04.04.2019 13:00| 4135| 2373043| 00:04:26.791( 266791.988 ms.)| 659.179| 00:00:00.064( 64.261 ms.)| .024 | 4| 04.04.2019 14:00| 4137| 2397326| 00:04:43.033( 283033.854 ms.)| 665.924| 00:00:00.024( 24.505 ms.)| .009 | 5| 04.04.2019 15:00| 4139| 2394416| 00:04:51.435( 291435.010 ms.)| 665.116| 00:00:12.025( 12025.895 ms.)| 4.126 | 6| 04.04.2019 16:00| 4143| 3525360| 00:10:13.492( 613492.351 ms.)| 979.267| 00:08:41.396( 521396.555 ms.)| 84.988 | 7| 04.04.2019 17:00| 4149| 3529197| 00:11:48.830( 708830.618 ms.)| 980.332| 00:12:47.834( 767834.052 ms.)| 108.324 | 8| 04.04.2019 18:01| 4157| 1145596| 00:01:19.217( 79217.372 ms.)| 313.004| 00:00:01.319( 1319.676 ms.)| 1.666 | 9| 04.04.2019 19:03| 4159| 2890362| 00:03:16.784( 196784.755 ms.)| 776.979| 00:00:01.441( 1441.386 ms.)| .732 | 10| 04.04.2019 20:04| 4161| 5758631| 00:06:30.513( 390513.926 ms.)| 1573.396| 00:00:01.470( 1470.110 ms.)| .376 | 11| 04.04.2019 21:03| 4163| 2781536| 00:03:06.470( 186470.979 ms.)| 785.745| 00:00:00.249( 249.865 ms.)| .134 | 12| 04.04.2019 23:03| 4165| 1443155| 00:01:34.467( 94467.539 ms.)| 200.438| 00:00:00.015( 15.287 ms.)| .016 | 13| 05.04.2019 01:03| 4167| 4387191| 00:06:51.380( 411380.293 ms.)| 609.332| 00:05:18.847( 318847.407 ms.)| 77.507 | 14| 05.04.2019 02:03| 4171| 189852| 00:00:10.989( 10989.899 ms.)| 52.737| 00:00:00.539( 539.110 ms.)| 4.906 | 15| 05.04.2019 03:01| 4173| 3627| 00:00:00.103( 103.000 ms.)| 1.042| 00:00:00.004( 4.131 ms.)| 4.010 | 16| 05.04.2019 04:00| 4175| 3627| 00:00:00.085( 85.235 ms.)| 1.025| 00:00:00.003( 3.811 ms.)| 4.471 | 17| 05.04.2019 05:00| 4177| 3747| 00:00:00.849( 849.454 ms.)| 1.041| 00:00:00.006( 6.124 ms.)| .721 | 18| 05.04.2019 06:00| 4179| 3747| 00:00:00.849( 849.561 ms.)| 1.041| 00:00:00.000( .051 ms.)| .006 | 19| 05.04.2019 07:00| 4181| 3747| 00:00:00.839( 839.416 ms.)| 1.041| 00:00:00.000( .062 ms.)| .007 | 20| 05.04.2019 08:00| 4183| 3747| 00:00:00.846( 846.382 ms.)| 1.041| 00:00:00.000( .007 ms.)| .001 | 21| 05.04.2019 09:00| 4185| 3747| 00:00:00.855( 855.426 ms.)| 1.041| 00:00:00.000( .065 ms.)| .008 | 22| 05.04.2019 10:00| 4187| 3797| 00:01:40.150( 100150.165 ms.)| 1.055| 00:00:21.845( 21845.217 ms.)| 21.812
Tekst wszystkich wyborów SQL
Zapytanie
SELECT
queryid ,
query
FROM
pg_stat_history
WHERE
queryid IS NOT NULL AND
database_id = DATABASE_ID AND
snapshot_timestamp BETWEEN BEGIN_TIMEPOINT AND END_TIMEPOINT
GROUP BY queryid , query
Łączny
Jak widać, w dość prosty sposób można uzyskać wiele przydatnych informacji o obciążeniu i stanie bazy danych.
Notatka:Jeśli poprawisz kwerendę w zapytaniach, to otrzymamy historię dla osobnego żądania (w celu zaoszczędzenia miejsca raporty dla osobnego żądania są pomijane).
Dane statystyczne dotyczące wydajności zapytań są więc dostępne i gromadzone.
Zakończył się pierwszy etap „zbierania danych statystycznych”.
Możesz przejść do drugiego etapu - „konfiguracji wskaźników wydajności”.
Ale to zupełnie inna historia.
To be continued ...
Źródło: www.habr.com