Keistimewaan mekanisme dalaman PostgreSQL membolehkannya menjadi sangat pantas dalam sesetengah situasi dan "tidak terlalu pantas" dalam situasi lain. Hari ini kita akan menumpukan pada contoh klasik konflik antara cara DBMS berfungsi dan perkara yang dilakukan oleh pembangun dengannya - KEMASKINI vs prinsip MVCC.
Cerita ringkas daripada
Apabila baris diubah suai oleh perintah KEMASKINI, dua operasi sebenarnya dilakukan: PADAM dan INSERT. DALAM versi semasa rentetan xmax ditetapkan sama dengan bilangan transaksi yang melakukan KEMASKINI. Kemudian ia dicipta versi baru baris yang sama; nilai xminnya bertepatan dengan nilai xmax versi sebelumnya.
Beberapa ketika selepas transaksi ini selesai, versi lama atau baharu, bergantung pada COMMIT/ROOLBACK
, akan diiktiraf "mati" (tuple mati) apabila berlalu VACUUM
mengikut jadual dan dibersihkan.
Tetapi ini tidak akan berlaku serta-merta, tetapi masalah dengan "mati" boleh diperolehi dengan cepat - dengan berulang atau
#1: Saya Suka Memindahkannya
Katakan kaedah anda berfungsi pada logik perniagaan, dan tiba-tiba ia menyedari bahawa adalah perlu untuk mengemas kini medan X dalam beberapa rekod:
UPDATE tbl SET X = <newX> WHERE pk = $1;
Kemudian, apabila pelaksanaan berlangsung, ternyata medan Y juga harus dikemas kini:
UPDATE tbl SET Y = <newY> WHERE pk = $1;
... dan kemudian juga Z - mengapa membuang masa dengan perkara remeh?
UPDATE tbl SET Z = <newZ> WHERE pk = $1;
Berapa banyak versi rekod ini yang kita ada sekarang dalam pangkalan data? Ya, 4 keping! Daripada jumlah ini, satu adalah berkaitan dan 3 perlu dibersihkan selepas anda dengan [auto]VACUUM.
Jangan lakukan dengan cara ini! guna mengemas kini semua medan dalam satu permintaan β hampir selalu logik kaedah boleh diubah seperti ini:
UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;
#2: Penggunaan ADALAH BERBEZA DARI, Luke!
Jadi, anda masih mahu
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;
Permintaan dalam lebih kurang borang ini berlaku agak kerap dan hampir selalu bukan untuk mengisi medan baharu yang kosong, tetapi untuk membetulkan beberapa ralat dalam data. Pada masa yang sama, dia sendiri ketepatan data sedia ada tidak diambil kira sama sekali - tetapi sia-sia! Iaitu, rekod itu ditulis semula, walaupun ia mengandungi apa yang dikehendaki - tetapi mengapa? Mari kita betulkan:
UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>;
Ramai orang tidak menyedari kewujudan pengendali yang begitu hebat, jadi berikut adalah helaian tipu IS DISTINCT FROM
dan pengendali logik lain untuk membantu:
... dan sedikit tentang operasi di kompleks ROW()
-ungkapan:
#3: Saya mengenali kekasih saya dengan... menyekat
sedang dilancarkan dua proses selari yang sama, yang setiap satunya cuba menandakan entri bahawa ia "sedang dijalankan":
UPDATE tbl SET processing = TRUE WHERE pk = $1;
Walaupun proses ini sebenarnya melakukan perkara yang bebas antara satu sama lain, tetapi dalam ID yang sama, pelanggan kedua akan "dikunci" pada permintaan ini sehingga transaksi pertama selesai.
Penyelesaian #1: tugas dikurangkan kepada yang sebelumnya
Mari tambah lagi IS DISTINCT FROM
:
UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;
Dalam borang ini, permintaan kedua tidak akan mengubah apa-apa dalam pangkalan data, semuanya sudah seperti yang sepatutnya - oleh itu, penyekatan tidak akan berlaku. Seterusnya, kami memproses fakta "tidak mencari" rekod dalam algoritma yang digunakan.
Penyelesaian #2: kunci nasihat
Topik besar untuk artikel berasingan, yang boleh anda baca
Penyelesaian #3: panggilan bodoh
Tetapi inilah yang sepatutnya berlaku kepada anda kerja serentak dengan rekod yang sama? Atau adakah anda keliru dengan algoritma untuk memanggil logik perniagaan di sisi pelanggan, sebagai contoh? Dan jika anda memikirkannya?..
Sumber: www.habr.com