Saya tidak mengerti apa yang dimaksud Craig Ringer ketika dia berkomentar:
Solusi ini dapat mengalami pembaruan yang hilang jika transaksi memasukkan kembali; tidak ada pemeriksaan untuk memastikan bahwa UPDATE memengaruhi setiap baris.
di https://stackoverflow.com/a/8702291/14731 . Harap berikan urutan contoh acara (mis. Thread 1 melakukan X, Thread 2 melakukan Y) yang menunjukkan bagaimana pembaruan yang hilang dapat terjadi.
postgresql
concurrency
cte
upsert
Gili
sumber
sumber
Jawaban:
Saya pikir saya mungkin bermaksud menambahkan komentar itu pada jawaban sebelumnya, tentang dua pernyataan terpisah. Sudah lebih dari setahun yang lalu, jadi saya tidak sepenuhnya yakin lagi.
Kueri berbasis wCTE tidak benar-benar menyelesaikan masalah yang seharusnya, tetapi setelah meninjaunya kembali lebih dari setahun kemudian saya tidak melihat kemungkinan pembaruan yang hilang dalam versi wCTE.
(Perhatikan bahwa semua solusi ini hanya akan berfungsi dengan baik jika Anda mencoba mengubah tepat satu baris pada setiap transaksi. Segera setelah Anda mencoba untuk melakukan beberapa perubahan dalam satu transaksi, semuanya menjadi berantakan karena kebutuhan untuk mencoba lagi loop pada rollback. Minimal Anda harus menggunakan savepoint di antara setiap perubahan.)
Versi dua pernyataan tunduk pada pembaruan yang hilang.
Versi yang menggunakan dua pernyataan terpisah dapat mengalami pembaruan yang hilang kecuali aplikasi memeriksa jumlah baris yang terpengaruh dari
UPDATE
pernyataan danINSERT
pernyataan dan mencoba lagi jika keduanya nol.Bayangkan apa yang terjadi jika Anda memiliki dua transaksi secara
READ COMMITTED
terpisah.UPDATE
(tidak ada efek)INSERT
(menyisipkan baris)UPDATE
(tidak ada efek, baris yang dimasukkan oleh TX1 belum terlihat)COMMIT
s.INSERT
, * yang mendapat snapshot baru yang dapat melihat baris yang dilakukan oleh TX1. TheEXISTS
klausul pengembalian benar, karena TX2 sekarang dapat melihat baris dimasukkan oleh TX1.Jadi TX2 tidak berpengaruh. Kecuali jika aplikasi memeriksa jumlah baris dari pembaruan dan sisipan dan coba lagi jika keduanya melaporkan nol baris, ia tidak akan tahu bahwa transaksi tidak berpengaruh dan akan melanjutkan dengan gembira.
Satu-satunya cara ia dapat memeriksa jumlah baris yang terpengaruh adalah menjalankannya sebagai dua pernyataan terpisah dan bukan multi-pernyataan, atau menggunakan prosedur.
Anda dapat menggunakan
SERIALIZABLE
isolasi, tetapi Anda masih perlu mengulangi loop untuk menghadapi kegagalan serialisasi.Versi wCTE melindungi terhadap masalah pembaruan yang hilang karena
INSERT
tergantung pada apakahUPDATE
mempengaruhi setiap baris, bukan pada permintaan yang terpisah.WCTE tidak menghilangkan pelanggaran unik
Versi CTE yang dapat ditulisi masih belum bisa diandalkan.
Pertimbangkan dua transaksi yang menjalankan ini secara bersamaan.
Keduanya menjalankan klausa VALUES.
Sekarang keduanya menjalankan
UPDATE
porsi. Karena tidak ada baris yang cocok denganUPDATE
klausa s where, keduanya mengembalikan resultset kosong dari pembaruan dan tidak membuat perubahan.Sekarang keduanya menjalankan
INSERT
porsinya. KarenaUPDATE
baris nol yang dikembalikan untuk kedua kueri, keduanya mencoba keINSERT
baris.Satu berhasil. Satu melemparkan pelanggaran unik dan batal.
Ini bukan alasan untuk khawatir tentang kehilangan data selama aplikasi memeriksa hasil kesalahan dari kueri-nya (yaitu aplikasi apa pun yang ditulis dengan sopan) dan mencoba ulang, tetapi itu membuat solusinya tidak lebih baik daripada versi dua pernyataan yang ada. Itu tidak menghilangkan kebutuhan untuk mengulangi loop.
Keuntungan yang ditawarkan wCTE dibandingkan versi dua pernyataan yang ada adalah bahwa ia menggunakan output dari
UPDATE
untuk memutuskan apakah akanINSERT
, daripada menggunakan kueri terpisah terhadap tabel. Itu sebagian optimasi, tetapi sebagian melindungi terhadap masalah dengan versi dua pernyataan yang menyebabkan pembaruan hilang; Lihat di bawah.Anda dapat menjalankan wCTE secara
SERIALIZABLE
terpisah, tetapi kemudian Anda hanya akan mendapatkan kegagalan serialisasi alih-alih pelanggaran unik. Itu tidak akan mengubah kebutuhan untuk mengulangi loop.WCTE tampaknya tidak rentan terhadap pembaruan yang hilang
Komentar saya menyarankan bahwa solusi ini dapat mengakibatkan pembaruan yang hilang, tetapi setelah meninjau bahwa saya pikir saya mungkin salah.
Sudah lebih dari setahun yang lalu, dan saya tidak dapat mengingat keadaan yang tepat, tetapi saya pikir saya mungkin melewatkan fakta bahwa indeks unik memiliki sebagian pengecualian dari aturan visibilitas transaksi untuk memungkinkan satu transaksi memasukkan menunggu yang lain untuk memasukkan atau memutar kembali sebelum melanjutkan.
Atau mungkin saya melewatkan fakta bahwa
INSERT
dalam WCTE tergantung pada apakahUPDATE
baris yang terpengaruh, bukan pada apakah baris kandidat ada di tabel.Konflik
INSERT
pada indeks unik menunggu komit / kembalikanKatakan bahwa satu salinan kueri berjalan, menyisipkan baris. Perubahan belum dilakukan. Tupel baru ada di heap dan indeks unik, tetapi belum terlihat oleh transaksi lain, terlepas dari tingkat isolasi.
Sekarang salinan kueri lain berjalan. Baris yang dimasukkan belum terlihat karena salinan pertama belum dilakukan, sehingga pembaruan tidak cocok dengan apa pun. Kueri akan melanjutkan untuk mencoba menyisipkan, yang akan melihat bahwa transaksi lain yang sedang berlangsung sedang memasukkan kunci yang sama dan akan memblokir menunggu transaksi itu dilakukan atau dibatalkan .
Jika transaksi pertama dilakukan, yang kedua akan gagal dengan pelanggaran unik, per di atas. Jika transaksi pertama gulung kembali maka yang kedua akan melanjutkan dengan memasukkannya sebagai gantinya.
The
INSERT
tergantung padaUPDATE
rowcount melindungi terhadap update yang hilangTidak seperti dalam kasus dua pernyataan, saya tidak berpikir WCTE rentan terhadap pembaruan yang hilang.
Jika
UPDATE
tidak memiliki efek, ituINSERT
akan selalu berjalan, karena itu sangat tergantung pada apakahUPDATE
melakukan sesuatu, bukan pada keadaan tabel eksternal. Jadi masih bisa gagal dengan pelanggaran unik, tetapi tidak bisa diam-diam gagal untuk memiliki efek dan kehilangan pembaruan sepenuhnya.sumber