it-swarm-eu.dev

Jak správně implementovat optimistické zamykání v MySQL

Jak správně implementovat optimistické zamykání v MySQL?

Náš tým usoudil, že musíme udělat # 4 níže, jinak existuje riziko, že jiné vlákno může aktualizovat stejnou verzi záznamu, ale rádi bychom ověřili, že je to nejlepší způsob, jak to udělat.

  1. Vytvořte v tabulce pole verze, pro které chcete použít optimistické uzamčení např. název sloupce = "version"
  2. U výběrů nezapomeňte uvést sloupec verze a poznamenat si verzi
  3. Při následné aktualizaci záznamu by měl aktualizační příkaz vydat „kde version = X“, kde X je verze, kterou jsme dostali v # 2, a nastavit pole verze během tohoto aktualizačního příkazu na X + 1
  4. Proveďte SELECT FOR UPDATE v záznamu, který se chystáme aktualizovat, abychom serializovali, kdo může provádět změny v záznamu, který se pokoušíme aktualizovat.

Abychom to vyjasnili, snažíme se zabránit tomu, aby dvě vlákna, která vyberou stejný záznam ve stejném časovém okně, kde popadnou stejnou verzi záznamu, přepsala všechny ostatní, pokud by se pokusily záznam aktualizovat současně. Věříme, že pokud neuděláme # 4, existuje šance, že pokud obě vlákna vstoupí do svých příslušných transakcí současně (ale zatím nevydaly své aktualizace), když jdou na aktualizaci, druhé vlákno, které použije UPDATE ... kde verze = X bude fungovat na starých datech.

Máme pravdu v myšlení, že při aktualizaci musíme provést toto pesimistické zamykání, přestože používáme pole verzí/optimistické zamykání?

13
BestPractices

Váš vývojář se mýlí. Potřebujete buď SELECT ... FOR UPDATE nebo verzování řádků, ne obojí.

Zkuste to a uvidíte. Otevřete tři relace MySQL (A), (B) a (C) do stejné databáze.

V (C) problém:

CREATE TABLE test(
    id integer PRIMARY KEY,
    data varchar(255) not null,
    version integer not null
);
INSERT INTO test(id,data,version) VALUES (1,'fred',0);
BEGIN;
LOCK TABLES test WRITE;

V obou (A) a (B) vydejte UPDATE, který testuje a nastavuje verzi řádku, v každém mění text winner, abyste viděli, která relace je která:

-- In (A):

BEGIN;
UPDATE test SET data = 'winnerA',
            version = version + 1
WHERE id = 1 AND version = 0;

-- in (B):

BEGIN;
UPDATE test SET data = 'winnerB',
            version = version + 1
WHERE id = 1 AND version = 0;

Nyní v (C), UNLOCK TABLES; k uvolnění zámku.

(A) a (B) bude závodit o uzamčení řady. Jeden z nich vyhraje a dostane zámek. Druhý blokuje zámek. Vítěz, který dostal zámek, bude pokračovat ve změně řádku. Za předpokladu (A) je vítěz, nyní můžete vidět změněný řádek (stále nezávazný, takže není viditelný pro jiné transakce) s SELECT * FROM test WHERE id = 1.

Nyní COMMIT ve výherní relaci, řekněte (A).

(B) získá zámek a bude pokračovat v aktualizaci. Verze se však již neshoduje, takže se nezmění žádné řádky, jak je uvedeno ve výsledku počtu řádků. Účinek měl pouze jeden UPDATE a klientská aplikace může jasně vidět, které UPDATE uspěly a které selhaly. Není nutné žádné další zamykání.

Viz protokoly relací v Pastebinu zde . Použil jsem mysql --Prompt="A> " atd., aby bylo snadné rozeznat rozdíl mezi relacemi. Zkopíroval jsem a vložil výstup vložený v časové posloupnosti, takže to není úplně surový výstup a je možné, že jsem mohl udělat chyby při jeho kopírování a vkládání. Vyzkoušejte to sami.


Pokud byste nepřidali pole s řádkem, , pak byste potřebovali na SELECT ... FOR UPDATE aby bylo možné spolehlivě zajistit objednávání.

Pokud o tom přemýšlíte, SELECT ... FOR UPDATE je zcela nadbytečné , pokud okamžitě děláte UPDATE bez opakovaného použití dat z SELECT, nebo pokud používáte verzi verzí řádků. Klávesa UPDATE přesto uzamkne. Pokud někdo jiný aktualizuje řádek mezi vaším čtením a následným zápisem, vaše verze se již nebude shodovat, takže aktualizace selže. Takto funguje optimistické zamykání.

Účel SELECT ... FOR UPDATE je:

  • Spravovat uspořádání zámku, aby se zabránilo uváznutí; a
  • Chcete-li rozšířit rozpětí zámku řádku, když chcete číst data z řádku, změňte je v aplikaci a napište nový řádek založený na původním, aniž byste museli použít izolaci SERIALIZABLE nebo verzi řádků .

Nemusíte používat jak optimistické zamykání (verzování řádků), tak SELECT ... FOR UPDATE. Použijte jeden nebo druhý.

17
Craig Ringer
UPDATE tbl SET owner = $me,
               id = LAST_INSERT_ID(id)
    WHERE owner = ''
    LIMIT 1;
$id = SELECT LAST_INSERT_ID();
Do some stuff (arbitrarily long time)...;
UPDATE  tbl SET owner = '' WHERE id = $id;

Není třeba žádných zámků (nikoli tabulkových, ne transakčních), nebo dokonce požadovaných:

  • UPDATE je atomová
  • LAST_INSERT_ID () je specifické pro relaci, a proto je bezpečné pro podprocesy.
0
Rick James