Bagaimana cara kueri dan meningkatkan nilai (penghitung) dengan cara yang aman? (hindari kondisi balapan)

10

Dalam tabel di mana setiap baris memiliki penghitung (hanya nilai integer), saya perlu mendapatkan nilai saat ini dan meningkatkannya pada saat yang sama .

Secara efektif, saya ingin melakukan ini:

SELECT counter FROM table WHERE id=123
UPDATE table SET counter=counter+1 WHERE id=123

Tetapi melakukan ini sebagai dua kueri jelas tidak aman: beberapa proses melakukan hal yang sama (pada baris yang sama) mungkin mendapatkan nilai penghitung yang sama. Saya ingin mereka semua menjadi unik, sehingga setiap proses akan mendapatkan nilai aktual saat ini dan meningkatkannya satu per satu.

Saya bisa memikirkan konstruksi di mana saya menerapkan kunci manual per baris, tapi saya ingin tahu apakah ada cara yang lebih mudah untuk melakukan ini?

RocketNuts
sumber
mungkin menggunakan transaksi?
ypercubeᵀᴹ

Jawaban:

15

Pernyataan pembaruan berfungsi dengan baik tanpa harus memilih sebelumnya! Karena pernyataan tunggal aman menurut definisi, bahkan dua kueri UPDATE yang dilakukan pada saat yang sama hanya akan menghasilkan baris yang bertambah dua kali.

Jika Anda benar-benar ingin memilih nilai untuk skrip PHP Anda, lakukan sesuatu dengannya dan kemudian ingin memperbarui nilai penghitung ini, Anda dapat melakukan hal berikut:

BEGIN;
SELECT `counter` FROM `table` WHERE `id` = 123 FOR UPDATE;
UPDATE `table` SET `counter` = `counter`+1 WHERE `id` = 123;
COMMIT;

Ini memulai transaksi baru, lalu memilih baris yang ingin Anda perbarui dan menguncinya secara eksklusif. Anda kemudian dapat memperbarui mereka dengan aman tanpa khawatir tentang klien lain mengubah konten mereka atau bahkan mengakses baris yang dikunci. Akhirnya Anda perlu melakukan perubahan Anda.

Anda juga harus membaca sesuatu tentang tingkat isolasi . Anda mungkin tidak ingin nilai seperti READ UNCOMMITTEDlevel isolasi. Segala sesuatu yang lain harusnya baik untuk use case ini.

GhostGambler
sumber
Saya membaca di tempat lain bahwa UPDATE table SET counter = counter + 1itu cukup atom? Apakah Anda masih memerlukan laporan transaksi yang mengelilinginya?
CMCDragonkai
@ CMCDragonkai Permintaan Anda sendiri adalah atomik, tetapi jika Anda memilih nilainya sebelum dan tidak menggunakan FOR UPDATEdan bertransaksi, nilai yang Anda pilih mungkin berbeda dari yang digunakan dalam permintaan pembaruan. Kombinasi kueri saya mengunci baris segera setelah nilainya dipilih dan karenanya memastikan bahwa nilai penghitung yang tepat ini akan digunakan dalam kueri pembaruan.
GhostGambler
Ok, tapi itu hanya perlu jika aku melakukan pekerjaan selain menambah kan? Seperti berdiri, permintaan pembaruan atom tunggal cukup baik jika hanya itu yang ingin saya lakukan?
CMCDragonkai
1
@ CMCDragonkai Jika Anda tidak menjalankan kueri lain yang menyentuh kolom, Anda baik.
GhostGambler