| 3 | | Трансакција е логичка целина од повеќе операции над базата на податоци. Тие располагаат со повеќе својства: |
| 4 | | |
| 5 | | * -атомичност: или сите операции ќе се извршат(commit) или ниедна нема да се зачува(rollback). Пример ако внесеме нов играч и во истата трансакција внесуваме статистики за тој играч, не сакаме да имаме статистики без играч |
| 6 | | |
| | 3 | Трансакција е логичка целина од повеќе операции над базата на податоци. Тие располагаат со повеќе својства (ACID својства): |
| | 4 | |
| | 5 | * атомичност: или сите операции ќе се извршат(commit) или ниедна нема да се зачува(rollback). Пример ако внесеме нов играч и во истата трансакција внесуваме статистики за тој играч, не сакаме да имаме статистики без играч |
| | 6 | |
| | 7 | * конзистентност: многу е важно секоја трансакција да го одржува интегритетот на базата, односно секој запис да ги почитува ограничувањата на базата. На пример, во базата, играчот мора да има валиден тим, во спротивно нема да се изврши трансакцијата. |
| | 8 | |
| | 9 | * изолација: трансакциите се изолирани една од друга. Така, во PostgreSQL нивото на изолација најчесто е READ COMMITTED, што значи дека една трансакција гледа само податоци кои се веќе извршени. Пример: додека една трансакција ажурира вредност, друга не може да види полу-променети податоци. |
| | 10 | |
| | 11 | * трајност: најважно од се е промените да се запишат и зачуваат по извршувањето. За тоа PostgreSQL користи WAL (ќе го споменеме понатаму). Ако серверот се рестартира, податоците нема да бидат изгубени по извршена трансакција. |
| | 12 | |
| | 13 | === Изолациони нивоа |
| | 14 | |
| | 15 | Изолационите нивоа дефинираат колку една трансакција ќе биде изолирана од другите. Во PostgreSQL постојат: |
| | 16 | |
| | 17 | * READ UNCOMMITTED: трансакцијата може да чите и непотврдени промени (dirty reads) |
| | 18 | |
| | 19 | * READ COMMITTED: трансакцијата чита само комитирани промени |
| | 20 | |
| | 21 | * REPEATABLE READ: сите SELECT операции во една трансакција гледаат исти податоци |
| | 22 | |
| | 23 | * SERIALIZABLE: највисоко ниво на изолација - трансакциите се случуваат една по друга |
| | 24 | |
| | 25 | READ COMMITTED vs SERIALIZABLE |
| | 26 | |
| | 27 | {{{ |
| | 28 | BEGIN; |
| | 29 | |
| | 30 | |
| | 31 | |
| | 32 | SELECT highest_market_value_in_eur |
| | 33 | |
| | 34 | FROM players |
| | 35 | |
| | 36 | WHERE player_id = 26; |
| | 37 | |
| | 38 | UPDATE players |
| | 39 | |
| | 40 | SET highest_market_value_in_eur = 7000000 |
| | 41 | |
| | 42 | WHERE player_id = 26; |
| | 43 | |
| | 44 | |
| | 45 | |
| | 46 | COMMIT; |
| | 47 | |
| | 48 | SELECT highest_market_value_in_eur |
| | 49 | |
| | 50 | FROM players |
| | 51 | |
| | 52 | WHERE player_id = 26; |
| | 53 | |
| | 54 | |
| | 55 | |
| | 56 | |
| | 57 | |
| | 58 | COMMIT; |
| | 59 | }}} |
| | 60 | |
| | 61 | Истата трансакција гледа различна вредност, ова е дозволено во READ COMMITTED |
| | 62 | |
| | 63 | {{{ |
| | 64 | BEGIN ISOLATION LEVEL SERIALIZABLE; |
| | 65 | |
| | 66 | |
| | 67 | |
| | 68 | SELECT highest_market_value_in_eur |
| | 69 | |
| | 70 | FROM players |
| | 71 | |
| | 72 | WHERE player_id = 26; |
| | 73 | |
| | 74 | |
| | 75 | |
| | 76 | UPDATE players |
| | 77 | |
| | 78 | SET highest_market_value_in_eur = 9000000 |
| | 79 | |
| | 80 | WHERE player_id = 26; |
| | 81 | |
| | 82 | COMMIT; |
| | 83 | |
| | 84 | |
| | 85 | |
| | 86 | |
| | 87 | |
| | 88 | SELECT highest_market_value_in_eur |
| | 89 | |
| | 90 | FROM players |
| | 91 | |
| | 92 | WHERE player_id = 26; |
| | 93 | |
| | 94 | |
| | 95 | |
| | 96 | |
| | 97 | |
| | 98 | COMMIT; |
| | 99 | }}} |
| | 100 | |
| | 101 | Serializable нема non-repeatable read, конзистентност на ниво на трансакција. |
| | 102 | |
| | 103 | === Deadlocks |
| | 104 | |
| | 105 | Deadlock настанува кога две или повеќе трансакции меѓусебно се блокираат, најчесто поради тоа што едната чека ресурс што го содржи другата. Најчеста причина за појавување на deadlock е неконзистентен редослед на заклучување на редови. |
| | 106 | |
| | 107 | {{{ |
| | 108 | BEGIN; |
| | 109 | |
| | 110 | |
| | 111 | |
| | 112 | SELECT * FROM players WHERE player_id = 10 FOR UPDATE; |
| | 113 | |
| | 114 | |
| | 115 | |
| | 116 | SELECT * FROM players WHERE player_id = 20 FOR UPDATE; |
| | 117 | |
| | 118 | |
| | 119 | |
| | 120 | COMMIT; |
| | 121 | |
| | 122 | BEGIN; |
| | 123 | |
| | 124 | |
| | 125 | |
| | 126 | SELECT * FROM players WHERE player_id = 20 FOR UPDATE; |
| | 127 | |
| | 128 | |
| | 129 | |
| | 130 | SELECT * FROM players WHERE player_id = 10 FOR UPDATE; |
| | 131 | |
| | 132 | |
| | 133 | |
| | 134 | COMMIT; |
| | 135 | }}} |
| | 136 | |
| | 137 | Овие две трансакции ажурираат два играчи, но во различен редослед. PostgreSQL детектира deadlock, но една многу добра карактеристика на PostgreSQL e тоа што има deadlock detector при што автоматски прекинува една трансакција. |
| | 138 | |
| | 139 | Како да избегнеме deadlock? |
| | 140 | |
| | 141 | {{{ |
| | 142 | SELECT * FROM players |
| | 143 | |
| | 144 | WHERE player_id IN (10, 20) |
| | 145 | |
| | 146 | ORDER BY player_id |
| | 147 | |
| | 148 | FOR UPDATE; |
| | 149 | }}} |
| | 150 | |
| | 151 | Deadlock состојбите претставуваат сериозен предизвик во системи со висока конкурентност. PostgreSQL користи автоматска детекција на deadlock, при што една од трансакциите се прекинува за да се избегне застој на системот. Примената на конзистентен редослед на заклучување и краткотрајни трансакции значително го намалува ризикот од вакви ситуации. |
| | 152 | |
| | 153 | === Locks и MVCC во PostgreSQL |
| | 154 | |
| | 155 | Во системи со повеќе истовремени корисници, базата на податоци мора да обезбеди конзистентност и изолација на податоците, без притоа да се жртвуваат перформансите. PostgreSQL го решава овој проблем преку комбинација на механизми за заклучување (locking) и Multi-Version Concurrency Control (MVCC). |
| | 156 | |
| | 157 | Во овој дел се прикажува разликата помеѓу table-level locking, row-level locking и MVCC, со практични примери кои покажуваат како различните пристапи влијаат врз конкурентноста, блокирањето и видливоста на податоците помеѓу трансакции. |
| | 158 | |
| | 159 | Table-level locking |
| | 160 | |
| | 161 | * целата табела е заклучена |
| | 162 | * други трансакции чекаат, дури и ако работат на друг ред |
| | 163 | * многу лоша конкурентност |
| | 164 | |
| | 165 | {{{ |
| | 166 | BEGIN; |
| | 167 | |
| | 168 | LOCK TABLE players IN ACCESS EXCLUSIVE MODE; |
| | 169 | |
| | 170 | -- табелата е целосно заклучена |
| | 171 | |
| | 172 | SELECT * FROM players; (ACCESS EXCLUSIVE е најсилен lock) |
| | 173 | }}} |
| | 174 | |
| | 175 | Row-level locking |
| | 176 | |
| | 177 | * многу подобра верзија од table-level locking |
| | 178 | * се заклучува само конкретниот ред, другите редови се слободни |
| | 179 | |
| | 180 | {{{ |
| | 181 | BEGIN; |
| | 182 | |
| | 183 | SELECT * FROM players WHERE player_id = 1 FOR UPDATE; |
| | 184 | |
| | 185 | -- само редот со id=1 е заклучен |
| | 186 | |
| | 187 | SELECT * FROM players WHERE player_id = 2; |
| | 188 | |
| | 189 | -- работи нормално |
| | 190 | |
| | 191 | UPDATE players SET highest_market_value_in_eur = highest_market_value_in_eur - 100 WHERE player_id = 1; |
| | 192 | |
| | 193 | -- чека, редот е заклучен |
| | 194 | }}} |
| | 195 | |
| | 196 | MVCC(Multi-Version Concurrency Control) |
| | 197 | |
| | 198 | * наместо locks, PostgreSQL креира нови верзии на редиците (помалку блокирања) |
| | 199 | |
| | 200 | {{{ |
| | 201 | -- Session 1 |
| | 202 | BEGIN; |
| | 203 | |
| | 204 | UPDATE players |
| | 205 | SET market_value_in_eur = market_value_in_eur - 5000000 |
| | 206 | WHERE player_id = 19054; |
| | 207 | |
| | 208 | -- трансакцијата е започната, но не е направен COMMIT |
| | 209 | }}} |
| | 210 | |
| | 211 | {{{ |
| | 212 | -- Session 2 |
| | 213 | SELECT market_value_in_eur |
| | 214 | FROM players |
| | 215 | WHERE player_id = 19054; |
| | 216 | -- Session 2 ја гледа старата вредност |
| | 217 | }}} |
| | 218 | |
| | 219 | {{{ |
| | 220 | -- Session 1 |
| | 221 | COMMIT; |
| | 222 | }}} |
| | 223 | |
| | 224 | {{{ |
| | 225 | -- Session 2 |
| | 226 | SELECT market_value_in_eur |
| | 227 | FROM players |
| | 228 | WHERE player_id = 19054; |
| | 229 | -- сега Session 2 ја гледа новата вредност |
| | 230 | }}} |
| | 231 | |
| | 232 | MVCC е одличен бидејќи нема блокирање на SELECT операции, нема dirty reads и има висока конкурентност. |