Veçoritë e funksionimit të mekanizmave të brendshëm të PostgreSQL e lejojnë atë të jetë shumë i shpejtë në disa situata dhe "nuk shumë" në të tjera. Sot do të ndalemi në një shembull klasik të konfliktit midis asaj se si funksionon SGBD dhe asaj që bën zhvilluesi me të — UPDATE vs parimet MVCC.
Përmbledhje e ngjarjes nga :
Kur një rresht ndryshohet nga komanda UPDATE, në fakt kryhen dy operacione: DELETE dhe INSERT. Në versionin aktual të rreshtit vendoset xmax, e barabartë me numrin e transaksionit që ka kryer UPDATE. Pastaj krijohet versioni i ri i njëjtit rresht; vlera xmin e tij përputhet me vlerën xmax të versionit të mëparshëm.
Pas një kohe të caktuar pas përfundimit të kësaj transaksioni, versioni i vjetër ose i ri, në varësi të COMMIT/ROLLBACK, do të njihen si "të vdekur" (dead tuples) kur kaloni VACUUM në tabelë dhe do të pastrohen.

Por kjo nuk do të ndodhë menjëherë, por problemet me "të vdekurit" mund të lindin shumë shpejt — gjatë një përditësimi të shumtë ose në një tabelë të madhe, dhe pak më vonë përballeni me situatën që edhe .
#1: I Like To Move It
Supozoni se metoda juaj në logjikën e biznesit funksionon siç duhet, dhe papritmas kupton se duhet të përditësojë fushën X në një regjistrim të caktuar:
UPDATE tbl SET X = WHERE pk = $1;Pastaj, gjatë ekzekutimit, zbulohet se fusha Y gjithashtu duhet të përditësohet:
UPDATE tbl SET Y = WHERE pk = $1;… dhe pastaj edhe Z — përse të bëjmë gjithçka në little?
UPDATE tbl SET Z = WHERE pk = $1;Sa versione të këtij regjistrimi kemi tani në bazë? Aha, 4 gjëra! Nga ato, një është aktuale, dhe 3 duhet të pastronin pas jush [auto]VACUUM.
Mos e bëni kështu! Përdorni përditësimin e të gjitha fushave me një kërkesë — pothuajse gjithmonë logjika e punës së metodës mund të ndryshohet në këtë mënyrë:
UPDATE tbl SET X = , Y = , Z = WHERE pk = $1;#2: Use IS DISTINCT FROM, Luke!
Pra, megjithatë keni dashur (në procesin e aplikimit të skriptit ose konvertuesit, për shembull). Dhe në skript fluturon diçka e tillë:
UPDATE tbl SET X = WHERE pk BETWEEN $1 AND $2;Dikushherë një kërkesë e tillë ndodhet mjaft shpesh dhe pothuajse gjithmonë jo për të mbushur një fushë të re të zbrazët, por për të korigjuar ndonjë gabim në të dhëna. Ndërkohë vetë saktësia e të dhënave që tashmë ekzistojnë nuk merret parasysh — dhe gabim! Pra, regjistrimi ripërpunohet, edhe nëse aty kishte saktësisht atë që duhej — dhe përse? Le të rregullojmë:
UPDATE tbl SET X = WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM ; Shumë njerëz nuk e dinë për ekzistencën e një operatori të tillë të shkëlqyer, prandaj ja një përmbledhje për IS DISTINCT FROM dhe operatorët e tjerë logjikë në ndihmë:

… dhe pak për operacionet mbi shprehjet komplekse: ROW()-shprehjet:

#3: А я милого узнаю по… блокировке
Aktivizohen dy procese paralele të njëjta, secili prej të cilëve përpiqet të regjistrojë që regjistrimi është "në punë":
UPDATE tbl SET processing = TRUE WHERE pk = $1;Edhe nëse këto procese bëjnë gjëra të pavarura nga njëra-tjetra, por brenda një ID, në këtë kërkesë klienti i dytë "do të bllokohet" derisa të përfundojë transaksioni i parë.
Zgjidhja №1: problemi i reduktuar në të kaluarën
Thjesht do të shtojmë përsëri IS DISTINCT FROM:
UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;Në këtë formë, kërkesa e dytë thjesht nuk do të ndryshojë asgjë në bazë, pasi aty "është gjithçka siç duhet" — prandaj bllokimi nuk do të ndodhë. Më pas, fakti i "mosgjetjes" së regjistrimit tashmë do të trajtohet në algoritmin aplikativ.
Zgjidhja №2: bllokimet e këshillimit
Një temë e madhe për një artikull të veçantë, ku mund të lexoni për .
Zgjidhja №3: thirrje të pa[detyrueshme]
Në fakt, me siguri duhet të ndodhë punë e njëkohshme me të njëjtin regjistrim? Или вы все-таки накосячили с алгоритмами вызовов бизнес-логики со стороны клиента, например? А если подумать?..
Burimi: habr.com
