Pelajaran modul (3/4)
Mengunci dan PILIH UNTUK UPDATE
Memahami cara kerja database secara terpisah pada satu terminal itu sederhana... tidak tunggu, saya sudah mengatakannya!
Terkadang "Pembaruan Atom" (misalnya stock = stock - 1) saja tidak cukup.
Bayangkan Anda harus memeriksa apakah pengguna memiliki cukup saldo untuk membeli barang senilai 50€:
1.SELECT balance FROM users WHERE id = 1; 2. JS: Jika saldo < 50, blokir! 3. Jika tidak, lanjutkan dengan checkout... 4. Jalankan pemotongannya! (Dan di sini Anda menemukan pengguna yang, saat memvalidasi pemeriksaan pada langkah 2, melepaskan pemeriksaan paralel lain yang membakar akun mereka dan sekarang Anda berada di bawah nol!).
UNTUK UPDATE Penguncian Eksplisit
Menambahkan FOR UPDATE di akhir SELECT secara ajaib mengubah pembacaan tidak bersalah Anda menjadi Kunci tersegel terhadap klien lain hingga COMMIT berikutnya. Tidak ada orang lain yang dapat melakukan UPDATE (atau pembacaan-untuk-pembaruan secara bersamaan) pada baris tertentu yang dipilih tersebut sampai Anda memutuskan apa yang harus dilakukan dengannya!
BEGIN;
-- If id=3 had already been locked by T1, T2 will sit in perpetual loading on this "read"
-- frozen in place, before receiving the raw data, until T1 runs COMMIT freeing everyone!
SELECT balance
FROM users
WHERE id = 3
FOR UPDATE;
-- at this point our Node JS instance lives with the guarantee, knowing for certain that NO ONE ELSE in the world has
-- manipulated (nor been able to read with intent to alter) that balance while we complete the application logic
UPDATE users SET balance = balance - 50 WHERE id = 3;
COMMIT;Bahkan ada FOR SHARE yang memungkinkan pembaca lain terus membaca (jika pada kunci dasar) tanpa memblokir pembacaan status, tetapi memblokir kemungkinan pembaruan berikutnya hingga Anda berkomitmen. Namun untuk modifikasi, selalu gunakan kekerasan dari klausa penguncian eksklusif:
Cobalah sendiri
Buka transaksi dengan cakupan waktu normal dan secara eksklusif mengunci pembacaan ID produk=5.\nJalankan kueri dua langkah:\n1. Buka transaksi (MULAI)\n2. Kueri 'produk' yang mengambil baris lengkap untuk ID 5 dengan jaminan tidak ada yang akan mengubahnya, dengan melampirkan opsi 'UNTUK PEMBARUAN'.
Tunjukkan petunjuk
BEGIN;, baris baru, SELECT * FROM produk WHERE id = 5 UNTUK UPDATE;
Solusi tersedia setelah 3 upaya
Terkadang transaksi terhenti selama periode bencana (Kebuntuan atau penantian tak terbatas).\nUntuk mencegah hal ini, Anda dapat memerintahkan database untuk berhenti saat baris tidak segera tersedia: menggunakan SEKARANG di akhir, yang akan menyebabkan pengecualian server SQL memantul kembali ke JS untuk dicoba ditangkap tanpa terhenti selamanya.\nJalankan SELECT untuk ID kategori=1 dan gunakan UNTUK UPDATE tetapi terapkan SEKARANG.
Tunjukkan petunjuk
PILIH * DARI kategori DIMANA id = 1 UNTUK UPDATE SEKARANG, TUNGGU;
Solusi tersedia setelah 3 upaya