Mendapatkan “Batas waktu tunggu tunggu melebihi; coba mulai ulang transaksi ”walaupun saya tidak menggunakan transaksi

267

Saya menjalankan UPDATEpernyataan MySQL berikut :

mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

Saya tidak menggunakan transaksi, jadi mengapa saya mendapatkan kesalahan ini? Saya bahkan mencoba me-restart server MySQL saya dan itu tidak membantu.

Tabel ini memiliki 406.733 baris.

Jason Swett
sumber

Jawaban:

211

Anda menggunakan transaksi; autocommit tidak menonaktifkan transaksi, itu hanya membuat mereka secara otomatis melakukan komit pada akhir pernyataan.

Apa yang terjadi adalah, beberapa utas lainnya menahan kunci rekaman pada beberapa rekaman (Anda memperbarui setiap catatan dalam tabel!) Terlalu lama, dan utas Anda sedang habis waktu.

Anda dapat melihat detail acara lebih lanjut dengan mengeluarkan a

SHOW ENGINE INNODB STATUS

setelah acara (dalam sqleditor). Idealnya lakukan ini pada mesin uji yang tenang.

MarkR
sumber
1
Apakah ada cara untuk menyimpan hasil ke file? Saya mencoba MENUNJUKKAN STATUS MESIN INNODB \ G> innodb_stat.txt tetapi tidak berhasil.
yantaq
14
Dari baris perintah: mysql [masukkan kredensial] -e "SHOW ENGINE INNODB STATUS \ G"> innodb_stat.txt
VenerableAgents
jika banyak utas mysql (atau proses) sedang sibuk, mis. beberapa permintaan membutuhkan waktu yang sangat lama, jadi Anda harus menunggu beberapa processuntuk diam. Jika demikian, Anda bisa mendapatkan kesalahan ini. Apakah saya benar?
zhuguowei
6
Menjalankan beberapa (2+) permintaan UPDATE pada baris yang sama selama satu transaksi juga akan menyebabkan kesalahan ini.
Okneloper
Bagi mereka yang menggunakan Konektor Python MySQL gunakan connection.commit()untuk melakukan INSERTatau UPDATEAnda baru saja melewatinya.
AER
334

CARA MEMBUAT UNLOCK untuk mengunci tabel di MySQL:

Memecahkan kunci seperti ini dapat menyebabkan atomicity dalam database tidak ditegakkan pada pernyataan sql yang menyebabkan kunci.

Ini peretasan, dan solusi yang tepat adalah memperbaiki aplikasi Anda yang menyebabkan kunci. Namun, ketika dolar ada di telepon, tendangan cepat akan membuat segalanya bergerak kembali.

1) Masukkan MySQL

mysql -u your_user -p

2) Mari kita lihat daftar tabel yang terkunci

mysql> show open tables where in_use>0;

3) Mari kita lihat daftar proses saat ini, salah satunya adalah mengunci meja Anda

mysql> show processlist;

4) Bunuh salah satu dari proses ini

mysql> kill <put_process_id_here>;
Eric Leschinski
sumber
14
Ini berbahaya dan meretas. Solusi yang tepat adalah memperbaiki aplikasi Anda.
Zenexer
71
Omong kosong, ini memungkinkan Anda membatalkan kekacauan dan kemudian memperbaiki aplikasi. Jika saya bisa memberi orang ini 100 suara untuk masalah ini yang harus saya perbaiki SEKARANG, saya akan melakukannya.
Lizardx
7
Saya setuju dengan Lizardx. Ini adalah solusi yang sangat berguna dalam situasi yang saya tidak punya hak istimewa untuk memanggil SHOW ENGINE INNODB STATUS
Travis Schneeberger
8
Bagaimana mematikan permintaan yang sudah berjalan lama dengan cara ini berbahaya ? Panggilan klien hanya akan mendapatkan kesalahan.
Xeoncross
7
@EricLeschinski Saya mengerti maksud Anda, tetapi saya harus bertanya mengapa Anda menggunakan database yang ceroboh seperti MySQL pada sistem Life-Critical ?
Xeoncross
96
mysql> set innodb_lock_wait_timeout=100

Query OK, 0 rows affected (0.02 sec)

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100   |
+--------------------------+-------+

Sekarang picu kunci lagi. Anda memiliki waktu 100 detik untuk mengeluarkan SHOW ENGINE INNODB STATUS\Gke database dan melihat transaksi lain mana yang mengunci milik Anda.

veen
sumber
8
Jawaban ini tidak menjelaskan mengapa penanya mendapatkan kesalahan mereka. Bisakah Anda menguraikan mengapa selain hanya memberikan jawaban?
Kereta luncur
5
+1 walaupun ini tidak menjawab pertanyaan secara langsung, bagi saya ini adalah referensi yang bagus untuk menyelesaikan masalah ini.
Jossef Harush
1
@ArtB dev.mysql.com/doc/innodb/1.1/en/… Intinya OP menerima kesalahan karena kunci dipanggil di atas meja dan waktu yang berlalu sebelum mengakhiri transaksi melebihi lock_wait_timeoutnilai
fyrye
70

Lihatlah apakah database Anda telah disesuaikan. Terutama isolasi transaksi. Bukan ide yang baik untuk meningkatkan variabel innodb_lock_wait_timeout.

Periksa tingkat isolasi transaksi database Anda di cli mysql:

mysql> SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation, @@session.transaction_isolation;
+-----------------------+-----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation  | @@session.tx_isolation |
+-----------------------+-----------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ | REPEATABLE-READ        |
+-----------------------+-----------------+------------------------+
1 row in set (0.00 sec)

Anda bisa mendapatkan perbaikan yang mengubah tingkat isolasi, gunakan oracle seperti BACA BERKOMITMASI alih-alih BACA ULANG (Default InnoDB)

mysql> SET tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> 

Coba juga gunakan SELECT FOR UPDATE hanya jika diperlukan.

saisyukusanagi
sumber
1
Ini adalah solusi bagus untuk mengunci masalah.
Wangi
3
Sangat bagus untuk saya, versi my.cnf adalah [mysqld] transaksi-isolasi = BACA-BERKOMITMEN
Benoit Gauthier
Sekedar catatan: MySQL 8 telah mengubah nama tx_isolationvariabel menjadi transaction_isolation.
xonya
Cares harus diambil untuk mengetahui apa yang Anda hadapi ketika Anda berubah dari baca-isolasi menjadi BACA BERKOMITMEN. Anda mungkin berakhir dengan data kotor yang mungkin ingin Anda hindari. Isolasi Wikipedia
huggie
27

Tidak ada solusi yang disarankan bekerja untuk saya tetapi ini berhasil.

Sesuatu memblokir eksekusi kueri. Kemungkinan besar permintaan lain memperbarui, menyisipkan, atau menghapus dari salah satu tabel dalam permintaan Anda. Anda harus mencari tahu apa itu:

SHOW PROCESSLIST;

Setelah Anda menemukan proses pemblokiran, temukan iddan jalankan:

KILL {id};

Jalankan kembali permintaan awal Anda.

BassMHL
sumber
Saya tidak sengaja MEMBUNUH semua proses yang terdaftar di SHOW PROCESSLIST; Sekarang saya mendapatkan 500 kesalahan di phpmyadmin. Apakah itu 500 kesalahan terkait dengan proses pembunuhan ini? Jika ya, bagaimana saya bisa memulai kembali.
Dashrath
Saya dapat melihat apa yang Anda gambarkan, namun membunuh prosesnya tidak berhasil. Perintah proses "terbunuh", tetapi tetap dalam daftar proses.
Torsten
12

100% dengan apa yang dikatakan MarkR. autocommit menjadikan setiap pernyataan transaksi satu pernyataan.

SHOW ENGINE INNODB STATUSharus memberi Anda beberapa petunjuk tentang alasan kebuntuan. Lihat baik-baik pada log kueri lambat Anda juga untuk melihat apa lagi yang meng-query tabel dan mencoba untuk menghapus apa pun yang melakukan tablescan penuh. Penguncian tingkat baris berfungsi dengan baik tetapi tidak ketika Anda mencoba untuk mengunci semua baris!

James C
sumber
5

Bisakah Anda memperbarui catatan lain dalam tabel ini, atau tabel ini banyak digunakan? Apa yang saya pikirkan adalah bahwa ketika sedang berusaha untuk mendapatkan kunci yang diperlukan untuk memperbarui catatan ini batas waktu yang telah ditentukan telah habis. Anda mungkin dapat menambah waktu yang dapat membantu.

John Kane
sumber
3
mungkin innodb_lock_wait_timeoutdimy.cnf
wajib
1
Saya atur di my.cnf: <br/> innodb_lock_wait_timeout = 120 <br/> Standarnya adalah 50 untuk mysql 5.5. Setelah perubahan ini, saya tidak dapat melihat masalah ini di unit test saya! Ini terjadi setelah beralih dari proxool ke kolam jdbc tomcat. Mungkin karena lebih banyak waktu transaksi dengan tomcat pool ?!
Champ
3

Jumlah baris tidak besar ... Buat indeks di account_import_id jika bukan kunci utama.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
budak
sumber
OMG ... ini baru saja menyelamatkan saya. Saya secara brutal mengacaukan DB produksi dengan menjatuhkan indeks dan ini memperbaikinya. Terimakasih.
Nick Gotch
2

Jika Anda baru saja membunuh permintaan besar, itu akan membutuhkan waktu rollback . Jika Anda mengeluarkan kueri lain sebelum kueri yang terbunuh selesai bergulir kembali, Anda mungkin mendapatkan kesalahan batas waktu kunci. Itulah yang terjadi pada saya. Solusinya hanya menunggu sebentar.

Detail:

Saya telah mengeluarkan permintaan DELETE untuk menghapus sekitar 900.000 dari sekitar 1 juta baris.

Saya menjalankan ini secara tidak sengaja (hanya menghapus 10% dari baris): DELETE FROM table WHERE MOD(id,10) = 0

Alih-alih ini (menghapus 90% dari baris): DELETE FROM table WHERE MOD(id,10) != 0

Saya ingin menghapus 90% dari baris, bukan 10%. Jadi saya membunuh proses di baris perintah MySQL, mengetahui bahwa itu akan memutar kembali semua baris yang telah dihapus sejauh ini.

Kemudian saya menjalankan perintah yang benar segera, dan mendapat lock timeout exceededkesalahan segera setelah itu. Saya menyadari bahwa kunci itu mungkin sebenarnya adalah rollbackpermintaan yang terbunuh yang masih terjadi di latar belakang. Jadi saya menunggu beberapa detik dan menjalankan kembali kueri.

Butkus Buttle
sumber
1

Pastikan tabel database menggunakan mesin penyimpanan InnoDB dan tingkat isolasi transaksi READ-COMMITTED.

Anda dapat memeriksanya dengan SELECT @@ GLOBAL.tx_isolation, @@ tx_isolation; di konsol mysql.

Jika tidak diatur untuk BACA-BERKOMITMEN maka Anda harus mengaturnya. Pastikan sebelum mengaturnya bahwa Anda memiliki hak SUPER di mysql.

Anda dapat mengambil bantuan dari http://dev.mysql.com/doc/refman/5.0/id/set-transaction.html .

Dengan mengatur ini saya pikir masalah Anda akan terpecahkan.


Anda mungkin juga ingin memeriksa bahwa Anda tidak berusaha memperbarui ini dalam dua proses sekaligus. Pengguna (@tala) telah menemui pesan kesalahan serupa dalam konteks ini, mungkin periksa ulang ...

Ravi Chhatrala
sumber
1

Saya berasal dari Google dan saya hanya ingin menambahkan solusi yang berfungsi untuk saya. Masalah saya adalah saya mencoba untuk menghapus catatan sebuah meja besar yang memiliki banyak FK di kaskade jadi saya mendapatkan kesalahan yang sama dengan OP.

Saya menonaktifkan autocommitdan kemudian berhasil hanya menambahkanCOMMIT di akhir kalimat SQL. Sejauh yang saya mengerti ini melepaskan buffer sedikit demi sedikit alih-alih menunggu di akhir perintah.

Agar sesuai dengan contoh OP, ini seharusnya bekerja:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

Jangan lupa untuk mengaktifkan autocommitkembali jika Anda ingin meninggalkan konfigurasi MySQL seperti sebelumnya.

mysql> set autocommit=1;

Kamae
sumber
0

Terlambat ke pesta (seperti biasa) namun masalah saya adalah fakta bahwa saya menulis beberapa SQL yang buruk (menjadi pemula) dan beberapa proses memiliki kunci pada catatan (s) <- tidak yakin bertele-tele yang sesuai. Saya akhirnya hanya harus: SHOW PROCESSLISTdan kemudian membunuh ID menggunakanKILL <id>

Smitty
sumber
0

Hal semacam ini terjadi pada saya ketika saya menggunakan bahasa php exit; di tengah transaksi. Maka transaksi ini "hang" dan Anda harus mematikan proses mysql (dijelaskan di atas dengan daftar proses;)

TomoMiha
sumber
0

Dalam contoh saya, saya menjalankan kueri yang tidak normal untuk memperbaiki data. Jika Anda mengunci tabel dalam kueri Anda, maka Anda tidak perlu berurusan dengan waktu tunggu Kunci:

LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;

Ini mungkin bukan ide yang baik untuk penggunaan normal.

Untuk info lebih lanjut, lihat: Manual Referensi MySQL 8.0

Jeff Luyet
sumber
0

Saya berlari ke ini memiliki 2 koneksi Doktrin DBAL, salah satunya sebagai non-transaksional (untuk log penting), mereka dimaksudkan untuk berjalan paralel tidak tergantung satu sama lain.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Tes integrasi saya dimasukkan ke dalam transaksi untuk pengembalian data setelah pengujian sangat.

beginTransaction()
CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery() // CONFLICT
)
rollBack()

Solusi saya adalah menonaktifkan transaksi pembungkus dalam tes-tes itu dan mengatur ulang data db dengan cara lain.

Fabian Picone
sumber
-4

Mengalami kesalahan yang sama, meskipun saya hanya memperbarui satu tabel dengan satu entri, tetapi setelah me-restart mysql, itu diselesaikan.

tubostik
sumber