Yang Anda inginkan adalah SELECT ... FOR UPDATE dari dalam konteks transaksi. PILIH UNTUK PEMBARUAN menempatkan kunci eksklusif pada baris yang dipilih, sama seperti jika Anda menjalankan UPDATE. Itu juga secara implisit berjalan di tingkat isolasi BACA BERKOMITMEN terlepas dari apa tingkat isolasi secara eksplisit ditetapkan. Perlu diketahui bahwa SELECT ... UNTUK PEMBARUAN sangat buruk untuk konkurensi dan hanya boleh digunakan jika benar-benar diperlukan. Ini juga memiliki kecenderungan untuk berkembang biak dalam basis kode karena orang memotong dan menempel.
Berikut adalah contoh sesi dari database Sakila yang menunjukkan beberapa perilaku kueri FOR UPDATE.
Pertama, supaya kami jelas, atur tingkat isolasi transaksi ke REPEATABLE READ. Ini biasanya tidak perlu, karena ini adalah level isolasi default untuk InnoDB:
session1> SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
session1> BEGIN;
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
Di sesi lain, perbarui baris ini. Linda menikah dan mengganti namanya:
session2> UPDATE customer SET last_name = 'BROWN' WHERE customer_id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Kembali di sesi1, karena kami berada di READATABLE READ, Linda masih LINDA WILLIAMS:
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
Tapi sekarang, kami ingin akses eksklusif ke baris ini, jadi kami memanggil UPDATE di baris tersebut. Perhatikan bahwa kita sekarang mendapatkan versi terbaru dari baris itu, yang diperbarui di sesi2 di luar transaksi ini. Itu bukan BACA ULANG, itu BACA BERKOMITMEN
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3 FOR UPDATE;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | BROWN |
+------------+-----------+
1 row in set (0.00 sec)
Mari kita coba set kunci di sesi1. Perhatikan bahwa session2 tidak dapat memperbarui baris.
session2> UPDATE customer SET last_name = 'SMITH' WHERE customer_id = 3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
Tapi kita masih bisa memilihnya
session2> SELECT c.customer_id, c.first_name, c.last_name, a.address_id, a.address FROM customer c JOIN address a USING (address_id) WHERE c.customer_id = 3;
+-------------+------------+-----------+------------+-------------------+
| customer_id | first_name | last_name | address_id | address |
+-------------+------------+-----------+------------+-------------------+
| 3 | LINDA | BROWN | 7 | 692 Joliet Street |
+-------------+------------+-----------+------------+-------------------+
1 row in set (0.00 sec)
Dan kita masih dapat memperbarui tabel anak dengan hubungan kunci asing
session2> UPDATE address SET address = '5 Main Street' WHERE address_id = 7;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
session1> COMMIT;
Efek samping lainnya adalah Anda meningkatkan kemungkinan menyebabkan jalan buntu.
Dalam kasus spesifik Anda, Anda mungkin ingin:
BEGIN;
SELECT id FROM `items` WHERE `status`='pending' LIMIT 1 FOR UPDATE;
-- do some other stuff
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `id`=<selected id>;
COMMIT;
Jika bagian "lakukan beberapa hal lain" tidak perlu dan Anda sebenarnya tidak perlu menyimpan informasi tentang baris tersebut, maka SELECT FOR UPDATE tidak perlu dan boros dan Anda bisa menjalankan pembaruan:
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `status`='pending' LIMIT 1;
Semoga ini masuk akal.
items
WHEREstatus
= 'pending' LIMIT 1 UNTUK PEMBARUAN;" dan mereka berdua melihat baris yang sama, maka yang satu akan mengunci yang lain. Saya berharap entah bagaimana itu akan dapat melewati baris yang terkunci dan kebagian item berikutnya yang tertunda ..