PostgreSQL vidinių mechanizmų ypatumai leidžia kai kuriose situacijose būti labai greitam, o kitose – „nelabai greitai“. Šiandien mes sutelksime dėmesį į klasikinį konflikto tarp to, kaip veikia DBVS ir ką kūrėjas daro su ja, pavyzdį. UPDATE vs MVCC principai.
Trumpa istorija iš :
Kai eilutė pakeičiama komanda UPDATE, iš tikrųjų atliekamos dvi operacijos: DELETE ir INSERT. IN dabartinė eilutės versija xmax yra lygus operacijos, kuri atliko UPDATE, skaičiui. Tada jis sukuriamas nauja versija ta pati eilutė; jo xmin reikšmė sutampa su ankstesnės versijos xmax reikšme.
Praėjus tam tikram laikui po šios operacijos atlikimo, senoji arba nauja versija, priklausomai nuo COMMIT/ROOLBACK, bus pripažintas „miręs“ (negyvos eilutės) kai praeina VACUUM pagal lentelę ir išvalyta.

Bet tai neįvyks iš karto, tačiau problemų su „mirusiaisiais“ galima įgyti labai greitai - pakartotinai arba dideliame stale, o kiek vėliau susidursite su ta pačia situacija .
# 1: Man patinka tai perkelti
Tarkime, kad jūsų metodas veikia su verslo logika, ir staiga jis supranta, kad reikėtų atnaujinti X lauką kokiame nors įraše:
UPDATE tbl SET X = <newX> WHERE pk = $1;Tada, vykdymo eigoje, paaiškėja, kad Y laukas taip pat turėtų būti atnaujintas:
UPDATE tbl SET Y = <newY> WHERE pk = $1;... o tada dar ir Z – kam gaišti laiką smulkmenoms?
UPDATE tbl SET Z = <newZ> WHERE pk = $1;Kiek šio įrašo versijų dabar turime duomenų bazėje? Taip, 4 vienetai! Iš jų vienas yra aktualus, o 3 turės būti išvalyti po jūsų [auto]VACUUM.
Nedarykite to tokiu būdu! Naudokite visų laukų atnaujinimas vienoje užklausoje — beveik visada metodo logiką galima pakeisti taip:
UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;Nr. 2: naudojimas SKIRIASIS, Luke!
Taigi, jūs vis tiek norėjote (pvz., naudojant scenarijų arba konverterį). Ir į scenarijų patenka kažkas panašaus:
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;Maždaug tokios formos prašymas pateikiamas gana dažnai ir beveik visada ne užpildyti tuščią naują lauką, o ištaisyti kai kurias duomenų klaidas. Tuo pačiu ji pati visiškai neatsižvelgiama į esamų duomenų teisingumą - bet veltui! Tai reiškia, kad įrašas perrašomas, net jei jame yra būtent tai, ko norėjosi – bet kodėl? Pataisykime:
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>; Daugelis žmonių nežino, kad egzistuoja toks nuostabus operatorius, todėl čia yra sukčiavimo lapas IS DISTINCT FROM ir kiti loginiai operatoriai, kurie padės:

... ir šiek tiek apie komplekso operacijas ROW()-išraiškos:

#3: Aš atpažįstu savo mylimąjį iš... blokavimo
Yra paleisti du identiški lygiagrečiai vykstantys procesai, kurių kiekvienas bando pažymėti įrašą, kad jis yra „vykdomas“:
UPDATE tbl SET processing = TRUE WHERE pk = $1;Net jei šie procesai iš tikrųjų atlieka veiksmus nepriklausomai vienas nuo kito, bet naudojant tą patį ID, antrasis klientas bus „užrakintas“ pagal šią užklausą, kol bus atlikta pirmoji operacija.
1 sprendimas: užduotis sumažinama iki ankstesnės
Tiesiog pridėkime dar kartą IS DISTINCT FROM:
UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;Šioje formoje antroji užklausa tiesiog nieko nepakeis duomenų bazėje, viskas jau yra taip, kaip turi būti – todėl blokavimas nebus. Toliau apdorojame įrašo „neradimo“ taikytame algoritme faktą.
2 sprendimas: patariamosios spynos
Didelė tema atskiram straipsniui, apie kurį galite perskaityti .
3 sprendimas: kvaili skambučiai
Bet būtent taip ir turi nutikti tau vienu metu dirbti su tuo pačiu įrašu? O gal sujaukėte, pavyzdžiui, verslo logikos iškvietimo kliento pusėje algoritmus? O jei pagalvoji?..
Šaltinis: www.habr.com
