Adakah cara untuk memilih tanpa menyebabkan penguncian di MySQL?

126

Pertanyaan:

SELECT COUNT(online.account_id) cnt from online;

Tetapi meja online juga dimodifikasi oleh suatu peristiwa, jadi sering saya bisa melihat kunci dengan berlari show processlist.

Apakah ada tata bahasa di MySQL yang dapat membuat pernyataan pilih tidak menyebabkan kunci?

Dan saya lupa menyebutkan di atas bahwa itu ada di database budak MySQL.

Setelah saya menambahkan ke my.cnf:transaction-isolation = READ-UNCOMMITTED dalam slave akan menemui kesalahan:

Kesalahan 'Pencatatan biner tidak dimungkinkan. Pesan: Level transaksi 'READ-UNCOMMITTED' di InnoDB tidak aman untuk mode binlog 'STATEMENT' 'pada permintaan

Jadi, apakah ada cara yang kompatibel untuk melakukan ini?

Oh Tuhan
sumber
3
Untuk orang lain yang mengalami pertanyaan ini dan mengalami kesulitan dengan kunci di atas meja mereka: Bagaimana mySQL menggunakan kunci secara internal tergantung pada mesin penyimpanan. Baca jawabannya oleh @ zombat di bawah ini.
Simon Forsberg

Jawaban:

169

Menemukan sebuah artikel berjudul "MYSQL WITH NOLOCK"

https://web.archive.org/web/20100814144042/http://sqldba.org/articles/22-mysql-with-nolock.aspx

di MS SQL Server Anda akan melakukan hal berikut:

SELECT * FROM TABLE_NAME WITH (nolock)

dan setara MYSQL adalah

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;

EDIT

Michael Mior menyarankan yang berikut (dari komentar)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;
Jon Erickson
sumber
54
Hanya catatan untuk pembaca masa depan yang mungkin ingin Anda hilangkan SESSIONdan dengan demikian tingkat transaksi hanya berlaku untuk transaksi berikutnya. Kemudian, cukup ganti pernyataan ketiga di atas dengan COMMIT. Ini akan menjadi noop dalam hal ini, tetapi memiliki efek samping untuk mengakhiri transaksi dan mengatur ulang ke tingkat isolasi default.
Michael Mior
3
Hanya sebuah catatan, tautan itu sudah mati ... :(
longda
5
Maaf, tetapi saya harus membatalkan jawaban ini karena tidak menyebutkan perbedaan yang sangat penting antara InnoDB dan MyISAM di sini. Seperti yang dinyatakan oleh @omg di atas, ini akan bekerja untuk InnoDB tetapi tidak untuk tabel MyISAM.
Simon Forsberg
4
@Craig Ini tentu tidak akurat yang MyISAM tidak mengeluarkan BACA kunci selama query SELECT - ada yang kunci dan menentang untuk InnoDB mereka kunci yang tabel kunci, memblokir semua kunci MENULIS diminta dan semua pertanyaan berikutnya selama eksekusi. Pertanyaan aslinya tampaknya tentang InnoDB meskipun dan tingkat isolasi juga tidak ada untuk MyISAM - dokumen untuk SET TRANSACTIONpernyataan tersebut : "Pernyataan ini menetapkan tingkat isolasi transaksi, yang digunakan untuk operasi pada tabel InnoDB."
syneticon-dj
1
Titik kebobolan. :-) Saya benar-benar mencoba merujuk pada perilaku penguncian MyISAM vs InnoDB. Solusi ini berdasarkan tingkat isolasi tidak berlaku untuk MyISAM, yang tidak transaksional, sehingga menggunakan sebuah kunci meja sederhana. MyISAM UPDATE dan DELETE harus menunggu kunci tabel dihapus, sehingga setiap antrian SELECT berikutnya di belakang permintaan penulisan, diblokir sampai penulisan selesai. MyISAM tidak memiliki "bacaan kotor" dan tidak ada cara untuk memungkinkan sebagian besar penulisan terjadi bersamaan dengan bacaan, sehingga tidak ada gunanya mencengkeram setiap komentar di sini "gagal menangani MyISAM." Saya pikir itulah yang saya maksudkan. :-)
Craig
24

Jika tabelnya adalah InnoDB, lihat http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html - tabel ini menggunakan baca-konsisten (mode tanpa-penguncian) untuk SELECT "yang melakukan tidak menentukan FOR UPDATE atau LOCK IN SHARE MODE jika opsi innodb_locks_unsafe_for_binlog diatur dan tingkat isolasi transaksi tidak diatur ke SERIALIZABLE. Dengan demikian, tidak ada kunci yang ditetapkan pada baris yang dibaca dari tabel yang dipilih ".

Alex Martelli
sumber
16

Menggunakan

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

Versi 5.0 Documents ada di sini .

Versi 5.1 Documents ada di sini .

Bukan saya
sumber
2
Terima kasih, saya pikir sudah dekat, tetapi berapa lama pernyataan ini akan mempengaruhi? Saya akan menggunakan pernyataan ini dalam program PHP, dan sebaiknya atur ulang LEVEL TRANSAKSI ISOLASI secara otomatis setelah kueri selesai
omg
14

Anda mungkin ingin membaca halaman ini dari manual MySQL. Bagaimana meja dikunci tergantung pada jenis tabel itu.

MyISAM menggunakan kunci tabel untuk mencapai kecepatan baca yang sangat tinggi, tetapi jika Anda memiliki pernyataan UPDATE yang menunggu, maka SELECTS di masa mendatang akan mengantri di belakang UPDATE.

Tabel InnoDB menggunakan penguncian tingkat baris, dan Anda tidak akan memiliki seluruh tabel terkunci di belakang PEMBARUAN. Ada beberapa jenis masalah penguncian yang terkait dengan InnoDB, tetapi Anda mungkin menemukannya cocok dengan kebutuhan Anda.

zombat
sumber
1
Akankah "MENETAPKAN TINGKAT ISOLASI TRANSAKSI BACA TIDAK DIKETAHUI" berfungsi untuk tabel MyISAM?
omg
5
Tabel MyISAM tidak mendukung transaksi dalam bentuk apa pun. Kueri transaksional akan berjalan pada tabel MyISAM, sehingga kueri yang Anda sebutkan di atas akan dijalankan, tetapi tidak berpengaruh.
Zombat
1
Lalu apa yang bisa saya lakukan untuk menghindari antrian SELECTS dalam kasus MyISAM?
omg
6
apa yang bisa saya lakukan untuk menghindari antrian SELECTS dalam kasus MyISAM? Beralih ke innodb. MyISAM menggunakan kunci level tabel untuk setiap permintaan. Itu cacat utama.
Frank Farmer
2

Tergantung pada jenis tabel Anda, penguncian akan tampil berbeda, tetapi begitu juga SELECT akan dihitung. Untuk tabel MyISAM hitungan SELECT sederhana (*) DARI tabel tidak boleh mengunci tabel karena mengakses data meta untuk menarik jumlah catatan. Innodb akan memakan waktu lebih lama karena harus mengambil tabel dalam snapshot untuk menghitung catatan, tetapi seharusnya tidak menyebabkan penguncian.

Anda setidaknya harus mengatur concurrent_insert ke 1 (default). Kemudian, jika tidak ada "celah" dalam file data untuk tabel untuk diisi, sisipan akan ditambahkan ke file dan SELECT dan INSERT dapat terjadi secara bersamaan dengan tabel MyISAM. Perhatikan bahwa menghapus catatan akan membuat "celah" pada file data yang akan berusaha diisi dengan sisipan dan pembaruan di masa mendatang.

Jika Anda jarang menghapus catatan, maka Anda dapat mengatur concurrent_insert sama dengan 2, dan sisipan akan selalu ditambahkan ke akhir file data. Kemudian pilih dan sisipan dapat terjadi secara bersamaan, tetapi file data Anda tidak akan pernah menjadi lebih kecil, tidak peduli berapa banyak catatan yang Anda hapus (kecuali semua catatan).

Intinya, jika Anda memiliki banyak pembaruan, menyisipkan, dan memilih pada tabel, Anda harus membuatnya InnoDB. Anda dapat dengan bebas mencampur tipe tabel dalam suatu sistem.

Brent Baisley
sumber
1

cara lain untuk mengaktifkan read kotor di mysql adalah menambahkan petunjuk: LOCK IN SHARE MODE

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 
PuGong
sumber
"Jika autocommit diatur ke 1, klausa LOCK IN SHARE MODE dan FOR UPDATE tidak berpengaruh." ... dan autocommit = 1 adalah default
Martin Zvarík
0

Dari referensi ini :

Jika Anda memperoleh kunci meja secara eksplisit dengan LOCK TABLES, Anda dapat meminta kunci READ LOCAL daripada kunci READ untuk mengaktifkan sesi lain untuk melakukan sisipan bersamaan saat Anda memiliki meja terkunci.

Paul Sonier
sumber
0

SELECT biasanya tidak melakukan penguncian yang Anda pedulikan di tabel InnoDB. Tingkat isolasi transaksi default berarti bahwa pemilih tidak mengunci barang.

Tentu saja pertengkaran masih terjadi.

MarkR
sumber
1
Saya tahu posting ini sudah tua, tetapi jawaban ini terlalu umum, dan terkadang hanya benar. Lihat dev.mysql.com/doc/refman/5.0/id/innodb-locks-set.html . Kunci pasti yang diperoleh untuk membaca, tergantung pada tingkat isolasi. Secara khusus, dalam hal ini, poster tersebut berurusan dengan basis data yang direplikasi dan dinyatakan secara eksplisit bahwa ia dapat menggunakan show processlistuntuk benar-benar melihat kunci. Jadi aman untuk mengasumsikan bahwa sebenarnya ada kunci yang diambil.
Craig
1
Jawabannya selalu benar. Tentu saja ada beberapa penguncian - beberapa mutex internal di dalam innodb yang digunakan (innodb buffer pool mutex, misalnya). Sebagian besar pengguna tidak peduli atau memperhatikan kunci ini dan mereka biasanya hanya bersaing selama operasi DDL (seperti jika Anda memiliki kumpulan buffer 16G dan melakukan "drop table" di utas lain). Tapi itu tidak mengambil kunci baris apa pun secara default. Itu yang saya maksud. Jawabannya agak kabur.
MarkR
1
Selalu selalu? Bagaimana jika tingkat isolasi transaksi diatur ke serializable, atau pernyataan pilih menggunakan LOCK IN SHARE MODE dan autocommit dinonaktifkan? Saya tahu banyak (kebanyakan / semua?) Server database sekarang menggunakan isolasi snapshot secara default alih-alih serialisasi yang sebenarnya, tetapi bukankah masih ada pembenaran sesekali untuk memaksa membaca serializable? Tapi sepertinya Anda mengatakan bahwa dalam semua kasus normal yang jauh, kondisi default di MySQL tidak menyebabkan kunci baca yang mempengaruhi utas lainnya, jadi jangan khawatir tentang masalah yang tidak Anda miliki? Saya mencoba untuk membatalkan downvote saya, BTW. Maaf ...
Craig
3
Saya berkata "tidak normal". Maksud saya jika Anda melakukan pemilihan normal (tanpa FOR UPDATE atau LOCK IN SHARE MODE) dan menggunakan tingkat isolasi transaksi default. Ada beberapa kasus yang valid untuk mengubah tingkat isolasi, tetapi saya hanya akan melakukannya berdasarkan per sesi tidak pernah default.
MarkR