| | 1 | === Мониторинг |
| | 2 | |
| | 3 | == Query Latency Distribution |
| | 4 | |
| | 5 | Просечниот execution time не ја претставува реалната состојба во системот. На пример 990 прашалници имаат време на извршување 1мс, додека 10 имаат 5000мс. Просекот е 50мс, но може да видиме дека тие 10 прашалници се outliers. Токму овие 10 спори прашалници можат да го нарушат корисничкото искуство. |
| | 6 | |
| | 7 | Пример за брз прашалник: |
| | 8 | {{{ |
| | 9 | SELECT * FROM players WHERE player_id = 10; |
| | 10 | }}} |
| | 11 | |
| | 12 | Пример за спор прашалник: |
| | 13 | {{{ |
| | 14 | SELECT |
| | 15 | p.player_name, |
| | 16 | COUNT(t.transfer_id), |
| | 17 | AVG(t.transfer_fee), |
| | 18 | SUM(m.attendance) |
| | 19 | FROM players p |
| | 20 | JOIN transfers t ON p.player_id = t.player_id |
| | 21 | JOIN matches m ON m.home_team_id = p.club_id |
| | 22 | OR m.away_team_id = p.club_id |
| | 23 | GROUP BY p.player_name; |
| | 24 | }}} |
| | 25 | |
| | 26 | Исто така, имаме два типа на прашалници: еден што има 10000 повици на ден со време на извршување 2мс и друг со 50 повици на ден со 5 секунди време на извршување. Вториот прашалник претставува поголем проблем за нашиот систем бидејќи корисникот не сака да чека slow response. |
| | 27 | |
| | 28 | == Hot Data vs Cold Data Monitoring |
| | 29 | |
| | 30 | Како што веќе видовме, системот располага со повеќе табели. Некои од нив се користат повеќе од останатите и може да се каже дека во реален систем 20% од податоците генерираат 80% од оптоварувањето. |
| | 31 | |
| | 32 | Класичен пример може да најдеме во табелата players. Од сите играчи кои се во табелата, најинтересни се познатите играчи - топ играчи. Токму затоа овие играчи ќе се најдат во повеќе прашалници од останати играчи и ќе се создаде row-level contention. Мониторинг резултат од гореспоменатиот проблем е тоа што играчот со id 10 ќе има 1200 queries/min, додека тој со id 187 ќе има 5 queries/min. Истите сценарија можат да се најдат и во други табели. |
| | 33 | |
| | 34 | Од друга страна, имаме и cold data која ретко се користи, нема locks и contention. |
| | 35 | |
| | 36 | == Пример |
| | 37 | |
| | 38 | Во Transfermarkt системот, „deadline day“ претставува екстремен случај на оптоварување, при што во краток временски интервал се извршуваат илјадници трансфер операции. За разлика од нормалниот режим, каде системот е претежно read-oriented, во овој период се јавува нагло зголемување на write операции и конкурентни трансакции. |
| | 39 | |
| | 40 | Во наредните примери ќе видиме и зашто ова е едно од најпредизивикувачките сценарија во системот. |
| | 41 | |
| | 42 | 1. Complex Transfer Transaction |
| | 43 | |
| | 44 | Секој трансфер не е едноставен INSERT, туку составена трансакција: |
| | 45 | {{{ |
| | 46 | BEGIN; |
| | 47 | |
| | 48 | INSERT INTO transfers(player_id, from_club_id, to_club_id, transfer_fee, season) |
| | 49 | VALUES (25, 3, 8, 12000000, '2025'); |
| | 50 | |
| | 51 | UPDATE players |
| | 52 | SET market_value = market_value + 1500000, |
| | 53 | club_id = 8 |
| | 54 | WHERE player_id = 25; |
| | 55 | |
| | 56 | UPDATE clubs |
| | 57 | SET budget = budget - 12000000 |
| | 58 | WHERE club_id = 8; |
| | 59 | |
| | 60 | UPDATE clubs |
| | 61 | SET budget = budget + 12000000 |
| | 62 | WHERE club_id = 3; |
| | 63 | |
| | 64 | COMMIT; |
| | 65 | }}} |
| | 66 | |
| | 67 | Може да видиме дека имаме 4 write операции во 3 различни табели и сето ова е само за еден играч, додека на deadline day имаме илјадници трансфери. Исто така, системот влегува во contention режим поради тоа што сите трансакции таргетираат исти ентитети (player_id = 25, club_id = 8) и latency и queries/sec се зголемуваат драстично. Уште еден проблем со кој се соочуваме е transaction queueing, а тоа се случува бидејќи трансакцијата која се извршува зад себе остава други трансакции кои чекаат ресурси. Овој проблем е чест и во нормални услови, но на deadline day е многу посериозен. |
| | 68 | |
| | 69 | == Спојување на две табели во една |
| | 70 | |
| | 71 | Во последниот пример ќе направиме денормализација на системот, односно табелите players и player_valuations ќе ги споиме во една и ќе споредиме што е подобро. |
| | 72 | |
| | 73 | {{{ |
| | 74 | SELECT |
| | 75 | p.last_name, |
| | 76 | pv.market_value_in_eur, |
| | 77 | pv.date |
| | 78 | FROM players p |
| | 79 | JOIN player_valuations pv |
| | 80 | ON p.player_id = pv.player_id |
| | 81 | WHERE p.player_id = 10 |
| | 82 | ORDER BY pv.date DESC |
| | 83 | LIMIT 1; |
| | 84 | }}} |
| | 85 | |
| | 86 | Ова query има JOIN и ORDER BY + LIMIT. |
| | 87 | |
| | 88 | {{{ |
| | 89 | CREATE TABLE players_with_value AS |
| | 90 | SELECT |
| | 91 | p.player_id, |
| | 92 | p.last_name, |
| | 93 | p.current_club_id, |
| | 94 | pv.market_value_in_eur, |
| | 95 | pv.date |
| | 96 | FROM players p |
| | 97 | JOIN player_valuations pv |
| | 98 | ON p.player_id = pv.player_id; |
| | 99 | }}} |
| | 100 | {{{ |
| | 101 | SELECT last_name, market_value_in_eur |
| | 102 | FROM players_with_value |
| | 103 | WHERE player_id = 10 |
| | 104 | ORDER BY date DESC |
| | 105 | LIMIT 1; |
| | 106 | }}} |
| | 107 | |
| | 108 | Користејќи ја новокреираната табела извршивме query кој не користи JOIN и има побрз пристап. Сепак, целосна денормализација не е најдоброто решение. За најдобри резултати во нашиот систем ќе искористиме partial denormalization, односно ќе направиме нова табела со моменталната/последната вредност на играчот: |
| | 109 | {{{ |
| | 110 | CREATE TABLE players_current_value AS |
| | 111 | SELECT DISTINCT ON (player_id) |
| | 112 | player_id, |
| | 113 | market_value, |
| | 114 | date |
| | 115 | FROM player_valuations |
| | 116 | ORDER BY player_id, date DESC; |
| | 117 | }}} |
| | 118 | |
| | 119 | Предности што ги добиваме со ова се тоа што нема непотребно скенирање на целата историја доколку корисникот не ја бара, помалку write операции и помал contention. |