Modulelessen (3/4)
Vergrendelen en SELECTEREN VOOR UPDATE
Begrijpen hoe een database geïsoleerd op een enkele terminal werkt, is eenvoudig... nee wacht, dat heb ik al gezegd!
Soms zijn 'Atomic Updates' (bijvoorbeeld stock = stock - 1) niet voldoende.
Stel je voor dat je moet controleren of de gebruiker voldoende saldo heeft om voor € 50 aan spullen te kopen:
SELECT balance FROM users WHERE id = 1;- JS: Als het saldo < 50 is, blokkeer ze dan!
- Ga anders verder met afrekenen...
- Voer de aftrek uit! (En hier vind je een gebruiker die, terwijl hij de cheque bij stap 2 valideerde, nog een parallelle cheque afvuurde die door zijn account brandde en nu ga je onder nul!).
VOOR UPDATE Expliciete vergrendeling
Door FOR UPDATE toe te voegen aan het einde van een SELECT transformeert je onschuldige read op magische wijze in een verzegeld slot tegen andere klanten tot de volgende COMMIT. Niemand anders kan UPDATE (of gelijktijdig lezen voor update) uitvoeren op die specifiek geselecteerde rijen totdat u besluit wat u ermee gaat doen!
BEGIN;
-- If id=3 had already been locked by T1, T2 will sit in perpetual loading on this "read"
-- frozen in place, before receiving the raw data, until T1 runs COMMIT freeing everyone!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;
-- at this point our Node JS instance lives with the guarantee, knowing for certain that NO ONE ELSE in the world has
-- manipulated (nor been able to read with intent to alter) that balance while we complete the application logic
UPDATE users SET balance = balance - 50 WHERE id = 3;
COMMIT;Er is zelfs FOR SHARE waarmee andere lezers kunnen blijven lezen (als ze een basisvergrendeling hebben) zonder het lezen van de status te blokkeren, maar elke mogelijke volgende update te blokkeren totdat je een commit maakt. Gebruik voor wijzigingen echter altijd de brute kracht van de exclusieve vergrendelingsclausule:
Probeer het zelf
Open een normale tijdsgebonden transactie en vergrendel exclusief het lezen van product-ID=5.\nVoer een query in twee stappen uit:\n1. Open de transactie (BEGIN)\n2. Query 'producten' waarbij de volledige rij voor ID 5 wordt opgehaald met de garantie dat niemand deze zal wijzigen, door de optie 'FOR UPDATE' toe te voegen.
Toon hint
BEGIN;, newline, SELECT * FROM producten WAAR id = 5 VOOR UPDATE;
Oplossing beschikbaar na 3 pogingen
Soms blijft de transactie hangen voor rampzalige perioden (deadlock of oneindige wachttijden).\nOm dit te voorkomen kunt u de database opdracht geven om op te geven op het moment dat de rij niet onmiddellijk beschikbaar is: gebruik NOWAIT aan het einde, waardoor een SQL-serveruitzondering terugkaatst naar JS om te worden opgevangen zonder voor altijd te blijven hangen.\nVoer een SELECT uit voor categorie ID=1 en gebruik FOR UPDATE, maar dwing NOWAIT af.
Toon hint
SELECTEER * UIT categorieën WAAR id = 1 VOOR UPDATE NU;
Oplossing beschikbaar na 3 pogingen