2016-11-17 136 views
0

我一直有一個特定的更新查詢,這似乎應該是非常沒有問題的各種麻煩。我已經改變了名稱,但該表是:Innodb更新鎖定

CREATE TABLE `problem_table` (
    `id` int(11) NOT NULL, 
    `type` enum('TYPE1','TYPE2','TYPE3') NOT NULL, 
    `date` datetime NOT NULL, 
    `reference_id` int(11) DEFAULT NULL, 
    `value` varchar(255) NOT NULL, 
    `source` varchar(16) DEFAULT NULL, 
    `problem_field` datetime DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `type_idx` (`type`), 
    KEY `value_idx` (`value`(12)), 
    KEY `latest_id` (`reference_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 

和查詢造成的問題是:

UPDATE problem_table SET problem_field = 20000101 WHERE id = 6526153; 

值的problem_fieldid這裏就不似乎是很重要的。

這個單一的更新是在problem_table上的各種選擇查詢反覆死鎖,所以我的問題是 - 這個簡單的更新查詢取出了什麼鎖?我應該補充說,這兩個死鎖事務只包含單個查詢。

我已閱讀the docs但他們似乎並不特別全面。

供參考,在這裏是它和它的INNODB狀態報告死鎖的查詢,雖然這僅僅是一個例子了許多不同的查詢:

INSERT INTO temp 
SELECT 
    p.*, 
    DATE(p.date) 
FROM 
    problem_table p 
WHERE p.type IN ('TYPE1', 'TYPE2') 
    AND p.source = 'FOO'; 


------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
161107 0:00:00 
*** (1) TRANSACTION: 
TRANSACTION 3C7788A94, ACTIVE 69 sec starting index read 
mysql tables in use 1, locked 1 
LOCK WAIT 10 lock struct(s), heap size 1248, 7 row lock(s), undo log entries 6 
MySQL thread id 6558222, OS thread handle 0x7f44a606d700, query id 3110073624 164.55.80.105 sym_dbuser Updating 
-- user=XXX progname=XXX host=XXX pid=XXX ldsn=XXX 
-- DBI::db=HASH(0x1d15ecb0) 
UPDATE problem_table SET problem_field = 'XXXX-XX-XX XX:XX:XX'WHERE id = 'XXXXX' 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 1069083 page no 313 n bits 280 index `PRIMARY` of table `XXX`.`problem_table` trx id 3C7788A94 lock_mode X locks rec but not gap waiting 
*** (2) TRANSACTION: 
TRANSACTION 3C766F450, ACTIVE 831 sec fetching rows, thread declared inside InnoDB 39 
mysql tables in use 2, locked 2 
47612 lock struct(s), heap size 5339576, 9395927 row lock(s), undo log entries 9194153 
MySQL thread id 6558799, OS thread handle 0x7f4203cb6700, query id 3108758081 172.29.1.16 XXX Sending data 
-- user=XXX progname=XXX host=XXX pid=XXX [email protected] 

INSERT INTO temp 
SELECT 
    p.*, 
    DATE(p.date) 
FROM 
    problem_table p 
WHERE p.type IN ('TYPE1', 'TYPE2') 
    AND p.source = 'FOO'; 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 1069083 page no 313 n bits 280 index `PRIMARY` of table `XXX`.`problem_table` trx id 3C766F450 lock mode S 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 1069083 page no 82008 n bits 280 index `PRIMARY` of table `XXX`.`problem_table` trx id 3C766F450 lock mode S waiting 
*** WE ROLL BACK TRANSACTION (2) 

編輯:

對於任何人的利益讀下來,我發現INNODB能夠檢測3個或更多事務的死鎖,但它只列出受害者和希望受害者鎖定在死鎖報告中的事務 - 其餘事務不是在那裏列出。

看到這一點,運行三個交易,如:

T(ransaction)1 take S lock on R(ecord) 1 
T2 take S lock on R2 
T2 take X lock on R1 (hangs waiting for T1) 
T3 take S lock on R3 
T3 take X lock on R2 (hangs waiting for T2) 
T1 take X lock on R3 (deadlock detected) 
+0

nope,它是無觸發的 – rbennett485

+0

它看起來像INSERT INTO臨時SELECT ... - 查詢鎖定problem_table。所以如果這個INSERT INTO查詢發生,你的更新查詢就會等待。根據行數量(超過9百萬個被鎖定),這可能會導致更新查詢失敗,因爲在有用的時間內無法獲得日誌。你有一個緩慢的查詢日誌,你能看到,這些插入查詢需要多長時間以及它們執行的頻率? – Seb

+0

@Seb我確實有一個緩慢的查詢日誌,但這些更新沒有出現在它中 - 它們只是出現死鎖,在這種情況下,其他事務總是受害者 – rbennett485

回答

0

要回答這個問題埋在中間有一個基本問題,通過

UPDATE problem_table SET problem_field = 20000101 WHERE id = 6526153; 

其中problem_table被定義爲在問題中取出鎖只是:

事實證明,事情並不像我之前所描述的那樣 - 工具1執行有問題的更新不會執行事務管理,因此所有更新通常都會形成自己的事務。

但是,在這種情況下,有一個工具2,它使用工具1,並將其所有操作包裝在自己的事務中。因此,這個單一更新畢竟是更大事務的一部分(包含同一表中另一行的更新)。

這使得它更清晰地解釋爲什麼會發生死鎖 - 正在實施的修復是按照主鍵的順序進行這兩個更新(在這種類型的死鎖here上有一個很好的帖子)。

我也會考慮@fancyPants建議的一些模式更改,雖然這需要更多的工作 - 我提供的示例相當簡單,表格設計總體上非常可怕。它可以肯定使用一些sprucing ...

1

這是因爲你沒有使用你的INSERT ... SELECT查詢的良好指標。

你有一個索引type,但這是(我猜)完全沒用。此列只能有3個不同的值。你的查詢甚至要求2中的3個。索引越好,選擇性越高。換句話說,這個距離越近,

SELECT COUNT(DISTINCT columnname)/COUNT(*) FROM yourtable; 

是1,最好是你的指數。

這裏使用索引太貴,因爲實際上它需要MySQL首先讀取索引,然後再讀取實際數據。因此,閱讀整個表格會更便宜。

對於您的情況,source的索引會更好。或者甚至是(source, type)的綜合指數。 (順序在這裏很重要,首先選擇性更強!)

+0

您能否闡明如何讓不同的索引避免死鎖? – rbennett485

+0

它會避免死鎖,因爲你的查詢根本不使用索引。因此InnoDB在整個​​表上使用行級鎖。查看最後一條語句[here](http://dev.mysql.com/doc/refman/5.7/en/insert-select.html)。你也可以在你的'show engine innodb status'輸出中看到你的查詢持有9395927行鎖。 – fancyPants

+0

謝謝你。我仍然不明白爲什麼整個表的行級鎖應該導致死鎖,它應該只是舉行更新查詢 - 你能夠解釋什麼更新查詢正在做的鎖定,允許一個發生死鎖?這是我真的在這個問題上得到的 – rbennett485