Transaktioner i databas

By . Latest revision .

En transaktion i en databas omsluter flera händelser och gör dem atomära – antingen utförs samtliga händelser, eller ingen.

För att ta ett exempel där man vill flytta pengar från en ägare till en annan och informationen om detta finns i en databas. Först plockar man bort pengen från den ena och sedan lägger man till pengen till den andre. Flytten som består av två SQL-satser måste utföras i sin helhet, eller inte alls. Här kommer en transaktion till hjälp. Transaktionen kan garantera att båda SQL-satserna utförs i sin helhet, eller ingen alls.

#Förutsättning

Exemplet visar hur du jobbar med transaktioner i MySQL. Ett avslutande stycke visar hur samma kod ser ut i SQLite.

Du kan läsa om transaktioner i manualen för MySQL.

Du kan även läsa om transaktioner i manualen för SQLite.

SQL-koden som visas i exemplet finner du på GitHub eller i ditt kursrepo databas under example/sql/transaction.sql.

#ACID

ACID är en samling properties för databastransaktioner. De beskriver hur en transaktion måste bete sig.

Property Beskrivning
A - Atomicity En transaktion utförs som en enhet, eller inte alls, allt eller inget. Den är odelbar.
C - Consistency Transaktionen flyttar databasen från ett giltigt läge till ett annat och alla regler såsom constraints, cascades och triggers skall vara giltiga.
I - Isolation Även om flera transaktioner exekveras samtidigt så skall resultatet från transaktionerna betraktas som om de exekverades sekventiellt. En transaktion kan inte se effekter av en delvis exekverad transaktion.
D - Durability När en transaktion är committad så skall den vara beständig, även om strömmen går och databasmotorn krashar.

En transaktion skall alltså vara atomär (A), bevara konsistensen (C) i databasen, vara isolerad (I) från övriga transaktioner och vara beständig (D) när den är committad. Därav ACID.

En transaktion innebär att en eller flera SQL-satser utförs i sin helhet, eller inte alls.

#En testdatabas

Artikeln förutsätter att du har en testdatabas dbwebb som du kan använda och att du kan logga in med användaren user med lösenordet pass.

Rent krasst kan du dock köra koden nedan mot godtycklig databas.

#Transaktion, ett exempel

Låt oss ta en bitcoin bank där Adam och Eva skall flytta en mängd bitcoins mellan varandra.

#En tabell

Först skapar vi en tabell med innehåll.

--
-- Example transactions
-- 
DROP TABLE IF EXISTS account;
CREATE TABLE account
(
    `id` CHAR(4) PRIMARY KEY,
    `name` VARCHAR(8),
    `balance` DECIMAL(4, 2)
);

-- DELETE FROM account;
INSERT INTO account
VALUES
    ("1111", "Adam", 10.0),
    ("2222", "Eva", 7.0)
;

SELECT * FROM account;

Bra, då har vi en tabell att ugå ifrån. Adam har 10 bitcoins och Eva har 7.

Det kan se ut så här.

+------+------+---------+
| id   | name | balance |
+------+------+---------+
| 1111 | Adam |   10.00 |
| 2222 | Eva  |    7.00 |
+------+------+---------+
2 rows in set (0.00 sec)

#Flytta pengar

Låt oss säga att Adam och Eva har slagit vad i Melodifestivalen och Adam förlorade och skall skicka 1.5 bitcoins till Eva.

SQL-koden för den flytten ser ut så här.

--
-- Move the money
--
UPDATE account 
SET
    balance = balance + 1.5
WHERE
    id = "2222";

UPDATE account 
SET
    balance = balance - 1.5
WHERE
    id = "1111";

SELECT * FROM account;

I den första satsen lägger vi till pengen till Evas konto, i den andra satsen så tar vi bort pengen från Admas konto.

I mellanläget innehåller databasen mer pengar än det egentligen finns. Det är när pengarna fyllts på Evas konto men ännu inte dragits från Adams konto. I detta läget är databasen inte konsistent.

Det hjälper inte om vi byter ordning på satserna och skapar ett underskott istället. Databasen är lika mycket ur balans och ej konsistent i båda fallen.

Till vår hjälp kommer transaktionen.

#Flytta med transaktion

Vi lägger flytten av pengarna inom ramen för en transaktion, så att flytten av pengar blir atomär och databasen behåller sin konsistens oavsett vad som händer.

En transaktion inleds med START TRANSACTION och avslutas med COMMIT. Om man av någon anledning vill avbryta en transaktion så gör man ROLLBACK istället för COMMIT.

--
-- Move the money, within a transaction
--
START TRANSACTION;

UPDATE account 
SET
    balance = balance + 1.5
WHERE
    id = "2222";

UPDATE account 
SET
    balance = balance - 1.5
WHERE
    id = "1111";

COMMIT;

SELECT * FROM account;

Det är alltså START TRANSACTION som påbörjar transaktionen och det är COMMIT som avslutar den. Alla skeenden som utförs inom transaktionen är nu atomära.

Om du gör en ROLLBACK istället för en COMMIT så görs en undo på hela sekvensen som utfördes inom transaktionen. Det blir alltså att COMMIT utför alla ändringar och ROLLBACK tar bort effekten av allt som utförts i transaktionen.

En transaktion avslutas med en COMMIT, eller avbryts med en ROLLBACK.

#Transaktioner i SQLite

Transaktioner i SQLite fungerar på samma sätt. Enda skillnaden är SQL-koden där transaktionens centrala delar ser ut så här.

BEGIN TRANSACTION;
--
COMMIT;
ROLLBACK;

Du kan se syntaxen för transaktioner i SQLite manualen.

#Avslutningsvis

Detta var grunderna i databastransaktioner. Så fort du gör flera satser där samtliga satser behöver utföras för att bevara konsistensen i databasen så kan transaktioner vara verktyget.

Har du tips, förslag eller frågor om artikeln så finns det en specifik forumtråd för det.

#Revision history

  • 2019-02-08: (D, mos) Genomgången och fokus mot kursen databas.
  • 2018-01-05: (C, mos) Genomgången och nu även i kursen databas.
  • 2017-04-25: (B, mos) Nu även i kursen oophp.
  • 2017-03-06: (A, mos) Första utgåvan inför kursen dbjs.

Document source.

Category: databas, sql, kurs dbjs, kurs oophp, kurs databas.