sql >> Database teknologi >  >> RDS >> Mysql

MySQL-transaktion:SELECT + INSERT

Det, du har brug for, er låsning . Transaktioner er faktisk "ikke strengt nødvendige".

Du kan vælge mellem "pessimistisk låsning" og "optimistisk låsning". Beslutningen om, hvilken af ​​disse to muligheder er op til dig og skal vurderes grundlæggende under hensyntagen til:

  • niveauet af samtidighed, du har
  • varigheden af ​​de skal-at-være-atomiske operationer på databasen
  • kompleksiteten af ​​hele operationen

Jeg vil anbefale at læse disse to for at opbygge en idé om de involverede ting:

Et eksempel til at forklare bedre

Dette er måske ikke så elegant, men er kun et eksempel, der viser, hvordan det er muligt at gøre alt uden transaktion (og endda uden de UNIKKE begrænsninger). for at kontrollere antallet af berørte rækker. Hvis antallet af berørte rækker er 1, så er det lykkedes ellers (hvis det er 0), har der været en kollision, og den anden part har vundet.

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);

Dette er et eksempel på Optimistisk låsning opnået uden transaktioner og med en enkelt SQL-operation.

Som det er skrevet har det det problem, at der skal være mindst én række allerede i slot tabel for at den kan fungere (ellers vil SELECT-sætningen altid returnere et tomt postsæt, og i så fald indsættes intet evei, hvis der ikke er nogen kollisioner. Der er to muligheder for at få det til at virke:

  • indsæt én dummy-række i tabellen, måske med datoen i fortiden
  • omskriv, så hoved-FROM-sætningen refererer til enhver tabel, der har mindst én række eller bedre, opret en lille tabel (måske kaldet dummy ) med kun én kolonne og kun én post i den og omskriv som følgende (bemærk, at der ikke længere er behov for GROUP BY-sætningen)

    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);
    

Her følger en række instruktioner, som hvis du blot kopierer/indsætter, viser ideen i aktion. Jeg har antaget, at du indkoder dato/klokkeslæt på int-felter som et tal med cifrene for dato og tid sammenkædet.

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`;

Dette er helt klart et ekstremt eksempel på Optimistic Locking, men det er meget effektivt i sidste ende, fordi alt foregår med kun én SQL-instruktion og med lav interaktion (dataudveksling) mellem databaseserveren og php-koden. Yderligere er der praktisk talt ingen "rigtig" låsning.

...eller med pessimistisk låsning

Den samme kode kan blive en god pessimistisk låseimplementering lige omkring med eksplicitte instruktioner om bordlås/oplåsning:

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;

Selvfølgelig i dette tilfælde (pessimistisk låsning) kunne SELECT og INSERT adskilles og noget php-kode udføres derimellem. Denne kode forbliver dog meget hurtig at udføre (ingen dataudveksling med php, ingen mellemliggende php-kode), og derfor er varigheden af ​​den pessimistiske lås den kortest mulige. At holde Pessimistic Lock så kort som muligt er et nøglepunkt for at undgå at bremse applikationen.

I hvert fald skal du kontrollere antallet af berørte posters returneringsværdi for at vide, om det lykkedes, da koden praktisk talt er den samme, og du får succes-/fejlinformationen på samme måde.

Her http://dev.mysql.com/doc/ refman/5.0/da/insert-select.html de siger, at "MySQL tillader ikke samtidige indsættelser for INSERT ... SELECT-sætninger" så det burde ikke være nødvendigt med Pessimistic Lock, men alligevel kan dette være en god mulighed, hvis du tror, ​​at dette vil ændre sig i fremtidige versioner af MySQL.

Jeg er "optimistisk" at dette ikke ændrer sig;-)




  1. Hvad er rækkefølgen af ​​poster i en tabel med en sammensat primærnøgle

  2. Fjern primærnøgle i MySQL

  3. MySQL:beholde serverens tidszone eller brugerens tidszone?

  4. SQL BESTIL EFTER