簡體   English   中英

MySQL InnoDB:鎖定外鍵的目的地

[英]MySQL InnoDB: locking the destination of foreign keys

當我添加引用另一個表的行(在事務中)時,MySQL 似乎鎖定了被引用的整行。 這可以防止更新目標表中應該能夠並發運行而不會出現任何問題的其他列。

簡化示例:

CREATE TABLE `t1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `num` int(11) DEFAULT NULL,
  UNIQUE KEY `id` (`id`)
);

CREATE TABLE `bar` (
  `foo` int(11) NOT NULL,
  KEY `foo` (`foo`),
  CONSTRAINT `bar_ibfk_1` FOREIGN KEY (`foo`) REFERENCES `t1` (`id`)
);
INSERT INTO `t1` VALUES (1,1),(2,0),(3,4);

任務一:

BEGIN;
insert into bar(foo) values(2);
-- now we ask task B to do some work for us

任務乙:

-- when triggered by Task A, tries to do this:
update t1 set num=num+1 where id=2;
-- does not complete because it waits for the lock

任何想法如何避免這種僵局? 任務 A 應該只讀鎖定它實際引用的單個值,因此任務 B 不能重新編號或刪除t1[id=2].id但可以自由更新該行。 是否可以說服 MySQL 這樣做?

將 t1 拆分為兩個鏈接表(一個供任務 A 引用,一個供任務 B 更新)將導致大量相當侵入性的重構。

加入任務不是一個選項,因為 B 的工作改變了全局 state,因此即使 A 失敗也必須能夠提交。

切換到 Postgres(它支持這個;我檢查過)不是一個容易執行的選項。

這是 MySQL 外鍵的行為,它坦率地說服許多項目避免使用外鍵約束,即使他們的數據庫邏輯上有外鍵引用。

您不能只鎖定一行中的一列。 如果引用它的子行上存在獨占鎖,則 InnoDB 有效地鎖定整行以防止更新或刪除。 這個想法是,雖然子行依賴於該父行並且正在進行插入/更新/刪除,但不應刪除父行或修改其鍵。 但是您不能只鎖定子行引用的鍵列。

最好的解決方案是立即完成並提交針對子表的事務。 您嘗試更新父行並超時(鎖等待超時默認為 50 秒)這一事實表明您讓事務運行的時間過長。

PS 你所描述的只是一個鎖等待。 這不是僵局。 死鎖是指兩個事務最終都被阻塞,等待對方釋放鎖,但都無法繼續,因為它們都在等待。 鎖等待是單向的。 死鎖是相互鎖等待的循環。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM