Je zde několik otázek.
1) Proč nemůžeme provést přírůstek v transakci, kterou nelze přerušit jiným příkazem?
Nejprve si prosím povšimněte, že „transakce“ Redis jsou zcela odlišné od toho, co si většina lidí myslí, že transakce jsou v klasickém DBMS.
# Does not work
redis.multi()
current = redis.get('powerlevel')
redis.set('powerlevel', current + 1)
redis.exec()
Musíte rozumět tomu, co se spouští na straně serveru (v Redis) a co se spouští na straně klienta (ve vašem skriptu). Ve výše uvedeném kódu budou příkazy GET a SET provedeny na straně Redis, ale přiřazení k proudu a výpočet proudu + 1 se předpokládá na straně klienta.
Aby byla zaručena atomicita, blok MULTI/EXEC zpožďuje provádění příkazů Redis až do spuštění exec. Klient tedy pouze nahromadí příkazy GET a SET do paměti a provede je najednou a nakonec atomicky. Samozřejmě, že pokus o přiřazení proudu k výsledku GET a inkrementaci proběhne mnohem dříve. Metoda redis.get ve skutečnosti vrátí pouze řetězec "QUEUED" jako signál, že příkaz je zpožděn, a inkrementace nebude fungovat.
V blocích MULTI/EXEC můžete používat pouze příkazy, jejichž parametry mohou být plně známy před začátkem bloku. Možná si budete chtít přečíst dokumentaci pro více informací.
2) Proč musíme před zahájením transakce opakovat a čekat, až nikdo nezmění hodnotu?
Toto je příklad souběžného optimistického vzoru.
Pokud bychom nepoužili žádné WATCH/MULTI/EXEC, měli bychom potenciální závod:
# Initial arbitrary value
powerlevel = 10
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: SET powerlevel 11
session B: SET powerlevel 11
# In the end we have 11 instead of 12 -> wrong
Nyní přidáme blok WATCH/MULTI/EXEC. S klauzulí WATCH se příkazy mezi MULTI a EXEC provádějí pouze v případě, že se hodnota nezměnila.
# Initial arbitrary value
powerlevel = 10
session A: WATCH powerlevel
session B: WATCH powerlevel
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: MULTI
session B: MULTI
session A: SET powerlevel 11 -> QUEUED
session B: SET powerlevel 11 -> QUEUED
session A: EXEC -> success! powerlevel is now 11
session B: EXEC -> failure, because powerlevel has changed and was watched
# In the end, we have 11, and session B knows it has to attempt the transaction again
# Hopefully, it will work fine this time.
Nemusíte tedy opakovat a čekat, dokud nikdo nezmění hodnotu, ale spíše se pokoušet o operaci znovu a znovu, dokud si Redis nebude jist, že hodnoty jsou konzistentní, a signalizuje, že je úspěšná.
Ve většině případů, pokud jsou „transakce“ dostatečně rychlé a pravděpodobnost sporu je nízká, jsou aktualizace velmi účinné. Nyní, pokud dojde ke sporu, bude třeba provést některé operace navíc pro některé „transakce“ (kvůli iteraci a opakování). Data však budou vždy konzistentní a není vyžadováno žádné zamykání.