PostgreSQL iekšējo mehānismu īpatnības dažās situācijās ļauj tam būt ļoti ātram, bet citās – “ne ļoti ātram”. Šodien mēs koncentrēsimies uz klasisku piemēru konfliktam starp to, kā darbojas DBVS un ko izstrādātājs ar to dara - UPDATE vs MVCC principi.
Īss stāsts no
Kad rinda tiek modificēta ar komandu UPDATE, faktiski tiek veiktas divas darbības: DELETE un INSERT. IN pašreizējā virknes versija xmax ir iestatīts vienāds ar transakcijas numuru, kas veica ATJAUNINĀJUMU. Tad tas tiek izveidots jauna versija tā pati līnija; tā xmin vērtība sakrīt ar iepriekšējās versijas xmax vērtību.
Kādu laiku pēc šī darījuma pabeigšanas vecā vai jaunā versija, atkarībā no COMMIT/ROOLBACK
, tiks atpazīts "miris" (mirušie kopas) kad iet garām VACUUM
saskaņā ar tabulu un notīrīta.
Bet tas nenotiks uzreiz, bet problēmas ar “mirušajiem” var iegūt ļoti ātri - ar atkārtotu vai
#1: Man patīk to pārvietot
Pieņemsim, ka jūsu metode strādā pie biznesa loģikas, un pēkšņi tā saprot, ka būtu nepieciešams atjaunināt X lauku kādā ierakstā:
UPDATE tbl SET X = <newX> WHERE pk = $1;
Pēc tam, izpildes gaitā izrādās, ka ir jāatjaunina arī Y lauks:
UPDATE tbl SET Y = <newY> WHERE pk = $1;
... un tad arī Z - kāpēc tērēt laiku niekiem?
UPDATE tbl SET Z = <newZ> WHERE pk = $1;
Cik šī ieraksta versiju tagad ir datu bāzē? Jā, 4 gabali! No tiem viens ir būtisks, un 3 būs jāiztīra pēc jums ar [auto]VACUUM.
Nedariet to šādā veidā! Izmantot visu lauku atjaunināšana vienā pieprasījumā — gandrīz vienmēr metodes loģiku var mainīt šādi:
UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;
#2: lietojums ATŠĶIRAS NO, Lūks!
Tātad, jūs joprojām gribējāt
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;
Pieprasījums aptuveni šādā formā rodas diezgan bieži un gandrīz vienmēr nevis aizpildīt tukšu jaunu lauku, bet gan izlabot dažas kļūdas datos. Tajā pašā laikā viņa pati esošo datu pareizība vispār netiek ņemta vērā - bet velti! Tas ir, ieraksts tiek pārrakstīts, pat ja tajā ir tieši tas, ko gribēja – bet kāpēc? Izlabosim:
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>;
Daudzi cilvēki nezina par tik brīnišķīga operatora esamību, tāpēc šeit ir krāpšanās lapa IS DISTINCT FROM
un citi loģiskie operatori, lai palīdzētu:
... un nedaudz par operācijām ar kompleksu ROW()
-izteicieni:
#3: Es atpazīstu savu mīļoto pēc... bloķēšanas
tiek palaistas divi identiski paralēli procesi, no kuriem katrs mēģina atzīmēt ierakstu, ka tas ir “notiek”.
UPDATE tbl SET processing = TRUE WHERE pk = $1;
Pat ja šie procesi faktiski veic darbības neatkarīgi viens no otra, bet viena ID ietvaros, otrais klients tiks “bloķēts” pēc šī pieprasījuma, līdz tiks pabeigts pirmais darījums.
Risinājums #1: uzdevums tiek samazināts līdz iepriekšējam
Vienkārši pievienosim to vēlreiz IS DISTINCT FROM
:
UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;
Šajā formā otrais pieprasījums vienkārši neko nemainīs datu bāzē, viss jau ir tā, kā vajadzētu - tāpēc bloķēšana nenotiks. Tālāk mēs apstrādājam ieraksta “neatrašanas” faktu lietotajā algoritmā.
Risinājums #2Kabīne: konsultatīvās slēdzenes
Liela tēma atsevišķam rakstam, kurā varat lasīt par
Risinājums #3: stulbi zvani
Bet tieši tam vajadzētu notikt ar jums vienlaicīgs darbs ar vienu un to pašu ierakstu? Vai arī jūs, piemēram, sajaucāt biznesa loģikas izsaukšanas algoritmus klienta pusē? Un ja padomā?...
Avots: www.habr.com