Інжынер - у перакладзе з латыні - натхнёны.
Інжынер можа ўсё. (с) Р.Дызель.
Эпіграфы.
Або гісторыя аб тым, навошта адміністратару баз дадзеных успамінаць сваё праграмісцкае мінулае.
Прадмова
Усе імёны зменены. Супадзенні выпадковыя. Матэрыял уяўляе сабой выключна асабістае меркаванне аўтара.
Адмова ад гарантый: у планаваным цыкле артыкулаў не будзе падрабязнага і дакладнага апісання выкарыстоўваных табліц і скрыптоў. Матэрыялы не атрымаецца адразу выкарыстоўваць "AS IS".
Па-першае, з прычыны вялікага аб'ёму матэрыялу,
па-другое па прычыне заменчанасці з прадакшн базай рэальнага заказчыка.
Таму ў артыкулах будуць прыведзены толькі ідэі і апісанні ў самым агульным выглядзе.
Можа быць у будучыні сістэма дарасце да ўзроўню выкладвання на GitHub, а можа быць і не. Час пакажа.
Пачатак гісторыі-
Што атрымалася ў выніку, у самых агульных рысах-
Навошта мне ўсё гэта?
Ну, па-першае, каб самому не забыцца, успамінаючы на пенсіі слаўныя дзянькі.
Па-другое, каб сістэматызаваць напісанае. Бо ўжо сам, часам пачынаю блытацца і забывацца на асобныя часткі.
Ну і самае галоўнае - раптам можа камусьці спатрэбіцца і дапаможа не вынаходзіць ровар і не збіраць граблі. Іншымі словамі, палепшыць сваю карму (не хаброўскую). Бо самае каштоўнае ў гэтым свеце гэта ідэі. Галоўнае знайсці ідэю. А рэалізаваць ідэю ў рэальнасць гэта ўжо пытанне чыста тэхнічнае.
Такім чынам, пачнем, паціху…
Пастаноўка задачы.
Маецца:
База дадзеных 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. Заказчыка трэба пераканаць у неабходнасці купіць нешта ў чым ён разбірацца ўвогуле кажучы не хоча, ды і не павінен і наогул бюджэт на год зацверджаны і мяняцца не будзе. Потым трэба вылучыць асобны рэсурс, настроіць пад пэўную сістэму. Г.зн. спачатку трэба плаціць, плаціць і яшчэ раз заплаціць. А заказчык скупы. Гэта норма гэтага жыцця.
Што ж рабіць - Чарнышэўскі? Тваё пытанне вельмі дарэчны. (с)
У дадзеным канкрэтным выпадку і сітуацыі, якая склалася можна паступіць крыху па-іншаму. а давайце зробім сваю ўласную сістэму маніторынгу.
Ну не сістэму вядома, у поўным сэнсе слова, гэта занадта гучна сказанае і саманадзейна, але хоць неяк палегчыць сабе задачу і сабраць пабольш інфармацыі для рашэння інцыдэнтаў прадукцыйнасці. Каб не аказвацца ў сітуацыі - "ідзі туды не ведаю куды, знайдзі тое, не ведаю што".
Якія ж плюсы і мінусы гэтага варыянту:
Плюсы:
1. Гэта цікава. Ну як мінімум цікавей, чым сталыя "shrink datafile, alter tablespace, etc."
2. Гэта новыя скілы і новае развіццё. Што ў перспектыве рана ці позна дасць заслужаныя пернікі і пышкі.
Мінусы:
1. Прыйдзецца працаваць. Працаваць шмат.
2. Прыйдзецца рэгулярна тлумачыць сэнс і далягляды ўсёй актыўнасці.
3. Нечым давядзецца ахвяраваць, бо адзіны даступны інжынеру рэсурс-час — абмежаваны Сусвету.
4. Самае страшнае і самае непрыемнае — у выніку можа атрымаецца бздура тыпу «Не мышаня, не жаба, а невядомая звярка».
Хто не рызыкуе не п'е шампанскае.
Такім чынам - пачынаецца самае цікавае.
Агульная ідэя - схематычна
(Ілюстрацыя ўзята з артыкула «
Тлумачэнне:
- У мэтавай базе ўсталёўваецца стандартнае пашырэнне PostgreSQL – "pg_stat_statements".
- У базе дадзеных маніторынгу ствараем набор сэрвісных табліц для захоўвання гісторыі pg_stat_statements на пачатковым этапе і для наладкі метрык і маніторынгу ў далейшым
- На хасце маніторынгу ствараем набор bash-скрыптоў, у тым ліку для генерацыі інцыдэнтаў у тыкетнай сістэме.
Сэрвісныя табліцы
Для пачатку схематычна-спрошчаная ERD, што ж атрымалася ў выніку:
Кароткае апісанне табліцканчатковая кропка - хост, кропка падлучэння да інстансу
база дадзеных - Параметры базы дадзеных
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 то атрымаем гісторыю па асобным запыце (з мэтай эканоміі месца справаздачы па асобным запыце апушчаны).
Такім чынам, статыстычныя дадзеныя аб прадукцыйнасці запытаў - маюцца і збіраюцца.
Першы этап "збор статыстычных дадзеных" - завершаны.
Можна пераходзіць да другога этапу-«настройка метрык прадукцыйнасці».
Але гэта ўжо зусім іншая гісторыя.
Працяг будзе…
Крыніца: habr.com