PostgreSQL antipatterns: cīņa ar "mirušo" bariem

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 lielisks raksts:

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.

PostgreSQL antipatterns: cīņa ar "mirušo" bariem

Bet tas nenotiks uzreiz, bet problēmas ar “mirušajiem” var iegūt ļoti ātri - ar atkārtotu vai masveida ierakstu atjaunināšana lielā tabulā, un nedaudz vēlāk jūs saskarsities ar tādu pašu situāciju VACUUM nevarēs palīdzēt.

#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 atjaunināt daudzus, daudzus ierakstus tabulā (piemēram, skripta vai pārveidotāja izmantošanas laikā). Un kaut kas līdzīgs šim ieplūst skriptā:

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:
PostgreSQL antipatterns: cīņa ar "mirušo" bariem
... un nedaudz par operācijām ar kompleksu ROW()-izteicieni:
PostgreSQL antipatterns: cīņa ar "mirušo" bariem

#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 pielietošanas metodes un rekomendējošās bloķēšanas “grābeklis”..

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

Pievieno komentāru