=== Мониторинг == Query Latency Distribution Просечниот execution time не ја претставува реалната состојба во системот. На пример 990 прашалници имаат време на извршување 1мс, додека 10 имаат 5000мс. Просекот е 50мс, но може да видиме дека тие 10 прашалници се outliers. Токму овие 10 спори прашалници можат да го нарушат корисничкото искуство. Пример за брз прашалник: {{{ SELECT * FROM players WHERE player_id = 10; }}} Пример за спор прашалник: {{{ SELECT p.player_name, COUNT(t.transfer_id), AVG(t.transfer_fee), SUM(m.attendance) FROM players p JOIN transfers t ON p.player_id = t.player_id JOIN matches m ON m.home_team_id = p.club_id OR m.away_team_id = p.club_id GROUP BY p.player_name; }}} Исто така, имаме два типа на прашалници: еден што има 10000 повици на ден со време на извршување 2мс и друг со 50 повици на ден со 5 секунди време на извршување. Вториот прашалник претставува поголем проблем за нашиот систем бидејќи корисникот не сака да чека slow response. == Hot Data vs Cold Data Monitoring Како што веќе видовме, системот располага со повеќе табели. Некои од нив се користат повеќе од останатите и може да се каже дека во реален систем 20% од податоците генерираат 80% од оптоварувањето. Класичен пример може да најдеме во табелата players. Од сите играчи кои се во табелата, најинтересни се познатите играчи - топ играчи. Токму затоа овие играчи ќе се најдат во повеќе прашалници од останати играчи и ќе се создаде row-level contention. Мониторинг резултат од гореспоменатиот проблем е тоа што играчот со id 10 ќе има 1200 queries/min, додека тој со id 187 ќе има 5 queries/min. Истите сценарија можат да се најдат и во други табели. Од друга страна, имаме и cold data која ретко се користи, нема locks и contention. == Пример Во Transfermarkt системот, „deadline day“ претставува екстремен случај на оптоварување, при што во краток временски интервал се извршуваат илјадници трансфер операции. За разлика од нормалниот режим, каде системот е претежно read-oriented, во овој период се јавува нагло зголемување на write операции и конкурентни трансакции. Во наредните примери ќе видиме и зашто ова е едно од најпредизивикувачките сценарија во системот. 1. Complex Transfer Transaction Секој трансфер не е едноставен INSERT, туку составена трансакција: {{{ BEGIN; INSERT INTO transfers(player_id, from_club_id, to_club_id, transfer_fee, season) VALUES (25, 3, 8, 12000000, '2025'); UPDATE players SET market_value = market_value + 1500000, club_id = 8 WHERE player_id = 25; UPDATE clubs SET budget = budget - 12000000 WHERE club_id = 8; UPDATE clubs SET budget = budget + 12000000 WHERE club_id = 3; COMMIT; }}} Може да видиме дека имаме 4 write операции во 3 различни табели и сето ова е само за еден играч, додека на deadline day имаме илјадници трансфери. Исто така, системот влегува во contention режим поради тоа што сите трансакции таргетираат исти ентитети (player_id = 25, club_id = 8) и latency и queries/sec се зголемуваат драстично. Уште еден проблем со кој се соочуваме е transaction queueing, а тоа се случува бидејќи трансакцијата која се извршува зад себе остава други трансакции кои чекаат ресурси. Овој проблем е чест и во нормални услови, но на deadline day е многу посериозен. == Спојување на две табели во една Во последниот пример ќе направиме денормализација на системот, односно табелите players и player_valuations ќе ги споиме во една и ќе споредиме што е подобро. {{{ SELECT p.last_name, pv.market_value_in_eur, pv.date FROM players p JOIN player_valuations pv ON p.player_id = pv.player_id WHERE p.player_id = 10 ORDER BY pv.date DESC LIMIT 1; }}} Ова query има JOIN и ORDER BY + LIMIT. {{{ CREATE TABLE players_with_value AS SELECT p.player_id, p.last_name, p.current_club_id, pv.market_value_in_eur, pv.date FROM players p JOIN player_valuations pv ON p.player_id = pv.player_id; }}} {{{ SELECT last_name, market_value_in_eur FROM players_with_value WHERE player_id = 10 ORDER BY date DESC LIMIT 1; }}} Користејќи ја новокреираната табела извршивме query кој не користи JOIN и има побрз пристап. Сепак, целосна денормализација не е најдоброто решение. За најдобри резултати во нашиот систем ќе искористиме partial denormalization, односно ќе направиме нова табела со моменталната/последната вредност на играчот: {{{ CREATE TABLE players_current_value AS SELECT DISTINCT ON (player_id) player_id, market_value, date FROM player_valuations ORDER BY player_id, date DESC; }}} Предности што ги добиваме со ова се тоа што нема непотребно скенирање на целата историја доколку корисникот не ја бара, помалку write операции и помал contention.