Маніторынг прадукцыйнасці запытаў PostgreSQL. Частка 1 - рэпартынг

Інжынер - у перакладзе з латыні - натхнёны.
Інжынер можа ўсё. (с) Р.Дызель.
Эпіграфы.
Маніторынг прадукцыйнасці запытаў PostgreSQL. Частка 1 - рэпартынг
Або гісторыя аб тым, навошта адміністратару баз дадзеных успамінаць сваё праграмісцкае мінулае.

Прадмова

Усе імёны зменены. Супадзенні выпадковыя. Матэрыял уяўляе сабой выключна асабістае меркаванне аўтара.

Адмова ад гарантый: у планаваным цыкле артыкулаў не будзе падрабязнага і дакладнага апісання выкарыстоўваных табліц і скрыптоў. Матэрыялы не атрымаецца адразу выкарыстоўваць "AS IS".
Па-першае, з прычыны вялікага аб'ёму матэрыялу,
па-другое па прычыне заменчанасці з прадакшн базай рэальнага заказчыка.
Таму ў артыкулах будуць прыведзены толькі ідэі і апісанні ў самым агульным выглядзе.
Можа быць у будучыні сістэма дарасце да ўзроўню выкладвання на GitHub, а можа быць і не. Час пакажа.

Пачатак гісторыі-Ты памятаеш, як усё пачыналася.
Што атрымалася ў выніку, у самых агульных рысах-Сінтэз як адзін з метадаў паляпшэння прадукцыйнасці PostgreSQL»

Навошта мне ўсё гэта?

Ну, па-першае, каб самому не забыцца, успамінаючы на ​​пенсіі слаўныя дзянькі.
Па-другое, каб сістэматызаваць напісанае. Бо ўжо сам, часам пачынаю блытацца і забывацца на асобныя часткі.

Ну і самае галоўнае - раптам можа камусьці спатрэбіцца і дапаможа не вынаходзіць ровар і не збіраць граблі. Іншымі словамі, палепшыць сваю карму (не хаброўскую). Бо самае каштоўнае ў гэтым свеце гэта ідэі. Галоўнае знайсці ідэю. А рэалізаваць ідэю ў рэальнасць гэта ўжо пытанне чыста тэхнічнае.

Такім чынам, пачнем, паціху…

Пастаноўка задачы.

Маецца:

База дадзеных PostgreSQL(10.5), змешанага тыпу нагрузкі (OLTP+DSS), сярэдняй-малой загружанасці, размешчанай у воблаку AWS.
Маніторынг базы дадзеных адсутнічае, маніторынг інфраструктуры прадстаўлены ў выглядзе штатныя сродкі AWS у мінімальнай канфігурацыі.

патрабуецца:

Маніторыць прадукцыйнасць і стан базы дадзеных, знаходзіць і мець пачатковую інфармацыю для аптымізацыі цяжкіх запыты да БД.

Кароткая прадмова ці аналіз варыянтаў рашэння

Для пачатку паспрабуем разабраць варыянты рашэння задачы з пункту гледжання параўнальнага аналізу выгод і непрыемнасцяў для інжынера, а карысцю і стратамі менеджменту няхай займаюцца тыя, каму належыць па штатным раскладзе.

Варыянт 1- "Working on demand"

Пакідаем усё як ёсць. Калі заказчыка не задавальняе нешта ў працаздольнасці, прадукцыйнасці базы дадзеных або дадатку ён паведаміць інжынераў DBA па e-mail або стварыўшы інцыдэнт у тикетнице.
Інжынер, атрымаўшы апавяшчэнне, разбярэцца ў праблеме, прапануе рашэнне або адкладзе праблему ў доўгую скрыню, спадзеючыся, што ўсё само сабой рассмокчацца, ды і ўсё роўна, хутка ўсё забудзецца.
Пернікі і пышкі, сінякі і шышкіПернікі і пышкі:
1. Нічога лішняга рабіць не трэба
2. Заўсёды ёсць магчымасць адмазацца і сачкануць.
3. Куча часу, якое можна выдаткаваць па ўласным меркаванні.
Сінякі і гузы:
1. Рана ці позна заказчык задумаецца аб сутнасці быцця і сусветнай справядлівасці ў гэтым свеце і ў чарговы раз задасць сабе пытанне - за, што я ім плачу свае грошы? Наступства заўсёды адно - пытанне толькі калі заказчык занудзіцца і махне рукой на развітанне. І кармушка спусцее. Гэта сумна.
2. Развіццё інжынера - нуль.
3. Складанасці планавання працы і загрузкі

Варыянт 2- «Танчым з бубнамі, упарваем і абуваем»

Пункт1-Навошта нам сістэма маніторынгу, мы будзем усё атрымліваць запытамі. Запускам кучу ўсякіх запытаў да слоўніка дадзеных і дынамічным паданням, уключаем усякія лічыльнікі, зводзім усё ў табліцы, перыядычна як бы аналізуем спісы і табліцы. У выніку маем прыгожыя ці не вельмі графікі, табліцы, справаздачы. Галоўнае - што б пабольш, пабольш.
Пункт2-Генеруем актыўнасць-запускаем аналіз усяго гэтага.
Пункт3-Гатовім нейкі дакумент, называем гэты дакумент, проста - "як нам уладкаваць базу дадзеных".
Пункт4-Заказчык, бачачы ўсё гэтае хараство графікаў і лічбаў знаходзіцца ў дзіцячай наіўнай упэўненасці - вось зараз то ў нас усё запрацуе, хутка. І, лёгка і бязбольна растаецца са сваімі грашовымі рэсурсамі. Мэнэджмент таксама ўпэўнены - інжынеры ў нас працуюць ого-га. Загрузка на максімуме.
Пункт5-Рэгулярна паўтарыць Пункт 1.
Пернікі і пышкі, сінякі і шышкіПернікі і пышкі:
1. Жыццё мэнэджараў і інжынераў - простае, прадказальная і напоўнена актыўнасцю. Усё гудзе, усе занятыя.
2. Жыццё заказчыка таксама нядрэннае - ён заўсёды ўпэўнены, што трэба вось ледзь-ледзь пацярпець і ўсё наладзіцца. Не наладжваецца, ну, што ж - гэта свет несправядлівы, у наступным жыцці - павязе.
Сінякі і гузы:
1. Рана ці позна, але знойдзецца больш шустры пастаўшчык аналагічнай паслугі, які будзе рабіць тое ж самае, але крыху танней. А калі ў рэзультат той жа самы, навошта плаціць больш. Што зноў прывядзе да знікнення кармушкі.
2. Гэта сумна. Як сумная любая малаасэнсоўная актыўнасць.
3. Як і ў папярэднім варыянце - развіцця ніякага. Але для інжынера мінус у тым, што ў адрозненні ад першага варыянту тут трэба ўвесь час генераваць ИБД. А гэта забірае час. Якое можна выдаткаваць з карысцю для сябе каханага. Бо сам пра сябе не паклапоцішся, усім на цябе па барабане.

Варыянт 3-Не трэба прыдумляць веласіпед, трэба яго купіць і катацца.

Інжынеры іншых кампаній не дарма ядуць піцу запіваючы півам (эх, хвалебныя часы Піцер 90-х). Давайце выкарыстоўваць маніторынгавыя сістэмы, якія зроблены, адладжаны і працуюць, і прыносяць наогул кажучы карысць (ну як мінімум іх стваральнікам).
Пернікі і пышкі, сінякі і шышкіПернікі і пышкі:
1. Не трэба марнаваць час на прыдумванне таго, што і так прыдумана. Бяры і карыстайся.
2. Сістэмы маніторынгу пішуць не дурні і яны вядома ж карысныя.
3. Якія працуюць сістэмы маніторынгу як правіла даюць карысную адфільтраваную інфармацыю.
Сінякі і гузы:
1. Інжынер у дадзеным выпадку не інжынер, а ўсяго толькі карыстач чужога прадукта.Або юзэр.
2. Заказчыка трэба пераканаць у неабходнасці купіць нешта ў чым ён разбірацца ўвогуле кажучы не хоча, ды і не павінен і наогул бюджэт на год зацверджаны і мяняцца не будзе. Потым трэба вылучыць асобны рэсурс, настроіць пад пэўную сістэму. Г.зн. спачатку трэба плаціць, плаціць і яшчэ раз заплаціць. А заказчык скупы. Гэта норма гэтага жыцця.

Што ж рабіць - Чарнышэўскі? Тваё пытанне вельмі дарэчны. (с)

У дадзеным канкрэтным выпадку і сітуацыі, якая склалася можна паступіць крыху па-іншаму. а давайце зробім сваю ўласную сістэму маніторынгу.
Маніторынг прадукцыйнасці запытаў PostgreSQL. Частка 1 - рэпартынг
Ну не сістэму вядома, у поўным сэнсе слова, гэта занадта гучна сказанае і саманадзейна, але хоць неяк палегчыць сабе задачу і сабраць пабольш інфармацыі для рашэння інцыдэнтаў прадукцыйнасці. Каб не аказвацца ў сітуацыі - "ідзі туды не ведаю куды, знайдзі тое, не ведаю што".

Якія ж плюсы і мінусы гэтага варыянту:

Плюсы:
1. Гэта цікава. Ну як мінімум цікавей, чым сталыя "shrink datafile, alter tablespace, etc."
2. Гэта новыя скілы і новае развіццё. Што ў перспектыве рана ці позна дасць заслужаныя пернікі і пышкі.
Мінусы:
1. Прыйдзецца працаваць. Працаваць шмат.
2. Прыйдзецца рэгулярна тлумачыць сэнс і далягляды ўсёй актыўнасці.
3. Нечым давядзецца ахвяраваць, бо адзіны даступны інжынеру рэсурс-час — абмежаваны Сусвету.
4. Самае страшнае і самае непрыемнае — у выніку можа атрымаецца бздура тыпу «Не мышаня, не жаба, а невядомая звярка».

Хто не рызыкуе не п'е шампанскае.
Такім чынам - пачынаецца самае цікавае.

Агульная ідэя - схематычна

Маніторынг прадукцыйнасці запытаў PostgreSQL. Частка 1 - рэпартынг
(Ілюстрацыя ўзята з артыкула «Сінтэз як адзін з метадаў паляпшэння прадукцыйнасці PostgreSQL»)

Тлумачэнне:

  • У мэтавай базе ўсталёўваецца стандартнае пашырэнне PostgreSQL – "pg_stat_statements".
  • У базе дадзеных маніторынгу ствараем набор сэрвісных табліц для захоўвання гісторыі pg_stat_statements на пачатковым этапе і для наладкі метрык і маніторынгу ў далейшым
  • На хасце маніторынгу ствараем набор bash-скрыптоў, у тым ліку для генерацыі інцыдэнтаў у тыкетнай сістэме.

Сэрвісныя табліцы

Для пачатку схематычна-спрошчаная ERD, што ж атрымалася ў выніку:
Маніторынг прадукцыйнасці запытаў PostgreSQL. Частка 1 - рэпартынг
Кароткае апісанне табліцканчатковая кропка - хост, кропка падлучэння да інстансу
база дадзеных - Параметры базы дадзеных
pg_stat_history - Гістарычная табліца для захоўвання часовых здымкаў прадстаўлення pg_stat_statements мэтавай базы дадзеных
metric_glossary - слоўнік метрык прадукцыйнасці
metric_config - канфігурацыя асобных метрык
метрычны - канкрэтная метрыка для запыту які маніторыцца
metric_alert_history - гісторыя папярэджанняў прадукцыйнасці
log_query — службовая табліца для захоўвання разабраных запісаў з log файла PostgreSQL, які загружаецца з AWS
базавая лінія - Параметры часовага перыяды выкарыстоўванага ў якасці базавага
КПП - канфігурацыя метрык праверкі стану базы дадзеных
checkpoint_alert_history - гісторыя папярэджанняў метрык праверкі стану базы дадзеных
pg_stat_db_queries - службовая табліца актыўных запытаў
activity_log - службовая табліца часопіса актыўнасці
trap_oid - службовая табліца канфігурацыі trap

Этап 1 - збіраем статыстычную інфармацыю аб прадукцыйнасці і атрымліваем справаздачы

Для захоўвання статыстычнай інфармацыі служыць табліца pg_stat_history
Структура табліцы pg_stat_history

                                          Table "public.pg_stat_history" Column | Type | Modifiers ---------------------+--------------------------- --+------------------------------------------- id | integer | not null default nextval('pg_stat_history_id_seq'::regclass) snapshot_timestamp | timestamp without time zone | database_id | integer | 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 | baseline_id | integer | Indexes: "pg_stat_history_pkey" PRIMARY KEY, btree (id) "database_idx" btree (database_id) "queryid_idx" btree (queryid) "snapshot_timestamp_idx" btree (snapshot_timestamp) Foreign-key constra _id) REFERENCES database(id ) ON DELETE CASCADE

Як бачна, табліца ўяўляе сабой усяго толькі кумулятыўныя дадзеныя прадстаўлення pg_stat_statements у мэтавай базе дадзеных.

Выкарыстанне гэтай табліцы вельмі простае

pg_stat_history будзе ўяўляць сабой назапашаную статыстыку выканання запытаў за кожную гадзіну. У пачатку кожнай гадзіны, пасля запаўнення табліцы, статыстыка pg_stat_statements скідаецца з дапамогай pg_stat_statements_reset().
Заўвага: статыстыка збіраецца для запытаў, з працягласцю выканання больш за 1 секунду.
Запаўненне табліцы 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;

У выніку, пасля некаторага перыяду часу ў табліцы pg_stat_history у нас будзе набор здымкаў змесціва табліцы pg_stat_statements мэтавай базы дадзеных.

Уласна рэпартынг

Выкарыстоўваючы простыя запыты, можна атрымаць цалкам карысныя і цікавыя справаздачы.

Агрэгаваныя дадзеныя за зададзены прамежак часу

Запыт

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 ;

DB Time

to_char(interval '1 millisecond' * pg_total_stat_history_rec.total_time, 'HH24:MI:SS.MS')

I/O Time

to_char(interval '1 millisecond' * ( pg_total_stat_history_rec.blk_read_time + pg_total_stat_history_rec.blk_write_time ), 'HH24:MI:SS.MS')

TOP10 SQL by total_time

Запыт

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 BY TOTAL EXECUTION TIME | #| queryid| calls| calls %| total_time (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 у цэлым I/O time

Запыт

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 BY TOTAL I/O TIME | #| queryid| calls| calls %| I/O time (ms)|db I/O time % +----+-----------+-----------+------ -----+--------------------------------+----------- - | 1| 4152624390 | 2| .00001|00:08:31.616( 511616.592 ms.)| 31.06/2 | 821760255| 2 | 00001| .00|08:27.099:507099.036( 30.78 ms.)| 3 | 655729273| 1 | 00000| .00|05:02.209:302209.137( 18.35 ms.)| 4 | 2460318461| 1 | 00000| .00|04:05.981:245981.117( 14.93 ms.)| 5 | 1484454471| 4 | 00001| .00|00:39.144:39144.221( 2.38 ms.)| 6 | 2194493487| 4 | 00001| .00|00:18.182:18182.816( 1.10 ms.)| 7 | 1053044345| 1 | 00000| .00|00:16.611:16611.722( 1.01 ms.)| 8 | 3644780286| 1 | 00000| .00|00:00.436:436.205( 03 ms.)| .XNUMX

TOP10 SQL паводле max time of execution

Запыт

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 BY MAX EXECUTION TIME | #| snapshot| snapshotID| queryid| max_time (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 by SHARED buffer read/write

Запыт

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 BY SHARED BUFFER READ / WRITE | #| snapshot| snapshotID| queryid| shared blocks read| shared blocks write +----+------------------+-----------+---------- -+---------------------+--------------------- | 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 ------------------------------------------------- -------------------------------------------

Гістаграма размеркавання запытаў па максімальным часе выканання

запыты

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 ) ;
|------------------------------------------------- ---------------------------------------------- | MAX_TIME HISTOGRAM | TOTAL CALLS : 33851920 | MIN TIME: 00:00:01.063 | MAX TIME : 00:02:01.869 ------------------------------------------ --------------------------------------- | min duration| max duration| calls +----------------------------------+------------- ---------------------+---------- | 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 Snapshots by Query per Second

запыты

--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 Snapshots ordered by QueryPerSeconds numbers -------------------------------------------- -------------------------------------------------- ------------------------------------------------- | #| snapshot| snapshotID| calls| total dbtime| QPS| I/O time| I/O time % +-----+------------------+-----------+------- ----+----------------------------------+---------- -+----------------------------------+----------- | 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

Hourly Execution History with QueryPerSeconds and I/O Time

Запыт

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

Text of all SQL-selects

Запыт

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

Вынік

Як відаць, даволі простымі сродкамі, можна атрымаць дастаткова шмат карыснай інфармацыі аб загружанасці і стане базы.

Заўвага:Калі ў запытах фіксаваць queryid то атрымаем гісторыю па асобным запыце (з мэтай эканоміі месца справаздачы па асобным запыце апушчаны).

Такім чынам, статыстычныя дадзеныя аб прадукцыйнасці запытаў - маюцца і збіраюцца.
Першы этап "збор статыстычных дадзеных" - завершаны.

Можна пераходзіць да другога этапу-«настройка метрык прадукцыйнасці».
Маніторынг прадукцыйнасці запытаў PostgreSQL. Частка 1 - рэпартынг

Але гэта ўжо зусім іншая гісторыя.

Працяг будзе…

Крыніца: habr.com

Дадаць каментар