Ingegnere - tradotto dal latino - ispirato.
Un ingegnere può fare qualsiasi cosa. (c) R. Diesel.
Epigrafi.
O una storia sul motivo per cui un amministratore di database deve ricordare il suo passato di programmazione.
prefazione
Tutti i nomi sono stati cambiati. Le partite sono casuali. Il materiale rappresenta esclusivamente l'opinione personale dell'autore.
Esclusione di garanzie: nella serie di articoli prevista non sarà presente una descrizione dettagliata e accurata delle tabelle e degli script utilizzati. I materiali non possono essere utilizzati immediatamente "COSÌ COME SONO".
Innanzitutto, a causa della grande quantità di materiale,
in secondo luogo, per la precisione con la base produttiva di un cliente reale.
Pertanto negli articoli verranno fornite solo idee e descrizioni nella forma più generale.
Forse in futuro il sistema raggiungerà il livello di pubblicazione su GitHub, o forse no. Il tempo mostrerà.
Inizio della storia-
Quello che è successo di conseguenza, in termini più generali..."
Perché ho bisogno di tutto questo?
Bene, in primo luogo, per non dimenticare te stesso, ricordando i gloriosi giorni in pensione.
In secondo luogo, sistematizzare ciò che è stato scritto. Per quanto mi riguarda, a volte comincio a confondermi e a dimenticare le singole parti.
Bene, e soprattutto, all'improvviso può tornare utile a qualcuno e aiutare a non reinventare la ruota e a non raccogliere un rastrello. In altre parole, migliora il tuo karma (non Khabrovsky). Perché la cosa più preziosa in questo mondo sono le idee. La cosa principale è trovare un'idea. E tradurre l'idea in realtà è già una questione puramente tecnica.
Quindi iniziamo lentamente...
Formulazione del problema.
C'è:
PostgreSQL(10.5), carico misto (OLTP+DSS), carico da medio a leggero, ospitato nel cloud AWS.
Non è previsto il monitoraggio del database, il monitoraggio dell'infrastruttura è presentato come strumenti AWS standard in una configurazione minima.
richiede:
Monitora le prestazioni e lo stato del database, trova e disponi di informazioni iniziali per ottimizzare le query pesanti del database.
Breve introduzione o analisi delle soluzioni
Per cominciare, proviamo ad analizzare le opzioni per risolvere il problema dal punto di vista di un'analisi comparativa dei vantaggi e dei problemi per l'ingegnere, e lasciamo che coloro che dovrebbero essere nell'elenco del personale si occupino dei benefici e delle perdite di gestione.
Opzione 1 - "Lavoro su richiesta"
Lasciamo tutto così com'è. Se il cliente non è soddisfatto di qualcosa relativo alla salute, alle prestazioni del database o dell'applicazione, avviserà gli ingegneri DBA tramite e-mail o creando un incidente nella ticket box.
Un ingegnere, ricevuta una segnalazione, capirà il problema, offrirà una soluzione, oppure accantonerà il problema, sperando che tutto si risolva da solo, e comunque tutto venga presto dimenticato.
Pan di zenzero e ciambelle, lividi e protuberanzePan di zenzero e ciambelle:
1. Niente in più da fare
2. C'è sempre la possibilità di uscire e sporcarsi.
3. Molto tempo che puoi trascorrere da solo.
Lividi e protuberanze:
1. Prima o poi, il cliente penserà all'essenza dell'essere e alla giustizia universale in questo mondo e si porrà ancora una volta la domanda: perché sto pagando loro i miei soldi? La conseguenza è sempre la stessa: l'unica domanda è quando il cliente si annoia e saluta. E l'alimentatore è vuoto. È triste.
2. Lo sviluppo di un ingegnere è zero.
3. Difficoltà nella programmazione del lavoro e del carico
Opzione 2 - "Balla con i tamburelli, mettiti e mettiti le scarpe"
Paragrafo 1-Perché abbiamo bisogno di un sistema di monitoraggio, riceveremo tutte le richieste. Lanciamo una serie di tutti i tipi di query al dizionario dei dati e alle visualizzazioni dinamiche, attiviamo tutti i tipi di contatori, inseriamo tutto nelle tabelle, analizziamo periodicamente elenchi e tabelle, per così dire. Di conseguenza, abbiamo grafici, tabelle, report belli o poco belli. La cosa principale sarebbe di più, di più.
Paragrafo 2-Generare attività ed eseguire l'analisi di tutto questo.
Paragrafo 3-Stiamo preparando un certo documento, lo chiameremo semplicemente - "come attrezziamo il database".
Paragrafo 4- Il cliente, vedendo tutta questa magnificenza di grafici e figure, ha una fiducia infantile e ingenua: ora tutto funzionerà per noi, presto. E separarsi facilmente e indolore dalle proprie risorse finanziarie. Anche il management è sicuro che i nostri ingegneri stiano lavorando sodo. Caricamento massimo.
Paragrafo 5- Ripetere regolarmente il passaggio 1.
Pan di zenzero e ciambelle, lividi e protuberanzePan di zenzero e ciambelle:
1. La vita di manager e ingegneri è semplice, prevedibile e piena di attività. Tutto è in fermento, tutti sono occupati.
2. Anche la vita del cliente non è male: è sempre sicuro che devi avere un po 'di pazienza e tutto funzionerà. Non migliorare, bene, bene - questo mondo è ingiusto, nella prossima vita - fortunato.
Lividi e protuberanze:
1. Prima o poi ci sarà un fornitore più intelligente di un servizio simile che farà la stessa cosa, ma un po’ più economico. E se il risultato è lo stesso, perché pagare di più? Il che porterà ancora una volta alla scomparsa dell'alimentatore.
2. È noioso. Com'è noiosa ogni attività poco significativa.
3. Come nella versione precedente, nessuno sviluppo. Ma per un ingegnere, lo svantaggio è che, a differenza della prima opzione, qui è necessario generare costantemente un IDB. E questo richiede tempo. Che può essere speso a beneficio della persona amata. Perché non puoi prenderti cura di te stesso, tutti si preoccupano di te.
Opzione 3: non c'è bisogno di inventare una bicicletta, devi comprarla e guidarla.
Gli ingegneri di altre aziende mangiano consapevolmente la pizza con la birra (oh, i tempi gloriosi di San Pietroburgo negli anni '90). Usiamo sistemi di monitoraggio realizzati, sottoposti a debug e funzionanti e, in generale, apportano vantaggi (beh, almeno ai loro creatori).
Pan di zenzero e ciambelle, lividi e protuberanzePan di zenzero e ciambelle:
1. Non c'è bisogno di perdere tempo a inventare ciò che è già inventato. Prendi e usa.
2. I sistemi di monitoraggio non sono scritti da stupidi e ovviamente sono utili.
3. I sistemi di monitoraggio funzionanti solitamente forniscono informazioni filtrate utili.
Lividi e protuberanze:
1. L'ingegnere in questo caso non è un ingegnere, ma semplicemente un utente del prodotto di qualcun altro o un utente.
2. Il cliente deve essere convinto della necessità di acquistare qualcosa che generalmente non vuole capire, e non dovrebbe, e in generale il budget dell'anno è stato approvato e non cambierà. Quindi è necessario allocare una risorsa separata, configurarla per un sistema specifico. Quelli. Per prima cosa devi pagare, pagare e pagare ancora. E il cliente è avaro. Questa è la norma di questa vita.
Cosa fare, Chernyshevskij? La tua domanda è molto pertinente. (Con)
In questo caso particolare e nella situazione attuale, puoi agire in modo leggermente diverso: creiamo il nostro sistema di monitoraggio.
Beh, non un sistema, ovviamente, nel pieno senso della parola, questo è troppo rumoroso e presuntuoso, ma almeno in qualche modo renditi più facile e raccogli più informazioni per risolvere gli incidenti di prestazione. Per non trovarti in una situazione: "vai lì, non so dove, trovalo, non so cosa".
Quali sono i pro e i contro di questa opzione:
pro:
1. È interessante. Beh, almeno più interessante della costante "riduci il file di dati, altera lo spazio della tabella, ecc."
2. Queste sono nuove competenze e nuovo sviluppo. Che in futuro, prima o poi, regalerà meritati pan di zenzero e ciambelle.
contro:
1. Devo lavorare. Lavorare molto.
2. Dovrai spiegare regolarmente il significato e le prospettive di ogni attività.
3. Qualcosa dovrà essere sacrificato, perché l'unica risorsa a disposizione dell'ingegnere - il tempo - è limitata dall'Universo.
4. Il peggiore e il più spiacevole - di conseguenza, potrebbe risultare spazzatura come "Non un topo, non una rana, ma un animaletto sconosciuto".
Chi non rischia qualcosa non beve champagne.
Quindi inizia il divertimento.
Idea generale - schema
(Illustrazione tratta dall'articolo «
spiegazione:
- Il database di destinazione viene installato con l'estensione PostgreSQL standard “pg_stat_statements”.
- Nel database di monitoraggio, creiamo una serie di tabelle di servizio per archiviare la cronologia pg_stat_statements nella fase iniziale e per configurare metriche e monitoraggio in futuro
- Sull'host di monitoraggio creiamo una serie di script bash, inclusi quelli per la generazione di incidenti nel sistema di ticket.
Tavoli di servizio
Per cominciare, un ERD schematicamente semplificato, cosa è successo alla fine:
Breve descrizione delle tabelleendpoint - host, punto di connessione all'istanza
banca dati - opzioni del database
pg_stat_history - una tabella storica per la memorizzazione di istantanee temporanee della vista pg_stat_statements del database di destinazione
metric_glossary - Dizionario delle metriche prestazionali
metric_config - configurazione delle singole metriche
metrico - una metrica specifica per la richiesta che viene monitorata
metric_alert_history - cronologia degli avvisi sulle prestazioni
log_query - tabella di servizio per l'archiviazione di record analizzati dal file di registro PostgreSQL scaricato da AWS
linea di base - parametri del periodo temporale preso come base
posto di controllo - configurazione delle metriche per il controllo dello stato del database
checkpoint_alert_history - cronologia degli avvisi delle metriche di controllo dello stato del database
pg_stat_db_queries — tabella di servizio delle richieste attive
registro delle attività — tabella di servizio del registro delle attività
trap_oid - tabella dei servizi di configurazione trap
Fase 1: raccogli statistiche sulle prestazioni e ottieni report
Una tabella viene utilizzata per memorizzare informazioni statistiche. pg_stat_history
struttura della tabella pg_stat_history
Tabella "public.pg_stat_history" Colonna | digitare | Modificatori--------------------+-------------------- --+---- -------------------------------- id | intero | not null default nextval('pg_stat_history_id_seq'::regclass) snapshot_timestamp | timestamp senza fuso orario | id_database | intero | dbid | oido | ID utente | oido | IDinterrogazione | bigint | domanda | testo | chiama | bigint | tempo_totale | doppia precisione | min_time | doppia precisione | tempo_max | doppia precisione | tempo_medio | doppia precisione | stddev_time | doppia precisione | righe | bigint | shared_blks_hit | bigint | shared_blks_read | bigint | shared_blks_dirtied | bigint | shared_blks_script | bigint | local_blks_hit | bigint | local_blks_read | bigint | local_blks_dirtied | bigint | local_blks_scritto | bigint | temp_blks_read | bigint | temp_blks_scritto | bigint | blk_read_time | doppia precisione | blk_write_time | doppia precisione | baseline_id | intero | Indici: "pg_stat_history_pkey" PRIMARY KEY, btree (id) "database_idx" btree (database_id) "queryid_idx" btree (queryid) "snapshot_timestamp_idx" btree (snapshot_timestamp) Vincoli di chiave esterna: "database_id_fk" FOREIGN KEY (database_id) REFERENCES database(id ) SU CANCELLA CASCATA
Come puoi vedere, la tabella è solo una visualizzazione cumulativa dei dati pg_stat_statements nel database di destinazione.
L'utilizzo di questa tabella è molto semplice.
pg_stat_history rappresenterà le statistiche accumulate dell'esecuzione delle query per ogni ora. All'inizio di ogni ora, dopo aver compilato la tabella, vengono visualizzate le statistiche pg_stat_statements reimpostare con pg_stat_statements_reset().
Nota: vengono raccolte statistiche per le richieste con una durata superiore a 1 secondo.
Popolamento della tabella 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;
Di conseguenza, dopo un certo periodo di tempo nella tabella pg_stat_history avremo una serie di istantanee del contenuto della tabella pg_stat_statements banca dati di destinazione.
Anzi, riporto
Utilizzando query semplici, puoi ottenere report piuttosto utili e interessanti.
Dati aggregati per un dato periodo di tempo
Richiesta
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 ;
Tempo DB
to_char(intervallo '1 millisecondo' * pg_total_stat_history_rec.total_time, 'HH24:MI:SS.MS')
Tempo I/O
to_char(intervallo '1 millisecondo' * ( pg_total_stat_history_rec.blk_read_time + pg_total_stat_history_rec.blk_write_time ), 'HH24:MI:SS.MS')
TOP10 SQL per total_time
Richiesta
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 PER TEMPO DI ESECUZIONE TOTALE | #| queryid| chiama| chiama %| tempo_totale (ms) | dbtime % +----+-----------+-----------+-----------+------ ---------------------+---------- | 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 per tempo I/O totale
Richiesta
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 PER TEMPO DI I/O TOTALE | #| queryid| chiama| chiama %| Tempo I/O (ms)|db Tempo I/O % +----+-----------+-----------+------ -----+--------------------------------+----------- -- | 1| 4152624390| 2| .00001|00:08:31.616( 511616.592 ms.)| 31.06 giugno | 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 per tempo massimo di esecuzione
Richiesta
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 PER TEMPO DI ESECUZIONE MAX | #| istantanea| IDistantanea| queryid| tempo_max (ms) +----+------------+-----------+--------- --+---------------------------------------- | 1| 05.04.2019/01/03 4169:655729273| 00| 02| 01.869:121869.981:2( 04.04.2019 ms.) | 17| 00/4153/821760255 00:01| 41.570| 101570.841| 3:04.04.2019:16( 00 ms.) | 4146| 821760255/00/01 41.570:101570.841| 4| 04.04.2019| 16:00:4144( 4152624390 ms.) | 00| 01/36.964/96964.607 5:04.04.2019| 17| 00| 4151:4152624390:00( 01 ms.) | 36.964| 96964.607/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:8( 04.04.2019 ms.) | 15| 00/4140/1484454471 00:00| 11.892| 11892.302| 9:04.04.2019:16( 00 ms.) | 4145| 1484454471/00/00 11.892:11892.302| 10| 04.04.2019| 17:00:4152( 1484454471 ms.) | 00| 00/11.892/11892.302 XNUMX:XNUMX| XNUMX| XNUMX| XNUMX:XNUMX:XNUMX( XNUMX ms.) | XNUMX| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX:XNUMX:XNUMX( XNUMX ms.)
TOP10 SQL tramite buffer di lettura/scrittura SHARED
Richiesta
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 PER BUFFER CONDIVISO LETTURA/SCRITTURA | #| istantanea| IDistantanea| queryid| blocchi condivisi letti| i blocchi condivisi scrivono +----+--------------------+-----------+---------- -+--------------------+--------------------- | 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| XNUMX| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX| XNUMX| XNUMX| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX| XNUMX ------------------------------------------------- -------------------------------------------------
Istogramma della distribuzione delle query in base al tempo di esecuzione massimo
richieste
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 ) ;
|------------------------------------------------- --------------------------------------- | ISTOGRAMMA MAX_TIME | TOTALE CHIAMATE: 33851920 | TEMPO MINIMO: 00:00:01.063 | TEMPO MASSIMO: 00:02:01.869 ---------------------------------- -------- ---------------------- | durata minima| durata massima| chiamate +---------------------------------+------------- ---------------------+---------- | 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 istantanee per query al secondo
richieste
--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 istantanee ordinate per numero QueryPerSeconds -------------------------------------- ------ -------------------------------------------- ------ -------------------------------------------------- | #| istantanea| IDistantanea| chiama| dbtime totale| QPS | Tempo I/O | Tempo I/O % +-----+------------+-----------+------- ----+--------------------------------+---------- -+--------------------------------+----------- | 1| 04.04.2019/20/04 4161:5758631| 00| 06| 30.513:390513.926:1573.396( 00 ms.)| 00| 01.470:1470.110:376( 2 ms.)| .04.04.2019 | 17| 00/4149/3529197 00:11| 48.830| 708830.618| 980.332:00:12( 47.834 ms.)| 767834.052| 108.324:3:04.04.2019( 16 ms.)| 00 | 4143| 3525360/00/10 13.492:613492.351| 979.267| 00| 08:41.396:521396.555( 84.988 ms.)| 4| 04.04.2019:21:03( 4163ms.)| 2781536| 00| 03/06.470/186470.979 785.745:00| 00| 00.249| 249.865:134:5( 04.04.2019 ms.)| 19| 03:4159:2890362( 00ms.)| .03 | 16.784| 196784.755/776.979/00 00:01.441| 1441.386| 732| 6:04.04.2019:14( 00 ms.)| 4137| 2397326:00:04( 43.033 ms.)| .283033.854 | 665.924| 00/00/00.024 24.505:009 | 7| 04.04.2019| 15:00:4139( 2394416 ms.)| 00| 04:51.435:291435.010( 665.116 ms.)| .00 | 00| 12.025/12025.895/4.126 8:04.04.2019| 13| 00| 4135:2373043:00( 04 ms.)| 26.791| 266791.988:659.179:00( 00 ms.)| 00.064| 64.261| 024/9/05.04.2019 01:03| 4167| 4387191| 00:06:51.380( 411380.293 ms.)| 609.332| 00:05:18.847( 318847.407ms.)| .77.507 | 10| 04.04.2019/18/01 4157:1145596| 00| 01| 19.217:79217.372:313.004( 00 ms.)| 00| 01.319:1319.676:1.666( XNUMX ms.)| XNUMX| XNUMX| XNUMX/XNUMX/XNUMX XNUMX:XNUMX| XNUMX| XNUMX| XNUMX:XNUMX:XNUMX( XNUMX ms.)| XNUMX| XNUMX:XNUMX:XNUMX( XNUMX ms.)| XNUMX
Cronologia oraria delle esecuzioni con QueryPerSeconds e tempo di I/O
Richiesta
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
Testo di tutte le selezioni SQL
Richiesta
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
risultato
Come puoi vedere, con mezzi abbastanza semplici, puoi ottenere molte informazioni utili sul carico di lavoro e sullo stato del database.
Primechenie:Se correggi il queryid nelle query, otterremo la cronologia per una richiesta separata (per risparmiare spazio, i report per una richiesta separata vengono omessi).
Pertanto, i dati statistici sulle prestazioni delle query sono disponibili e raccolti.
La prima fase "raccolta di dati statistici" è completata.
Puoi procedere alla seconda fase: "configurazione delle metriche delle prestazioni".
Ma questa è una storia completamente diversa.
To be continued ...
Fonte: habr.com