Jaminan yang Anda nyatakan berlaku dalam kasus sederhana ini, tetapi tidak harus dalam kueri yang sedikit lebih kompleks. Lihat akhir jawaban untuk contoh.
Kasus sederhana
Dengan asumsi bahwa col1 unik, memiliki tepat satu nilai "2", atau memiliki pengurutan yang stabil sehingga setiap UPDATE
cocok dengan baris yang sama dalam urutan yang sama:
Apa yang akan terjadi untuk kueri ini adalah bahwa utas akan menemukan baris dengan col=2 dan semuanya mencoba mengambil kunci tulis pada Tuple itu. Tepat satu dari mereka akan berhasil. Yang lain akan memblokir menunggu transaksi utas pertama dilakukan.
Tx pertama itu akan menulis, melakukan, dan mengembalikan jumlah baris 1. Komit akan melepaskan kunci.
Tx yang lain akan mencoba lagi untuk mengambil kunci. Satu per satu mereka akan berhasil. Setiap transaksi pada gilirannya akan melalui proses berikut:
- Dapatkan kunci tulis pada tupel yang diperebutkan.
- Cek ulang
WHERE col=2
kondisi setelah mendapatkan kunci. - Pemeriksaan ulang akan menunjukkan bahwa kondisi sudah tidak sesuai lagi sehingga
UPDATE
akan melewati baris itu. UPDATE
tidak memiliki baris lain sehingga akan melaporkan nol baris yang diperbarui.- Berkomitmen, lepaskan kunci untuk tx berikutnya mencoba memegangnya.
Dalam kasus sederhana ini, penguncian tingkat baris dan pemeriksaan ulang kondisi secara efektif membuat serial pembaruan. Dalam kasus yang lebih kompleks, tidak terlalu banyak.
Anda dapat dengan mudah menunjukkan ini. Buka katakan empat sesi psql. Yang pertama, kunci tabel dengan BEGIN; LOCK TABLE test;
. Di sisa sesi, jalankan UPDATE
yang identik s - mereka akan memblokir kunci level meja. Sekarang lepaskan kunci dengan COMMIT
ting sesi pertama Anda. Lihat mereka berlomba. Hanya satu yang akan melaporkan jumlah baris 1, yang lain akan melaporkan 0. Ini mudah otomatis dan ditulis untuk pengulangan dan peningkatan ke lebih banyak koneksi/utas.
Untuk mempelajari lebih lanjut, baca aturan untuk penulisan bersamaan , halaman 11 dari Masalah konkurensi PostgreSQL - lalu baca sisa presentasi itu.
Dan jika col1 tidak unik?
Seperti yang dicatat Kevin di komentar, jika col
tidak unik sehingga Anda dapat mencocokkan beberapa baris, lalu eksekusi UPDATE
. yang berbeda bisa mendapatkan pesanan yang berbeda. Ini dapat terjadi jika mereka memilih paket yang berbeda (misalnya salah satunya adalah melalui PREPARE
dan EXECUTE
dan yang lainnya langsung, atau Anda mengacaukan enable_
GUCs) atau jika paket yang mereka gunakan menggunakan jenis nilai yang sama yang tidak stabil. Jika mereka mendapatkan baris dalam urutan yang berbeda maka tx1 akan mengunci satu tupel, tx2 akan mengunci yang lain, maka mereka masing-masing akan mencoba untuk mendapatkan kunci pada tupel yang sudah terkunci satu sama lain. PostgreSQL akan membatalkan salah satunya dengan pengecualian kebuntuan. Ini adalah alasan bagus lainnya mengapa semua kode database Anda harus selalu bersiaplah untuk mencoba kembali transaksi.
Jika Anda berhati-hati untuk memastikan UPDATE
bersamaan s selalu mendapatkan baris yang sama dalam urutan yang sama Anda masih dapat mengandalkan perilaku yang dijelaskan di bagian pertama jawaban.
Dengan frustrasi, PostgreSQL tidak menawarkan UPDATE ... ORDER BY
jadi memastikan bahwa pembaruan Anda selalu memilih baris yang sama dalam urutan yang sama tidak sesederhana yang Anda inginkan. A SELECT ... FOR UPDATE ... ORDER BY
diikuti dengan UPDATE
yang terpisah sering kali paling aman.
Kueri yang lebih kompleks, sistem antrian
Jika Anda melakukan kueri dengan beberapa fase, melibatkan banyak tupel, atau kondisi selain kesetaraan, Anda bisa mendapatkan hasil mengejutkan yang berbeda dari hasil eksekusi serial. Secara khusus, menjalankan sesuatu secara bersamaan seperti:
UPDATE test SET col = 1 WHERE col = (SELECT t.col FROM test t ORDER BY t.col LIMIT 1);
atau upaya lain untuk membangun sistem "antrian" sederhana akan *gagal* untuk bekerja seperti yang Anda harapkan. Lihat dokumen PostgreSQL tentang konkurensi dan presentasi ini untuk info lebih lanjut.
Jika Anda menginginkan antrian kerja yang didukung oleh database, ada solusi teruji yang menangani semua kasus sudut yang sangat rumit. Salah satu yang paling populer adalah PgQ . Ada kertas PgCon yang berguna pada topik, dan pencarian Google untuk 'antrean postgresql' penuh dengan hasil yang bermanfaat.
BTW, bukan LOCK TABLE
anda dapat menggunakan SELECT 1 FROM test WHERE col = 2 FOR UPDATE;
untuk mendapatkan kunci tulis hanya pada tuple. Itu akan memblokir pembaruan terhadapnya tetapi tidak memblokir penulisan ke tupel lain atau memblokir pembacaan apa pun. Itu memungkinkan Anda untuk mensimulasikan berbagai jenis masalah konkurensi.