Uno dei metodi per ottenere la cronologia dei blocchi in PostgreSQL

Continuazione dell'articolo «Tentativo di creare un analogo di ASH per PostgreSQL «.

Nell'articolo verrà esaminato e mostrato, attraverso richieste specifiche e esempi, quali informazioni utili si possano ottenere dalla visualizzazione pg_locks.

Avviso.
A causa della novità dell'argomento e della conclusione non ancora conclusa del periodo di test, l'articolo potrebbe contenere errori. Critiche e commenti sono benvenuti e attesi.

Dati di input

Storia della visualizzazione pg_locks

archive_locking

CREATE TABLE archive_locking 
(       timepoint timestamp senza fuso orario ,
	locktype text ,
	relation oid ,
	mode text ,
	tid xid ,
	vtid text ,
	pid integer ,
	blocking_pids integer[] ,
	granted boolean ,
        queryid bigint 
);

In sostanza, la tabella è analoga alla tabella archive_pg_stat_activity, descritta più dettagliatamente qui — pg_stat_statements + pg_stat_activity + loq_query = pg_ash? e qui — Tentativo di creare un analogo di ASH per PostgreSQL.

Per popolari la colonna queryid si utilizza la funzione

update_history_locking_by_queryid

--update_history_locking_by_queryid.sql
CREATE OR REPLACE FUNCTION update_history_locking_by_queryid() RETURNS boolean AS $$
DECLARE
  result boolean ;
  current_minute double precision ; 
  
  start_minute integer ;
  finish_minute integer ;
  
  start_period timestamp senza fuso orario ;
  finish_period timestamp senza fuso orario ;
  
  lock_rec record ; 
  endpoint_rec record ; 
  
  current_hour_diff double precision ;
BEGIN
  RAISE NOTICE '***update_history_locking_by_queryid';
  
  result = TRUE ;
  
  current_minute = extract ( minute from now() );

  SELECT * FROM endpoint WHERE is_need_monitoring
  INTO endpoint_rec ;
  
  current_hour_diff = endpoint_rec.hour_diff ;
  
  IF current_minute < 5 
  THEN
	RAISE NOTICE 'L'orario attuale è inferiore a 5 minuti.';
	
	start_period = date_trunc('hour',now()) + (current_hour_diff * interval '1 hour');
    finish_period = start_period - interval '5 minute' ;
  ELSE 
    finish_minute =  extract ( minute from now() ) / 5 ;
    start_minute =  finish_minute - 1 ;
  
    start_period = date_trunc('hour',now()) + interval '1 minute'*start_minute*5+(current_hour_diff * interval '1 hour');
    finish_period = date_trunc('hour',now()) + interval '1 minute'*finish_minute*5+(current_hour_diff * interval '1 hour') ;
    
  END IF ;  
  
  RAISE NOTICE 'start_period = %', start_period;
  RAISE NOTICE 'finish_period = %', finish_period;

	FOR lock_rec IN   
	WITH act_queryid AS
	 (
		SELECT 
				pid , 
				timepoint ,
				query_start AS started ,			
				MAX(timepoint) OVER (PARTITION BY pid ,	query_start   ) AS finished ,			
				queryid 
		FROM 
				activity_hist.history_pg_stat_activity 			
		WHERE 			
				timepoint BETWEEN start_period and 
								  finish_period
		GROUP BY 
				pid , 
				timepoint ,  
				query_start ,
				queryid 
	 ),
	 lock_pids AS
		(
			SELECT
				hl.pid , 
				hl.locktype  ,
				hl.mode ,
				hl.timepoint , 
				MIN ( timepoint ) OVER (PARTITION BY pid , locktype  ,mode ) as started 
			FROM 
				activity_hist.history_locking hl
			WHERE 
				hl.timepoint between start_period and 
								     finish_period
			GROUP BY 
				hl.pid , 
				hl.locktype  ,
				hl.mode ,
				hl.timepoint 
		)
	SELECT 
		lp.pid , 
		lp.locktype  ,
		lp.mode ,
		lp.timepoint ,     
		aq.queryid 
	FROM lock_pids 	lp LEFT OUTER JOIN act_queryid aq ON ( lp.pid = aq.pid AND lp.started BETWEEN aq.started AND aq.finished )
	WHERE aq.queryid IS NOT NULL 
	GROUP BY  
		lp.pid , 
		lp.locktype  ,
		lp.mode ,
		lp.timepoint , 
		aq.queryid
	LOOP
		UPDATE activity_hist.history_locking SET queryid = lock_rec.queryid 
		WHERE pid = lock_rec.pid AND locktype = lock_rec.locktype AND mode = lock_rec.mode AND timepoint = lock_rec.timepoint ;	
	END LOOP;    
  
  RETURN result ;
END
$$ LANGUAGE plpgsql;

Spiegazione: Il valore della colonna queryid viene aggiornato nella tabella history_locking e successivamente, quando viene creata una nuova sezione per la tabella archive_locking, il valore verrà mantenuto nei valori storici.

Uscita

Informazioni generali sui processi in generale.

ATTENDA PER BLOCCHI PER TIPI DI BLOCCO

Query

CON
 t AS
(
	SELECT 
		locktype  ,
		mode ,
		count(*) as total 
	FROM 
		activity_hist.archive_locking
	WHERE 
		timepoint between pg_stat_history_begin+(current_hour_diff * interval '1 hour') AND pg_stat_history_end+(current_hour_diff * interval '1 hour') AND 
		NOT granted
	GROUP BY 
		locktype  ,
		mode  
)
SELECT 
	locktype  ,
	mode ,
	total * interval '1 second' as duration			
FROM t 		
ORDER BY 3 DESC 

Esempio

| IN ATTESA DI BLOCCO PER TIPO DI BLOCCO
+--------------------+------------------------------+--------------------
|            tipo di blocco|                          modalità|            durata
+--------------------+------------------------------+--------------------
|       transactionid|                     ShareLock|            19:39:26
|               tuple|           AccessExclusiveLock|            00:03:35
+--------------------+------------------------------+--------------------

PRELIEVI DI BLOCCO PER TIPO DI BLOCCO

Query

CON
t AS
(
	SELECT 
		tipo_di_blocco,
		modalità,
		count(*) AS totale 
	FROM 
		activity_hist.archive_locking
	WHERE 
		timepoint BETWEEN pg_stat_history_begin+(current_hour_diff * interval '1 hour') AND pg_stat_history_end+(current_hour_diff * interval '1 hour') AND 
		concesso
	GROUP BY 
		tipo_di_blocco,
		modalità
)
SELECT 
	tipo_di_blocco,
	modalità,
	totale * interval '1 second' AS durata
FROM t
ORDER BY 3 DESC 

Esempio

| PRELIEVI DI BLOCCO PER TIPO DI BLOCCO
+--------------------+------------------------------+--------------------
|            tipo di blocco|                          modalità|            durata
+--------------------+------------------------------+--------------------
|            relazione|              RowExclusiveLock|            51:11:10
|          virtualxid|                 ExclusiveLock|            48:10:43
|       transactionid|                 ExclusiveLock|            44:24:53
|            relazione|               AccessShareLock|            20:06:13
|               tuple|           AccessExclusiveLock|            17:58:47
|               tuple|                 ExclusiveLock|            01:40:41
|            relazione|      ShareUpdateExclusiveLock|            00:26:41
|              oggetto|              RowExclusiveLock|            00:00:01
|       transactionid|                     ShareLock|            00:00:01
|              estendere|                 ExclusiveLock|            00:00:01
+--------------------+------------------------------+--------------------

Informazioni dettagliate sui singoli queryid delle richieste

IN ATTESA DI BLOCCO PER TIPO DI BLOCCO PER QUERYID

Query

CON
lt AS
(
	SELECT
		pid,
		tipo_di_blocco,
		modalità,
		timepoint,
		queryid,
		blocking_pids,
                MIN(timepoint) OVER (PARTITION BY pid, tipo_di_blocco, modalità) AS iniziato
	FROM 
		activity_hist.archive_locking
	WHERE 
		timepoint BETWEEN pg_stat_history_begin+(current_hour_diff * interval '1 hour') AND 
				pg_stat_history_end+(current_hour_diff * interval '1 hour') AND 
		NON concesso AND
		queryid IS NOT NULL 
	GROUP BY 
		pid,
		tipo_di_blocco,
		modalità,
		timepoint,
		queryid,
		blocking_pids
)
SELECT 
        lt.pid,
	lt.tipo_di_blocco,
	lt.modalità,
        lt.iniziato,
	lt.queryid,
	lt.blocking_pids,
	COUNT(*) * interval '1 second' AS durata
FROM lt
GROUP BY 
	lt.pid,
        lt.tipo_di_blocco,
	lt.modalità,
        lt.iniziato,
        lt.queryid,
	lt.blocking_pids
ORDER BY 4

Esempio

| IN ATTESA DI BLOCCO PER TIPO DI BLOCCO PER QUERYID
+----------+-------------------------+--------------------+------------------------------+--------------------+--------------------+--------------------
|       pid|                 locktype|                mode|                       started|             queryid|       blocking_pids|            duration
+----------+-------------------------+--------------------+------------------------------+--------------------+--------------------+--------------------
|     11288|            transactionid|           ShareLock|    2019-09-17 10:00:00.302936|  389015618226997618|             {11092}|            00:03:34
|     11626|            transactionid|           ShareLock|    2019-09-17 10:00:21.380921|  389015618226997618|             {12380}|            00:00:29
|     11626|            transactionid|           ShareLock|    2019-09-17 10:00:21.380921|  389015618226997618|             {11092}|            00:03:25
|     11626|            transactionid|           ShareLock|    2019-09-17 10:00:21.380921|  389015618226997618|             {12213}|            00:01:55
|     11626|            transactionid|           ShareLock|    2019-09-17 10:00:21.380921|  389015618226997618|             {12751}|            00:00:01
|     11629|            transactionid|           ShareLock|    2019-09-17 10:00:24.331935|  389015618226997618|             {11092}|            00:03:22
|     11629|            transactionid|           ShareLock|    2019-09-17 10:00:24.331935|  389015618226997618|             {12007}|            00:00:01
|     12007|            transactionid|           ShareLock|    2019-09-17 10:05:03.327933|  389015618226997618|             {11629}|            00:00:13
|     12007|            transactionid|           ShareLock|    2019-09-17 10:05:03.327933|  389015618226997618|             {11092}|            00:01:10
|     12007|            transactionid|           ShareLock|    2019-09-17 10:05:03.327933|  389015618226997618|             {11288}|            00:00:05
|     12213|            transactionid|           ShareLock|    2019-09-17 10:06:07.328019|  389015618226997618|             {12007}|            00:00:10

PRENDENDO BLOCCO PER TIPO DI BLOCCO PER QUERYID

Query

CON
lt COME
(
	SELEZIONA
		pid , 
		locktype  ,
		mode ,
		timepoint , 
		queryid , 
		blocking_pids ,
                MIN ( timepoint ) OVER (PARTITION BY pid , locktype  ,mode ) as started  
	DA 
		activity_hist.archive_locking
	DOVE 
		timepoint tra pg_stat_history_begin+(current_hour_diff * interval '1 hour') E 
			                  pg_stat_history_end+(current_hour_diff * interval '1 hour') E 
		concesso E
		queryid IS NOT NULL 
	GRUPPA PER 
	        pid , 
		locktype  ,
		mode ,
		timepoint ,
		queryid ,
		blocking_pids 
)
SELEZIONA 
        lt.pid , 
	lt.locktype  ,
	lt.mode ,			
        lt.started ,
	lt.queryid  ,
	lt.blocking_pids ,
	COUNT(*)  * interval '1 second'	 come duration			
DA lt 	
GRUPPA PER 
	lt.pid , 
	lt.locktype  ,
	lt.mode ,			
        lt.started ,
	lt.queryid ,
	lt.blocking_pids 
ORDINA PER 4

Esempio

| OTTENERE LOCK DA LOCKTYPES PER QUERYID
+----------+-------------------------+--------------------+------------------------------+--------------------+--------------------+--------------------
|       pid|                 locktype|                mode|                       started|             queryid|       blocking_pids|            duration
+----------+-------------------------+--------------------+------------------------------+--------------------+--------------------+--------------------
|     11288|                 relation|    RowExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|             {11092}|            00:03:34
|     11092|            transactionid|       ExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|                  {}|            00:03:34
|     11288|                 relation|    RowExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|                  {}|            00:00:10
|     11092|                 relation|    RowExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|                  {}|            00:03:34
|     11092|               virtualxid|       ExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|                  {}|            00:03:34
|     11288|               virtualxid|       ExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|             {11092}|            00:03:34
|     11288|            transactionid|       ExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|             {11092}|            00:03:34
|     11288|                    tuple| AccessExclusiveLock|    2019-09-17 10:00:00.302936|  389015618226997618|             {11092}|            00:03:34

Utilizzo della cronologia delle blocchi nell'analisi degli incidenti di performance.

  1. La richiesta con queryid=389015618226997618 eseguita dal processo con pid=11288 ha atteso un blocco a partire dal 2019-09-17 10:00:00 per 3 minuti.
  2. Il blocco era mantenuto dal processo con pid=11092.
  3. Il processo con pid=11092, eseguendo la richiesta con queryid=389015618226997618 a partire dal 2019-09-17 10:00:00, ha mantenuto il blocco per 3 minuti.

Risultato

Ora, spero, inizia la parte più interessante e utile: la raccolta di statistiche e l'analisi dei casi sulla cronologia delle attese e delle blocchi.

In prospettiva, spero di poter ottenere un insieme di note simili a quelle del metadato di Oracle.

In effetti, proprio per questo motivo la metodologia utilizzata viene presentata il più rapidamente possibile per una diffusione generale.

Nel più breve tempo possibile cercherò di pubblicare il progetto su github.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS - ProHoster