Mysql當
當where
子句不匹配時並發更新查詢鎖定行
我的應用程序向 Slack 發送消息。在極少數情況下,我們可能需要撤回消息。
messageId
這些撤回是通過為迄今為止創建的每個作業排隊來處理的。在大約一周前的最後一次撤稿中,我注意到我們看到了大約 2k:
try restarting transaction (SQL: update
messageset
deleted_at= 2018-12-04 04:47:44 where (
slack_channel_id= xxxxxxxxxxxxxxxx and message_id = xxxxxxxxxxxxxxxx))"
似乎隊列作業(絕對同時發生)正在互相踩踏。我的期望是這個查詢只會在它需要的單個行上加鎖(我們在 和 上有一個複合唯一索引)
slack_server_id
,但似乎不止一項工作正在鎖定這條記錄。slack_channel_id``message_id
where
為什麼這些並發更新查詢會鎖定與子句不匹配的行?CREATE TABLE `messages` ( `slack_id` bigint(20) unsigned NOT NULL, `slack_channel_id` bigint(20) unsigned NOT NULL, `message_id` bigint(20) unsigned NOT NULL, `slack_message_id` bigint(20) unsigned DEFAULT NULL, `premium` tinyint(1) NOT NULL DEFAULT '0', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, `deleted_at` timestamp NULL DEFAULT NULL, UNIQUE KEY `slack_message_slack_id_channel_id_message_id_unique` (`slack_id`,`slack_channel_id `,`message_id`), KEY `slack_message_message_id_foreign` (`message_id`), CONSTRAINT `slack_message_slack_id_foreign` FOREIGN KEY (`slack_id`) REFERENCES `slacks` (`id`), CONSTRAINT `slack_message_message_id_foreign` FOREIGN KEY (`message_id`) REFERENCES `messages` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
更新
我添加了一個
id
自動增量主列,但它沒有解決事務問題。
學習索引的一課。
INDEX(a,b,c)
不是最優的WHERE a=1 AND c=2
。這是因為只會使用索引的最左邊的列;不能有任何跳過列(b
在本例中)。回到你的問題。
INDEX(slack_channel_id, message_id)
是需要的,並且
INDEX(`slack_id`,`slack_channel_id `,`message_id`)
一點用都沒有。
slack_id
擋路了。KEY `slack_message_message_id_foreign` (`message_id`),
將部分有用 - 即用於將搜尋範圍縮小到具有所需
message_id
.關於建構最佳索引的食譜。