A PostgreSQL belső mechanizmusainak sajátosságai lehetővé teszik, hogy bizonyos helyzetekben nagyon gyors, más esetekben „nem túl gyors”. Ma a DBMS működése és a fejlesztői tevékenység közötti konfliktus klasszikus példájára fogunk összpontosítani. UPDATE vs MVCC elvek.
Rövid történet innen
Ha egy sort módosít egy UPDATE paranccsal, akkor valójában két műveletet hajtanak végre: DELETE és INSERT. BAN BEN a karakterlánc aktuális verziója Az xmax értéke megegyezik a FRISSÍTÉST végrehajtó tranzakció számával. Aztán létrejön egy új verzió ugyanaz a sor; az xmin értéke egybeesik az előző verzió xmax értékével.
A tranzakció befejezése után némi idővel a régi vagy az új verzió, attól függően COMMIT/ROOLBACK
, felismerik "halott" (halott sorok) elhaladásakor VACUUM
táblázat szerint és letisztult.
De ez nem fog azonnal megtörténni, de a „halottakkal” kapcsolatos problémák nagyon gyorsan megszerezhetők - ismételt ill
#1: Szeretem mozgatni
Tegyük fel, hogy a módszered az üzleti logikán dolgozik, és hirtelen rájön, hogy frissíteni kellene az X mezőt valamelyik rekordban:
UPDATE tbl SET X = <newX> WHERE pk = $1;
Aztán a végrehajtás előrehaladtával kiderül, hogy az Y mezőt is frissíteni kell:
UPDATE tbl SET Y = <newY> WHERE pk = $1;
... és akkor Z is - miért vesztegeti az időt apróságokra?
UPDATE tbl SET Z = <newZ> WHERE pk = $1;
Ennek a rekordnak hány verziója van most az adatbázisban? Igen, 4 darab! Ezek közül egy releváns, és 3-at az [auto]VÁKUUM segítségével kell feltakarítania.
Ne csináld így! Használat az összes mező frissítése egy kérelemben — a metódus logikája szinte mindig így változtatható:
UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;
#2: A használat KÜLÖNBÖZIK, Luke!
Szóval még mindig akartad
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;
Meglehetősen gyakran és szinte mindig fordul elő ilyen jellegű kérés, hogy ne egy üres új mezőt töltsön ki, hanem néhány adathibát javítson ki. Ugyanakkor ő maga a meglévő adatok helyességét egyáltalán nem veszik figyelembe - de hiába! Vagyis újraírják a lemezt, még akkor is, ha pontosan azt tartalmazza, amit akartak – de miért? Javítsuk ki:
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>;
Sokan nincsenek tisztában egy ilyen csodálatos operátor létezésével, ezért itt van egy csalólap IS DISTINCT FROM
és egyéb logikai operátorok, amelyek segítenek:
... és egy kicsit a komplexen végzett műveletekről ROW()
-kifejezések:
#3: Felismerem a kedvesemet arról, hogy... blokkolok
elindítják két azonos párhuzamos folyamat, amelyek mindegyike megpróbálja megjelölni a bejegyzést, hogy „folyamatban van”:
UPDATE tbl SET processing = TRUE WHERE pk = $1;
Még akkor is, ha ezek a folyamatok ténylegesen egymástól függetlenül, de ugyanazon az azonosítón belül végeznek dolgokat, a második kliens „le lesz zárva” erre a kérésre az első tranzakció befejezéséig.
1. megoldás: a feladat az előzőre redukálódik
Tegyük hozzá még egyszer IS DISTINCT FROM
:
UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;
Ebben a formában a második kérés egyszerűen nem változtat semmit az adatbázisban, minden már úgy van, ahogy lennie kell - ezért a blokkolás nem történik meg. Ezután feldolgozzuk a rekord „nem találásának” tényét az alkalmazott algoritmusban.
2. megoldás: tanácsadó zárak
Nagy téma egy külön cikkhez, amiben olvashatsz róla
3. megoldás: hülye hívások
De pontosan ennek kell történnie veled egyidejű munka ugyanazzal a rekorddal? Vagy elrontottad például az üzleti logika ügyféloldali hívásának algoritmusait? És ha belegondolsz?...
Forrás: will.com