sql >> Databáze >  >> RDS >> Mysql

Transakce MySQL:SELECT + INSERT

Co potřebujete, je uzamykání . Transakce skutečně „nejsou nezbytně nutné“.

Můžete si vybrat mezi "pesimistickým zamykáním" a "optimistickým zamykáním". Rozhodnutí o tom, která z těchto dvou možností je na vás a musí být vyhodnoceno v zásadě s ohledem na:

  • úroveň souběžnosti, kterou máte
  • doba trvání nezbytných atomických operací v databázi
  • složitost celé operace

Doporučuji přečíst si tyto dva, abyste si vytvořili představu o tom, co se týká:

Příklad pro lepší vysvětlení

Možná to není tak elegantní, ale je to pouze příklad, který ukazuje, jak je možné dělat vše bez transakce (a dokonce bez omezení UNIQUE). Co je třeba udělat, je použít následující kombinovaný statemet INSERT + SELECT a po jeho provedení zkontrolovat počet ovlivněných řádků. Pokud je počet ovlivněných řádků 1, uspěl v opačném případě (pokud je 0), došlo ke kolizi a druhá strana vyhrála.

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= @endTime AND `end` >= @startTime
    AND `devices_id` = @deviceId)
GROUP BY (1);

Toto je příklad optimistického zamykání získaného bez transakcí a s jedinou operací SQL.

Jak je psáno, má problém, že v slot již musí být alespoň jeden řádek tabulky, aby fungovala (jinak klauzule SELECT vždy vrátí prázdnou sadu záznamů a v takovém případě se nic nevloží evei, pokud nedojde ke kolizi. Existují dvě možnosti, jak to skutečně zprovoznit:

  • vložte do tabulky jeden fiktivní řádek, možná s datem v minulosti
  • přepište tak, aby hlavní klauzule FROM odkazovala na jakoukoli tabulku, která má alespoň jeden řádek, nebo lépe vytvořte jednu malou tabulku (může se jmenovat dummy ) pouze s jedním sloupcem a pouze jedním záznamem v něm a přepište jej následovně (všimněte si, že již není potřeba klauzule GROUP BY)

    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
    FROM `dummy`
    WHERE NOT EXISTS (
        SELECT `id` FROM `slot`
        WHERE `start` <= @endTime AND `end` >= @startTime
        AND `devices_id` = @deviceId);
    

Zde následuje řada pokynů, které pokud jednoduše zkopírujete/vložíte, ukáže myšlenku v akci. Předpokládám, že v polích int kódujete datum/časy jako číslo se zřetězenými číslicemi data a času.

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
VALUES (1008141200, 1008141210, 11, 2, 'Dummy Record', 14)

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT 1408141206, 1408141210, 11, 2, 'Hello', 14
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= 1408141210 AND `end` >= 1408141206
    AND `devices_id` = 14)
GROUP BY (1);

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT 1408141208, 1408141214, 11, 2, 'Hello', 14
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= 1408141214 AND `end` >= 1408141208
    AND `devices_id` = 14)
GROUP BY (1);

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT 1408141216, 1408141220, 11, 2, 'Hello', 14
FROM `slot`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= 1408141220 AND `end` >= 1408141216
    AND `devices_id` = 14)
GROUP BY (1);

SELECT * FROM `slot`;

Toto je zjevně extrémní příklad optimistického zamykání, ale ve finále je velmi efektivní, protože vše probíhá pouze s jednou instrukcí SQL a s nízkou interakcí (výměnou dat) mezi databázovým serverem a php kódem. Dále neexistuje prakticky žádné "skutečné" zamykání.

...nebo s pesimistickým zamykáním

Stejný kód se může stát dobrou implementací pesimistického zamykání, která obklopuje explicitní instrukce pro zamykání/odemykání tabulky:

LOCK TABLE slot WRITE, dummy READ;

INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
FROM `dummy`
WHERE NOT EXISTS (
    SELECT `id` FROM `slot`
    WHERE `start` <= @endTime AND `end` >= @startTime
    AND `devices_id` = @deviceId);

UNLOCK TABLES;

Samozřejmě v tomto případě (pesimistické zamykání) lze SELECT a INSERT oddělit a mezitím spustit nějaký php kód. Tento kód však zůstává velmi rychle proveditelný (žádná výměna dat s php, žádný přechodný php kód), a tak je trvání Pesimistického zámku nejkratší možné. Udržet pesimistický zámek co nejkratší je klíčovým bodem, aby se zabránilo zpomalení aplikace.

Každopádně musíte zkontrolovat návratovou hodnotu počtu ovlivněných záznamů, abyste věděli, zda uspěla, protože kód je prakticky stejný, takže informace o úspěchu/neúspěchu získáte stejným způsobem.

Zde http://dev.mysql.com/doc/ refman/5.0/en/insert-select.html říkají, že "MySQL nepovoluje souběžné vkládání pro příkazy INSERT ... SELECT" takže pesimistický zámek by neměl být potřeba, ale každopádně to může být dobrá volba, pokud si myslíte, že se to v budoucích verzích MySQL změní.

Jsem "optimistický" že se to nezmění;-)




  1. ORACLE Jak používat cívku s dynamickým umístěním cívky

  2. postgresql - počet (žádné hodnoty null) každého sloupce v tabulce

  3. Nejlepší způsob, jak vytvořit vyhledávač SMART mySQL &PHP?

  4. MariaDB se po aktualizaci nemůže spustit:[Upozornění] Nelze vytvořit testovací soubor /home/mysql/beta.lower-test