Deadlock MySql Gap Mengunci pada Sisipan

8

Saya mendapatkan Deadlock dari kunci celah di atas meja ketika sering memasukkannya ke berbagai sumber. Berikut ini adalah ikhtisar proses saya.

START TRANSACTION
  UPDATE vehicle_image
  SET active = 0
  WHERE vehicleID = SOMEID AND active = 1

  Loop:
    INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath
      ,vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
    VALUES (%s, %s, %s, %s, %s, %s, 1);
END TRANSACTION

Output dari SHOW Create table vehicle_image;adalah:

CREATE TABLE `vehicle_image` (
  `vehicleImageID` int(11) NOT NULL AUTO_INCREMENT,
  `vehicleID` int(11) DEFAULT NULL,
  `vehicleImageFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageSplashFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageThumbnailFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageMiniFilePath` varchar(200) DEFAULT NULL,
  `mainVehicleImage` bit(1) DEFAULT NULL,
  `active` bit(1) DEFAULT b'1',
  `userCreated` int(11) DEFAULT NULL,
  `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `userModified` int(11) DEFAULT NULL,
  `dateModified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`vehicleImageID`),
  KEY `active` (`active`),
  KEY `mainvehicleimage` (`mainVehicleImage`),
  KEY `vehicleid` (`vehicleID`)
) ENGINE=InnoDB AUTO_INCREMENT=22878102 DEFAULT CHARSET=latin1

Dan Deadlock terakhir diberikan oleh SHOW engine innodb status:

LATEST DETECTED DEADLOCK
------------------------
2018-03-27 12:31:15 11a58
*** (1) TRANSACTION:
TRANSACTION 5897678083, ACTIVE 2 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873570, OS thread handle 0x124bc, query id 198983754 ec2-34-239-240-179.compute-1.amazonaws.com 34.239.240.179 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006176, 'f180928(1)1522168276.230837full.jpg', 'f180928(1)1522168276.230837splash.jpg', 'f180928(1)1522168276.230837thumb.jpg', 'f180928(1)1522168276.230837mini.jpg', 1, 1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678083
  lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** (2) TRANSACTION:
TRANSACTION 5897678270, ACTIVE 1 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873571, OS thread handle 0x11a58, query id 198983849 ec2-35-171-169-21.compute-1.amazonaws.com 35.171.169.21 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006326, '29709(1)1522168277.4443843full.jpg', '29709(1)1522168277.4443843splash.jpg', '29709(1)1522168277.4443843thumb.jpg', '29709(1)1522168277.4443843mini.jpg', 1, 1)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 875 page no 238326 n bits 464
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
  lock_mode X locks gap before rec
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
  lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** WE ROLL BACK TRANSACTION (2)

Saya menjalankan banyak proses ini secara bersamaan tetapi tidak pernah menjalankan dua proses yang menggunakan hal yang sama VehicleID. Saya benar-benar bingung mengapa saya mendapatkan Deadlock.

Saya sementara telah memecahkan masalah dengan menggunakan level Isolasi READ COMMITTED, tetapi saya telah membaca bahwa ini memerlukan perubahan replikasi karena Anda harus melakukan replikasi level baris.

Saya telah membaca pertanyaan lain di sini yang mirip dengan saya, tetapi saya agak baru untuk SQL dan masih tidak dapat memahami mengapa ini terjadi.

Pertanyaan Serupa:
- Deadlock pada statments insert MySQL
- Deadlock MySQL InnoDB Untuk 2 query insert sederhana

MEMPERBARUI:

Saya menemukan bahwa menggunakan READ COMMITTEDbelum benar-benar menyelesaikan masalah. Saya masih belum menemukan mengapa kebuntuan terjadi dan saya benar-benar tidak tahu bagaimana cara mendiagnosis lebih jauh dari yang saya miliki saat ini. Saya terus mendapatkan Deadlock dalam sistem produksi saya. Bantuan apa pun akan dihargai.

Brian Sizemore
sumber
Bisakah Anda memberi kami lebih banyak detail - khususnya: Konfigurasi disk, jumlah piringan, karakteristik kinerja? RAM, berapa banyak? CPU, angka dan kinerja? Jumlah transaksi per detik, per menit, per jam dan per hari? Apakah tarif ini bervariasi dari waktu ke waktu? Bagaimana, tepatnya, kebuntuan ini memengaruhi kinerja? Beri kami hasil SHOW PROCESSLIST;. Sebagian besar waktu, REPEATABLE READadalah tingkat isolasi terbaik untuk sebagian besar aplikasi, jadi saya tidak akan terlalu khawatir menggunakannya. Apakah ada peningkatan kinerja yang nyata ketika Anda mengubahnya dari default - REPEATABLE READ?
Vérace
Bagaimana itu bisa berhasil? Anda memiliki string tanda kutip yang masuk VARCHARs.
Rick James
Di mana END LOOP?
Rick James
@ RickJames Saya tidak memiliki string tanda kutip masuk ke VARCHARS, kueri bekerja seperti yang diharapkan ketika berlari 95% dari waktu. END LOOP ditandai dengan tab kembali ke tingkat yang sama. Misalnya Loop dimulai, saya menjalankan pernyataan memasukkan itu beberapa kali, kemudian loop berakhir dan transaksi berakhir. Perhatikan bahwa loopini hanya kodesemu untuk mewakili apa yang sedang terjadi.
Brian Sizemore
@ Vérace Repeatable Read adalah default untuk tabel ini (menggunakan mesin innodb). Aku benar-benar diuji berubah dari repeatable readke read committedyang merupakan tingkat isolasi yang lebih rendah kemudian diulang membaca, tapi sayangnya hal itu tidak menghentikan kebuntuan. Saya tahu bahwa perangkat keras akan mempengaruhi server (ini adalah contoh EC2, saya harus mencari secara spesifik) tetapi saya tidak berpikir bahwa informasi itu perlu untuk memahami mengapa kebuntuan terjadi. Sifat sporadis ini juga membuatnya sulit untuk menangkap output dari daftar proses acara; ketika kebuntuan terjadi.
Brian Sizemore

Jawaban:

4

Saya bukan ahli MySQL, tetapi dengan tampilan Deadlock log Anda, meskipun Anda MEMASANG ID kendaraan yang berbeda per pernyataan, mereka memerlukan seluruh datapage (238326) dari indeks VehicleIDnon-cluster untuk dikunci .

Fakta bahwa Anda kadang-kadang mendapatkan kebuntuan berarti bahwa dalam 1 halaman Anda memiliki beberapa ID kendaraan, jadi ada kemungkinan kecil bahwa 2 proses yang berbeda akan membutuhkan kunci untuk halaman yang sama.

Hal terbaik untuk disarankan adalah menjaga transaksi Anda sekecil mungkin .

Jika ada beberapa cara yang dapat Anda lakukan berikut ini, ini akan membantu mengurangi kemungkinan kebuntuan:

START TRANSACTION;
  UPDATE vehicle_image SET active = 0 WHERE vehicleID = SOMEID and active = 1;
END TRANSACTION;
Loop:
  START TRANSACTION;
  INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath,
    vehicleImageSplashFilePath, vehicleImageThumbnailFilePath,
    vehicleImageMiniFilePath, mainVehicleImage, active)
  VALUES (%s, %s, %s, %s, %s, %s, 1);  
  END TRANSACTION;
--EndLoop here

Jika Anda bisa, cobalah untuk mengubah faktor pengisian indeks menjadi 95% , dan uji untuk melihat apakah Anda mendapatkan lebih sedikit deadlock.

Tes yang lebih ekstrem adalah menghapus indeks itu sepenuhnya saat INSERTing, lalu membuatnya kembali ketika selesai.

Oreo
sumber
Apakah Anda memiliki wawasan tentang Mengapa seluruh halaman akan dikunci Vs hanya baris / s yang saya coba masukkan?
Brian Sizemore
1
Juga, saya akan sedikit memperbaiki kode saya dan mengurangi waktu transaksi. Saya percaya Anda benar bahwa itu harus membuat perbedaan yang signifikan.
Brian Sizemore
Tidak yakin bagaimana internal MySQL bekerja, tetapi jawaban ini menjelaskannya untuk MS SQL. Beberapa tips MySQL yang bagus dalam Manual MySQL juga.
Oreo
1
MySQL tidak memberikan kontrol atas fillfactor.
Rick James
2
Setelah refactoring kode saya untuk mengantri sisipan saya dan pernyataan pembaruan dan menjalankannya sangat dekat, itu telah memecahkan masalah saya. Bukan hanya itu, tapi saya sudah bisa terus meningkatkan ini (kira-kira dua kali lipat jumlah proses paralel sebelumnya) dan masih berfungsi dengan benar. Oreo terima kasih!
Brian Sizemore
1

MySQL tidak hanya mengunci baris yang terpengaruh, tetapi juga baris indeks yang terpengaruh dan kesenjangan antara baris indeks (seperti dijelaskan di sini ). Karena kunci utama selalu diindeks dan Anda menggunakannya dalam pembaruan Anda, saya menduga bahwa beberapa transaksi mencoba untuk memperbarui beberapa baris setiap hasil dalam tumpang tindih kunci kesenjangan indeks yang pada gilirannya menciptakan kebuntuan.

Untuk mengatasi ini, saya juga merekomendasikan saran Oreos untuk menjaga transaksi sekecil mungkin. Jika baris yang diperbarui saling independen, Anda harus menggunakan transaksi terpisah untuk masing-masingnya.

Tepung
sumber