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

Problemet med tabt opdatering i samtidige transaktioner

Det tabte opdateringsproblem opstår, når 2 samtidige transaktioner forsøger at læse og opdatere de samme data. Lad os forstå dette ved hjælp af et eksempel.

Antag, at vi har en tabel med navnet "Produkt", der gemmer id, navn og varer på lager for et produkt.

Det bruges som en del af et online system, der viser antallet af varer på lager for et bestemt produkt og skal derfor opdateres, hver gang et salg af det pågældende produkt foretages.

Tabellen ser således ud:

Id

Navn

Vare på lager

1

Bærbare computere

12

Overvej nu et scenario, hvor en bruger ankommer og starter processen med at købe en bærbar computer. Dette vil igangsætte en transaktion. Lad os kalde denne transaktion, transaktion 1.

Samtidig logger en anden bruger på systemet og starter en transaktion, lad os kalde denne transaktion 2. Tag et kig på følgende figur.

Transaktion 1 aflæser varerne på lager for bærbare computere, som er 12. Lidt senere aflæser transaktion 2 værdien for Varer i Lager for bærbare computere, som stadig vil være 12 på dette tidspunkt. Transaktion 2 sælger derefter tre bærbare computere, kort før transaktion 1 sælger 2 varer.

Transaktion 2 vil derefter afslutte sin eksekvering først og opdatere ItemsinStock til 9, da den solgte tre af de 12 bærbare computere. Transaktion 1 forpligter sig selv. Da transaktion 1 solgte to varer, opdaterer den ItemsinStock til 10.

Dette er forkert, det korrekte tal er 12-3-2 =7

Fungerende eksempel på mistet opdateringsproblem

Lad os tage et kig på problemet med tabt opdatering i aktion i SQL Server. Som altid vil vi først oprette en tabel og tilføje nogle dummy-data til den.

Som altid skal du sikre dig, at du er ordentligt sikkerhedskopieret, før du spiller med ny kode. Hvis du ikke er sikker, kan du se denne artikel om SQL Server-sikkerhedskopi.

Udfør følgende script på din databaseserver.

<span style="font-size: 14px;">CREATE DATABASE pos;

USE pos;

CREATE TABLE products
(
	Id INT PRIMARY KEY,
	Name VARCHAR(50) NOT NULL,
	ItemsinStock INT NOT NULL

)

INSERT into products

VALUES 
(1, 'Laptop', 12),
(2, 'Iphon', 15),
(3, 'Tablets', 10)</span>

Åbn nu to SQL Server Management Studio-instanser side om side. Vi kører én transaktion i hvert af disse tilfælde.

Tilføj følgende script til den første forekomst af SSMS.

<span style="font-size: 14px;">USE pos;

-- Transaction 1

BEGIN TRAN

DECLARE @ItemsInStock INT

SELECT @ItemsInStock = ItemsInStock
FROM products WHERE Id = 1

WaitFor Delay '00:00:12'
SET @ItemsInStock = @ItemsInStock - 2

UPDATE products SET ItemsinStock = @ItemsInStock
WHERE Id = 1

Print @ItemsInStock
Commit Transaction</span>

Dette er scriptet til transaktion 1. Her begynder vi transaktionen og erklærer en heltalstypevariabel "@ItemsInStock". Værdien af ​​denne variabel sættes til værdien af ​​kolonnen ItemsinStock for posten med Id 1 fra produkttabellen. Derefter tilføjes en forsinkelse på 12 sekunder, så transaktion 2 kan fuldføre sin eksekvering før transaktion 1. Efter forsinkelsen formindskes værdien af ​​@ItemsInStock variabel med 2, der angiver salget af 2 produkter.

Til sidst opdateres værdien for ItemsinStock-kolonnen for posten med Id 1 med værdien af ​​@ItemsInStock-variablen. Vi udskriver derefter værdien af ​​@ItemsInStock variabel på skærmen og forpligter transaktionen.

I den anden forekomst af SSMS tilføjer vi scriptet til transaktion 2, som er som følger:

<span style="font-size: 14px;">USE pos;

-- Transaction 2

BEGIN TRAN

DECLARE @ItemsInStock INT

SELECT @ItemsInStock = ItemsInStock
FROM products WHERE Id = 1

WaitFor Delay '00:00:3'
SET @ItemsInStock = @ItemsInStock - 3

UPDATE products SET ItemsinStock = @ItemsInStock
WHERE Id = 1

Print @ItemsInStock
Commit Transaction</span>

Scriptet til transaktion 2 ligner transaktion 1. Men her i transaktion 2 er forsinkelsen kun i tre sekunder, og faldet i værdien for @ItemsInStock variabel er tre, da det er et salg af tre varer.

Kør nu transaktion 1 og derefter transaktion 2. Du vil først se transaktion 2 fuldføre sin udførelse. Og værdien, der udskrives for @ItemsInStock-variabelen, vil være 9. Efter nogen tid vil transaktion 1 også fuldføre sin udførelse, og værdien, der udskrives for dens @ItemsInStock-variabel, vil være 10.

Begge disse værdier er forkerte. Den faktiske værdi for kolonnen ItemsInStock for produktet med Id 1 skal være 7.

BEMÆRK:

Det er vigtigt at bemærke her, at problemet med tabt opdatering kun opstår med læst forpligtet og læst ikke-forpligtet transaktionsisolationsniveau. Med alle de andre transaktionsisoleringsniveauer opstår dette problem ikke.

Læs isolationsniveau for gentagen transaktion

Lad os opdatere isolationsniveauet for begge transaktioner for at læse gentagelige og se, om det tabte opdateringsproblem opstår. Men før det skal du udføre følgende sætning for at opdatere værdien for ItemsInStock tilbage til 12.

Update products SET ItemsinStock = 12

Script til transaktion 1

<span style="font-size: 14px;">USE pos;

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
-- Transaction 1

BEGIN TRAN
DECLARE @ItemsInStock INT

SELECT @ItemsInStock = ItemsInStock
FROM products WHERE Id = 1

WaitFor Delay '00:00:12'
SET @ItemsInStock = @ItemsInStock - 2

UPDATE products SET ItemsinStock = @ItemsInStock
WHERE Id = 1

Print @ItemsInStock
Commit Transaction</span>

Script til transaktion 2

<span style="font-size: 14px;">USE pos;

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
-- Transaction 2

BEGIN TRAN
DECLARE @ItemsInStock INT

SELECT @ItemsInStock = ItemsInStock
FROM products WHERE Id = 1

WaitFor Delay '00:00:3'
SET @ItemsInStock = @ItemsInStock - 3

UPDATE products SET ItemsinStock = @ItemsInStock
WHERE Id = 1

Print @ItemsInStock
Commit Transaction</span>

Her i begge transaktioner har vi sat isolationsniveauet til gentagelig aflæsning.

Kør nu transaktion 1 og kør derefter straks transaktion 2. I modsætning til det tidligere tilfælde skal transaktion 2 vente på, at transaktion 1 forpligter sig. Derefter opstår følgende fejl for transaktion 2:

Besked 1205, niveau 13, tilstand 51, linje 15

Transaktionen (Proces ID 55) var låst på låsessourcer med en anden proces og er blevet valgt som offer for dødvande. Kør transaktionen igen.

Denne fejl opstår, fordi gentagelig læsning låser ressourcen, som læses eller opdateres af transaktion 1, og den skaber et dødvande for den anden transaktion, der forsøger at få adgang til den samme ressource.

Fejlen siger, at transaktion 2 har en deadlock på en ressource med en anden proces, og at denne transaktion er blevet blokeret af deadlock. Det betyder, at den anden transaktion fik adgang til ressourcen, mens denne transaktion blev blokeret og ikke fik adgang til ressourcen.

Der står også, at transaktionen skal køres igen, da ressourcen er gratis nu. Nu, hvis du kører transaktion 2 igen, vil du se den korrekte værdi af varer på lager, dvs. 7. Dette skyldes, at transaktion 1 allerede havde sænket varelagerværdien med 2, transaktion 2 sænker dette yderligere med 3, derfor 12 – (2+) 3) =7.


  1. Hvad er den mest anbefalede måde at gemme tid i PostgreSQL ved hjælp af Java?

  2. Kunne ikke indlæse sql-moduler i databaseklyngen under PostgreSQL-installationen

  3. SINH() Funktion i Oracle

  4. Android - Vis brugernavn fra sqlite-database efter login i textView