MySQL: apakah transaksi mengunci baris?

13

Saya belum pernah mencoba menggunakan transaksi MySQL sebelumnya, saya hanya ingin mengklarifikasi sesuatu.

Jika dua pengguna menjalankan kueri pada waktu yang sangat tepat, bagaimana MySQL akan menangani ini? mis. pengguna mencoba memperbarui catatan.

user1: perbarui tabel set kolom = kolom - 4 di mana column_id = 1;

user2: perbarui tabel set kolom = kolom - 7 di mana column_id = 1;

Sekarang jika saya menggunakan transaksi, akankah MySQL memilih kueri mana yang akan dieksekusi lebih dulu dan mengunci pengguna kedua hingga kueri pertama dilakukan? Apakah itu kunci meja atau kunci baris?

Bagaimana jika pengguna ketiga akan mengeluarkan pernyataan pilih? Berapa nilai yang akan dikembalikan oleh MySQL?

PS ini akan ada di Innodb.

zer09
sumber

Jawaban:

17

Pernyataan tunggal seperti itu berfungsi sama dengan MyISAM atau InnoDB, dengan transaksi atau dengan autocommit = ON. Itu cukup blok untuk melakukan kueri, sehingga memblokir koneksi lainnya. Setelah selesai, koneksi lainnya berlanjut. Dalam semua kasus, kolom segera dikurangi dengan 11.

Pengguna ketiga dapat melihat nilai dikurangi dengan 0 atau 4 atau 7 atau 11. "Waktu yang sangat tepat" tidak benar-benar mungkin karena, pada beberapa titik dalam pelaksanaan setiap pernyataan, kunci berulir tunggal dicentang / disetel / apa pun . Yaitu, mereka akan diserialkan, sangat cepat sehingga Anda tidak bisa melihatnya.

InnoDB hanya mengunci baris, bukan tabel. (Oke, pernyataan DDL melakukan kunci yang lebih berani.)

Yang lebih menarik adalah transaksi yang mengubah dua hal, atau yang membutuhkan waktu cukup lama:

Intention Case: Item tunggal tetapi membutuhkan waktu:

BEGIN;
SELECT something;
think about it for a while
UPDATE that something;
COMMIT;

Pilih harus ditulis demikian:

SELECT something  FOR UPDATE;

Ini memberi tahu koneksi lain "Saya bermaksud memperbarui baris; tolong jangan mengacaukan saya". (Saya mengemukakan contoh ini, karena banyak pemula melewatkan kehalusan ini.)

Kasus kebuntuan: Bercak dengan 2 hal:

BEGIN;    -- in one connection
UPDATE thing_1;
UPDATE thing_2;
COMMIT;

BEGIN;    -- in another connection, at the "exact same time"
UPDATE thing_2;
UPDATE thing_1;
COMMIT;

Ini adalah contoh klasik dari kebuntuan - masing-masing meraih satu hal dan kemudian meraih yang lainnya. Jelas itu tidak bisa dibuat berfungsi. Satu transaksi terbunuh; yang lainnya selesai. Karenanya, Anda harus memeriksa kesalahan, sehingga Anda dapat menemukannya.

Reaksi normal terhadap kebuntuan adalah memutar ulang seluruh transaksi yang gagal. Pada saat itu, koneksi lain tidak akan mengganggu, dan itu harus dilanjutkan tanpa masalah. (OK, koneksi lain bisa membuat kebuntuan lain.)

Delay Case: Jika kedua koneksi mengambil beberapa hal dalam urutan yang sama , maka satu dapat ditunda hingga yang lainnya selesai. Untuk menjaga ini dari "menunggu selamanya", ada default 50 detik innodb_lock_wait_timeout. Pasangan sederhana Anda UPDATEssebenarnya adalah contoh dari kasus ini. Satu akan selesai dengan segera; yang lain macet sampai yang pertama selesai.

Perhatikan bagaimana Deadlock dapat (dalam beberapa kasus) diubah menjadi Keterlambatan dengan secara konsisten memesan hal-hal yang Anda sentuh.

autocommit = 1: Dengan pengaturan ini dan tanpa menelepon BEGIN, setiap pernyataan secara efektif:

BEGIN;
your statement
COMMIT;

autocommit = 0: Ini masalah yang menunggu untuk terjadi. Ketika Anda melakukan kueri tulis, a BEGINsecara implisit dihasilkan. Namun, adalah tanggung jawab Anda untuk akhirnya menerbitkan COMMIT. Jika Anda gagal melakukannya, Anda akan bertanya-tanya mengapa sistem Anda digantung. (Bug pemula lainnya.) Saran saya: "Jangan pernah gunakan =0".

Rick James
sumber