PostgreSQL-i antimustrid: võitlus "surnute hordidega"

PostgreSQL-i sisemiste mehhanismide iseärasused võimaldavad mõnes olukorras olla väga kiire ja mõnes olukorras “mitte väga kiire”. Täna keskendume klassikalisele näitele konfliktist selle vahel, kuidas DBMS töötab ja mida arendaja sellega teeb - UPDATE vs MVCC põhimõtted.

Lühilugu alates suurepärane artikkel:

Kui rida muudetakse käsuga UPDATE, tehakse tegelikult kaks toimingut: DELETE ja INSERT. IN stringi praegune versioon xmax on võrdne UPDATE sooritanud tehingu arvuga. Siis see luuakse uus versioon sama rida; selle xmin väärtus langeb kokku eelmise versiooni xmax väärtusega.

Mõni aeg pärast selle tehingu lõpuleviimist, olenevalt sellest, kas vana või uus versioon COMMIT/ROOLBACK, tunnustatakse "surnud" (surnud korteežid) möödumisel VACUUM tabeli järgi ja puhastatud.

PostgreSQL-i antimustrid: võitlus "surnute hordidega"

Kuid seda ei juhtu kohe, vaid probleeme "surnutega" saab omandada väga kiiresti - korduvate või rekordite massiline uuendamine suures tabelis ja veidi hiljem kohtate sama olukorda VACUUM ei saa aidata.

# 1: mulle meeldib seda liigutada

Oletame, et teie meetod töötab äriloogika kallal ja äkki mõistab see, et mõnes kirjes oleks vaja X välja värskendada:

UPDATE tbl SET X = <newX> WHERE pk = $1;

Seejärel selgub täitmise edenedes, et värskendada tuleks ka välja Y:

UPDATE tbl SET Y = <newY> WHERE pk = $1;

... ja siis ka Z - milleks raisata aega pisiasjadele?

UPDATE tbl SET Z = <newZ> WHERE pk = $1;

Mitu selle kirje versiooni meil nüüd andmebaasis on? Jah, 4 tükki! Neist üks on asjakohane ja 3 tuleb pärast [auto]VACUUM teie järel ära koristada.

Ärge tehke seda nii! Kasutage kõigi väljade värskendamine ühe taotlusega — peaaegu alati saab meetodi loogikat muuta järgmiselt:

UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;

#2: kasutamine ERINEB, Luke!

Niisiis, sa ikka tahtsid värskendada tabelis palju-palju kirjeid (näiteks skripti või konverteri kasutamise ajal). Ja midagi sellist lendab skripti sisse:

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;

Ligikaudu sellisel kujul taotlus esitatakse üsna sageli ja peaaegu alati mitte tühja uue välja täitmiseks, vaid mõne andmete vea parandamiseks. Samal ajal ta ise olemasolevate andmete õigsust ei võeta üldse arvesse - aga asjata! See tähendab, et plaat kirjutatakse ümber, isegi kui see sisaldas täpselt seda, mida taheti – aga miks? Teeme selle korda:

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>;

Paljud inimesed pole sellise suurepärase operaatori olemasolust teadlikud, seega on siin petuleht IS DISTINCT FROM ja muud loogilised operaatorid, mis aitavad:
PostgreSQL-i antimustrid: võitlus "surnute hordidega"
... ja natuke operatsioonidest kompleksiga ROW()-väljendid:
PostgreSQL-i antimustrid: võitlus "surnute hordidega"

#3: Ma tunnen oma kallima ära... blokeerimise järgi

käivitatakse kaks identset paralleelset protsessi, millest igaüks püüab märkida kirjele, et see on pooleli:

UPDATE tbl SET processing = TRUE WHERE pk = $1;

Isegi kui need protsessid teevad asju tegelikult üksteisest sõltumatult, kuid sama ID piires, lukustatakse teine ​​klient selle päringu alusel kuni esimese tehingu sooritamiseni.

Lahendus # 1: ülesanne taandatakse eelmisele

Lisame selle uuesti IS DISTINCT FROM:

UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;

Sellel kujul ei muuda teine ​​​​päring lihtsalt andmebaasis midagi, kõik on juba nii, nagu peaks - seetõttu blokeerimist ei toimu. Järgmisena töötleme rakendatud algoritmis kirje "ei leia" fakti.

Lahendus # 2: nõuandelukud

Suur teema eraldi artikli jaoks, millest saate lugeda rakendusmeetodid ja soovitusliku blokeerimise “reha”..

Lahendus # 3: lollid kõned

Kuid just see peaks teiega juhtuma samaaegne töö sama plaadiga? Või ajasite sassi näiteks kliendipoolse äriloogika kutsumise algoritmidega? Ja kui järele mõelda?...

Allikas: www.habr.com

Lisa kommentaar