2011-08-31 98 views
2

我下表MySQL的InnoDB的死鎖問題有兩個相同的查詢(不同參數)

CREATE TABLE IF NOT EXISTS `task` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `job_id` int(10) unsigned NOT NULL COMMENT 'The id of the related job', 
    `server_id` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'job/task owner', 
    `jobtype_id` int(10) unsigned NOT NULL DEFAULT '0', 
    `node_id` int(10) unsigned NOT NULL COMMENT 'The id of the user currently executing this task', 
    `status` enum('QUEUED','EXECUTING','COMPLETED','CANCELED','TERMINATED','PAUSED','FAILED') NOT NULL COMMENT 'Current status of the task', 
    `last_updated` int(11) NOT NULL COMMENT 'When was the last status change of this task. Used in requeueing hung tasks', 
    `data_in` blob NOT NULL COMMENT 'An input data to the task. Sets when the task is created.', 
    `data_out` blob NOT NULL COMMENT 'An output data of the task. Sets upon task completion. Can be absent.', 
    `speed` bigint(20) unsigned NOT NULL DEFAULT '0', 
    `time_spent` int(11) NOT NULL DEFAULT '0', 
    `has_data_out` tinyint(1) NOT NULL COMMENT 'Shows if the task has any output data. Serves caching purposes. Used by Summarizers to quickly find out what tasks of the job yielded data.', 
    `comment` varchar(200) NOT NULL DEFAULT '', 
    PRIMARY KEY (`id`), 
    KEY `fk_task_job_id` (`job_id`), 
    KEY `index_has_data_out` (`has_data_out`), 
    KEY `index_last_updated` (`last_updated`), 
    KEY `index_status` (`status`), 
    KEY `fk_task_userid` (`node_id`), 
    KEY `jobtype_id` (`jobtype_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='This table holds all subjobs - tasks' AUTO_INCREMENT=1081595 ; 

-- 
-- Constraints for dumped tables 
-- 

-- 
-- Constraints for table `task` 
-- 
ALTER TABLE `task` 
    ADD CONSTRAINT `task_ibfk_5` FOREIGN KEY (`job_id`) REFERENCES `job` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, 
    ADD CONSTRAINT `task_ibfk_7` FOREIGN KEY (`jobtype_id`) REFERENCES `jobtype` (`id`), 
    ADD CONSTRAINT `task_ibfk_8` FOREIGN KEY (`node_id`) REFERENCES `node` (`id`); 

而下面的死鎖問題有:

------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
110831 14:23:56 
*** (1) TRANSACTION: 
TRANSACTION 102B4D2, ACTIVE 0 sec, OS thread id 5480 
mysql tables in use 2, locked 1 
LOCK WAIT 7 lock struct(s), heap size 1248, 4 row lock(s), undo log entries 3 
MySQL thread id 74315, query id 2364347 192.168.1.120 usr_sl3 Sending data 
select `usr_sl3`.`task`.`id` from `usr_sl3`.`task` where (`usr_sl3`.`task`.`node_id` = 103 and `usr_sl3`.`task`.`status` = 'EXECUTING') for update 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 38 page no 2303 n bits 576 index `index_status` of table `usr_sl3`.`task` trx id 102B4D2 lock_mode X locks rec but not gap waiting 
Record lock, heap no 471 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 1; hex 02; asc ;; 
1: len 4; hex 00107dac; asc } ;; 

*** (2) TRANSACTION: 
TRANSACTION 102B4D3, ACTIVE 0 sec, OS thread id 5692 starting index read, thread declared inside InnoDB 500 
mysql tables in use 2, locked 1 
7 lock struct(s), heap size 1248, 4 row lock(s), undo log entries 3 
MySQL thread id 74354, query id 2364348 192.168.1.120 usr_sl3 Sending data 
select `usr_sl3`.`task`.`id` from `usr_sl3`.`task` where (`usr_sl3`.`task`.`node_id` = 95 and `usr_sl3`.`task`.`status` = 'EXECUTING') for update 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 38 page no 2303 n bits 576 index `index_status` of table `usr_sl3`.`task` trx id 102B4D3 lock_mode X locks rec but not gap 
Record lock, heap no 471 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 1; hex 02; asc ;; 
1: len 4; hex 00107dac; asc } ;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 38 page no 2303 n bits 576 index `index_status` of table `usr_sl3`.`task` trx id 102B4D3 lock_mode X locks rec but not gap waiting 
Record lock, heap no 481 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 1; hex 02; asc ;; 
1: len 4; hex 00107dab; asc } ;; 

*** WE ROLL BACK TRANSACTION (2) 

能否請你幫我瞭解的機制這個僵局?

這兩個查詢是從不同的線程發出的。每個線程在查詢中都有自己的node_id。沒有兩個查詢具有相同的node_id。

我懷疑,我可以通過在字段(node_id,status)上創建一個複合索引來解決這種情況,但我認爲這不是一個好的解決方案。我需要了解問題的性質。

同一查詢上的這些死鎖定期發生,而不是一次或兩次。

解釋其對受影響的查詢給出了有趣的結果:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE task index_merge  index_status,fk_task_userid  index_status,fk_task_userid  1,4  NULL 1 Using intersect(index_status,fk_task_userid); Using where; Using index 

MySQL的版本是5.5。

此外,在死鎖的時刻,表包含沒有行匹配受影響的查詢的條件(例如選擇usr_sl3taskidusr_sl3task其中(usr_sl3tasknode_id = 95和usr_sl3taskstatus ='EXECUTING')更新 完全不產生任何行)。

在此先感謝。

回答

1

該查詢使用index_status索引而不是fk_task_userid(node_id上​​的索引)。這就是它用其他node_id鎖定記錄的原因。

您可以在您查詢說明,看看有多少記錄實際上被鎖定(在檢查的行)對,你需要怎樣他們多爲鎖定(返回行)

我懷疑,我能解決通過在字段(node_id,status)上創建複合索引來實現這種情況,但我認爲這不是一個好的解決方案。我需要了解問題的性質。

爲什麼?我覺得你的索引不是最優的...無論如何...在node_id上​​創建索引,狀態,它應該可以解決問題

+0

在問題中增加了EXPLAIN結果。查詢在此查詢中使用index_merge或索引交集。 –

+1

我使用兩個mysql客戶端執行測試,並且在使用index_merge時,與第一個索引匹配的所有行都被鎖定,因此需要複合索引以確保只有匹配的行被鎖定。 – ColinM

相關問題