önsöz
Uygulamamız, DELETE
sorguları paralel olarak yürüten birkaç iş parçacığı çalıştırır . Sorgular izole edilmiş verileri etkiler, yani DELETE
aynı satırda ayrı ayrı iş parçacıklarından eşzamanlı oluşma olasılığı olmamalıdır . Bununla birlikte, belgelere göre MySQL DELETE
ifadeler için hem sonraki anahtarı hem de bazı boşlukları kilitleyen 'sonraki anahtar' kilidi kullanır . Bu şey ölü kilitlere yol açıyor ve bulduğumuz tek çözüm READ COMMITTED
izolasyon seviyesini kullanmak .
Sorun
Büyük tablolar DELETE
ile karmaşık ifadeler yürütülürken sorun ortaya çıkar JOIN
. Belirli bir durumda, yalnızca iki satıra sahip uyarıları içeren bir tablonuz vardır, ancak sorgunun, bazı belirli varlıklara ait tüm uyarıları iki ayrı INNER JOIN
ed tablosundan bırakması gerekir . Sorgu aşağıdaki gibidir:
DELETE pw
FROM proc_warnings pw
INNER JOIN day_position dp
ON dp.transaction_id = pw.transaction_id
INNER JOIN ivehicle_days vd
ON vd.id = dp.ivehicle_day_id
WHERE vd.ivehicle_id=? AND dp.dirty_data=1
Day_position tablosu yeterince büyük olduğunda (test durumumda 1448 satır var), READ COMMITTED
yalıtım moduyla bile herhangi bir işlem tüm proc_warnings
tabloyu engeller .
Sorun her zaman bu örnek verilerde yeniden oluşturulur - http://yadi.sk/d/QDuwBtpW1BxB9 hem MySQL 5.1 (5.1.59'da kontrol edildi) hem de MySQL 5.5 (MySQL 5.5.24'te kontrol edildi).
DÜZENLEME: Bağlantılı örnek veriler, sorgu tabloları için şema ve dizinleri de içerir ve burada kolaylık olması açısından çoğaltılmıştır:
CREATE TABLE `proc_warnings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`transaction_id` int(10) unsigned NOT NULL,
`warning` varchar(2048) NOT NULL,
PRIMARY KEY (`id`),
KEY `proc_warnings__transaction` (`transaction_id`)
);
CREATE TABLE `day_position` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`transaction_id` int(10) unsigned DEFAULT NULL,
`sort_index` int(11) DEFAULT NULL,
`ivehicle_day_id` int(10) unsigned DEFAULT NULL,
`dirty_data` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `day_position__trans` (`transaction_id`),
KEY `day_position__is` (`ivehicle_day_id`,`sort_index`),
KEY `day_position__id` (`ivehicle_day_id`,`dirty_data`)
) ;
CREATE TABLE `ivehicle_days` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`d` date DEFAULT NULL,
`sort_index` int(11) DEFAULT NULL,
`ivehicle_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ivehicle_days__is` (`ivehicle_id`,`sort_index`),
KEY `ivehicle_days__d` (`d`)
);
İşlem başına sorgulama aşağıdaki gibidir:
İşlem 1
set transaction isolation level read committed; set autocommit=0; begin; DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=2 AND dp.dirty_data=1;
İşlem 2
set transaction isolation level read committed; set autocommit=0; begin; DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=13 AND dp.dirty_data=1;
Bunlardan biri her zaman 'Kilit bekleme zaman aşımı aşıldı ...' hatasıyla başarısız oluyor. information_schema.innodb_trx
Aşağıdaki satırları içerir:
| trx_id | trx_state | trx_started | trx_requested_lock_id | trx_wait_started | trx_wait | trx_mysql_thread_id | trx_query |
| '1A2973A4' | 'LOCK WAIT' | '2012-12-12 20:03:25' | '1A2973A4:0:3172298:2' | '2012-12-12 20:03:25' | '2' | '3089' | 'DELETE pw FROM proc_warnings pw INNER JOIN day_position dp ON dp.transaction_id = pw.transaction_id INNER JOIN ivehicle_days vd ON vd.id = dp.ivehicle_day_id WHERE vd.ivehicle_id=13 AND dp.dirty_data=1' |
| '1A296F67' | 'RUNNING' | '2012-12-12 19:58:02' | NULL | NULL | '7' | '3087' | NULL |
information_schema.innodb_locks
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
| '1A2973A4:0:3172298:2' | '1A2973A4' | 'X' | 'RECORD' | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
| '1A296F67:0:3172298:2' | '1A296F67' | 'X' | 'RECORD' | '`deadlock_test`.`proc_warnings`' | '`PRIMARY`' | '0' | '3172298' | '2' | '53' |
Gördüğüm gibi her iki sorgu X
da birincil anahtar = 53 olan bir satırda özel bir kilit istiyor. Ancak, bunların hiçbiri proc_warnings
tablodan satırları silmemelidir. Endeksin neden kilitlendiğini anlamıyorum. Ayrıca, proc_warnings
tablo boş olduğunda veya day_position
tablo daha az sayıda satır (yani yüz satır) içerdiğinde dizin kilitlenmez .
EXPLAIN
Benzer SELECT
sorgu üzerinde daha fazla araştırma yapılması gerekti. Sorgu iyileştiricinin proc_warnings
tabloyu sorgulamak için dizin kullanmadığını ve neden tüm birincil anahtar dizinini engellediğini hayal edebildiğim tek nedenini gösterir.
Basitleştirilmiş dava
Birkaç kayıt içeren yalnızca iki tablo olduğunda da sorun daha basit bir durumda yeniden oluşturulabilir, ancak alt tablonun üst tablo ref sütununda bir dizini yoktur.
parent
Tablo oluştur
CREATE TABLE `parent` (
`id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
child
Tablo oluştur
CREATE TABLE `child` (
`id` int(10) unsigned NOT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
Tabloları doldur
INSERT INTO `parent` (id) VALUES (1), (2);
INSERT INTO `child` (id, parent_id) VALUES (1, NULL), (2, NULL);
İki paralel işlemle test edin:
İşlem 1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET AUTOCOMMIT=0; BEGIN; DELETE c FROM child c INNER JOIN parent p ON p.id = c.parent_id WHERE p.id = 1;
İşlem 2
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SET AUTOCOMMIT=0; BEGIN; DELETE c FROM child c INNER JOIN parent p ON p.id = c.parent_id WHERE p.id = 2;
Her iki durumda da ortak nokta, MySQL'in indeks kullanmamasıdır. Tüm masanın kilitlenmesinin nedeni olduğuna inanıyorum.
Bizim çözümümüz
Şimdilik görebildiğimiz tek çözüm, ipliğin temizlenmesini sağlamak için varsayılan kilit bekleme zaman aşımını 50 saniyeden 500 saniyeye çıkarmak. Sonra parmaklarınızı çapraz tutun.
Herhangi bir yardım takdir.
day_position
zaman aşımı sınırını 500 saniyeye çıkarmanız gerekecek kadar yavaş çalışmaya başladığında, tablo normalde kaç satır içerir? 2) Yalnızca örnek verilere sahip olduğunuzda çalışması ne kadar sürer?