MySQL 為什麼更新 0 行很慢?
更新: where 子句是相同的。結果如下所述。
以下查詢在 0.1 秒內執行並返回 0 個結果。
SELECT id from content WHERE compliance_overridden = 1 AND compliance_overridden_date < DATE_SUB(NOW(), INTERVAL 30 DAY);
因此,我希望以下查詢會在同一時間內返回(因為它實際上不需要更新任何內容)。
UPDATE content SET compliance_overridden = 0 WHERE compliance_overridden = 1 AND compliance_overridden_date < DATE_SUB(NOW(), INTERVAL 30 DAY);
Using
EXPLAIN
表明第一個查詢沒有使用任何索引(key=None),但第二個查詢是(key=PRIMARY)。這讓我更加困惑。b) 為什麼一個查詢使用索引,而另一個不使用?也許 MySQL 先選擇行然後按主鍵更新?我曾嘗試分別執行這兩個查詢(選擇 ids,然後更新)並且兩者都很快。
c) 為什麼使用 PRIMARY 索引而不是無索引時第二個查詢會變慢?
**PS:**我嘗試添加
USE INDEX (PRIMARY)
到第一個查詢和IGNORE INDEX (PRIMARY)
第二個查詢,兩者都對查詢時間沒有影響。**PPS:**用於
SQL_NO_CACHE
第一個查詢也對查詢時間沒有影響。引擎: Aurora MySQL 5.6.10a
顯示創建表內容;
CREATE TABLE `content` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `content_key` char(40) NOT NULL, `body` mediumtext, `original_body` mediumtext, `meta` mediumblob, `status` enum('approved','deleted','draft','library','flagged','in progress','pending','published','publishing error','queued','ready','retracted','scheduled','removed','for review','reviewed','disabled') NOT NULL DEFAULT 'in progress', `is_flagged` int(11) NOT NULL DEFAULT '0', `extraction_status` enum('queued','processing','processed','processing error') DEFAULT NULL, `type` enum('internal','external','library') NOT NULL DEFAULT 'internal', `compliance_status` enum('ok','error','warning','unchecked','fail','overridden-ok','overridden-error','queued') DEFAULT 'unchecked' COMMENT 'Status when content is reviewed against guidelines', `queued_for_compliance` tinyint(4) NOT NULL DEFAULT '0', `queued_date` timestamp NULL DEFAULT NULL, `review_retries` int(11) NOT NULL, `review_notes` varchar(255) DEFAULT NULL, `needs_review` tinyint(1) DEFAULT '0' COMMENT 'Indicates that the content items needs the compliance reviewed to be checked', `pushed_for_review` enum('no','yes','error') NOT NULL DEFAULT 'no' COMMENT 'Flags if content is pushed to Minerva.', `risk_score` tinyint(1) DEFAULT '0', `keywords` text, `track` varchar(60) DEFAULT NULL, `purpose` enum('arbitrary','compliance_file_check','compliance_text_check','document','promotion','reply','retweet','site','site_post','soa','status_update','url') NOT NULL DEFAULT 'site' COMMENT 'Changed to an enum with applicable values. This will allow us to optimise queries on this column.', `purpose_note` text COMMENT 'Optional note explaining the purpose of this item', `format` varchar(20) DEFAULT NULL, `media` varchar(255) DEFAULT NULL, `media_alternate` varchar(255) DEFAULT NULL, `mime_type` varchar(255) DEFAULT NULL, `user_id` int(11) DEFAULT NULL, `tag_id` int(11) DEFAULT NULL, `email_template_id` int(11) DEFAULT NULL, `social_account_id` int(11) DEFAULT NULL, `target_social_account_id` int(11) DEFAULT NULL, `parent_id` int(11) DEFAULT NULL, `notes` text, `options` text, `compliance_notes` text, `notified` tinyint(1) NOT NULL COMMENT 'Indicates if legal have been notified of the content change', `publish_immediately` tinyint(1) DEFAULT '0', `score` int(11) DEFAULT NULL, `insights` text, `publish_date` timestamp NULL DEFAULT NULL, `date_published` timestamp NULL DEFAULT NULL, `external_id` varchar(40) DEFAULT NULL, `feed_id` int(11) DEFAULT NULL, `site_id` int(11) DEFAULT NULL, `client_id` int(11) DEFAULT NULL, `has_changed` tinyint(1) DEFAULT '0' COMMENT 'Indicates if the content has ever changed', `was_flagged` tinyint(4) NOT NULL DEFAULT '0', `last_flagged` timestamp NULL DEFAULT NULL COMMENT 'Date content was last flagged', `compliance_overridden` tinyint(4) NOT NULL DEFAULT '0', `compliance_overridden_date` timestamp NULL DEFAULT NULL, `was_risky` tinyint(1) DEFAULT '0' COMMENT 'Indicates if the content was ever risky', `last_risky` timestamp NULL DEFAULT NULL COMMENT 'Date content was last assigned risk', `reference_urls` enum('none','ok','unchecked','error') NOT NULL DEFAULT 'none', `screenshot_url` text, `rescan` enum('no','manual-queued','compliance-changed','compliance-unchanged','compliance-unchecked') NOT NULL DEFAULT 'no', `rescan_rules` varchar(50) DEFAULT NULL, `applicable_rules` text COMMENT 'Applicable rules for a review', `lsh_hash` char(70) DEFAULT NULL, `comment_count` int(11) NOT NULL DEFAULT '0', `modified` timestamp NULL DEFAULT NULL, `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `content_key` (`content_key`), KEY `fk_content_user` (`user_id`), KEY `fk_content_content` (`parent_id`), KEY `fk_content_email_template` (`email_template_id`), KEY `fk_content_target_social_account` (`social_account_id`), KEY `fk_content_feed` (`feed_id`), KEY `fk_content_site` (`site_id`), KEY `title` (`title`), KEY `status` (`status`), KEY `type` (`type`), KEY `needs_review` (`needs_review`), KEY `compliance_status` (`compliance_status`), KEY `risk_score` (`risk_score`), KEY `purpose` (`purpose`), KEY `extraction_status_idx` (`extraction_status`), KEY `client_id` (`client_id`), KEY `is_flagged_idx` (`is_flagged`), KEY `purpose_idx` (`purpose`), FULLTEXT KEY `content_index` (`title`,`body`), CONSTRAINT `content_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `client` (`id`), CONSTRAINT `fk_content_content` FOREIGN KEY (`parent_id`) REFERENCES `content` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_content_feed` FOREIGN KEY (`feed_id`) REFERENCES `feed` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_content_site` FOREIGN KEY (`site_id`) REFERENCES `site` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_content_social_account` FOREIGN KEY (`social_account_id`) REFERENCES `social_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_content_target_social_account` FOREIGN KEY (`social_account_id`) REFERENCES `social_account` (`id`), CONSTRAINT `fk_content_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ) ENGINE=InnoDB AUTO_INCREMENT=247236 DEFAULT CHARSET=utf8
在更新語句中,MySQL 必須為每一行計算“date_sub(created,interval 30 day)”,並將其與compliance_overridden_date 進行比較。在選擇中,它只需要計算一次 date_sub(now(), interval 30 day) 。您的 where 子句不允許 MySQL 使用索引來選擇要修改的行。而且由於必須讀取所有行,因此最好掃描表而不是在索引和數據之間跳過。我對解釋中的“PRIMARY”沒有任何解釋。你的猜測比我的好。
此查詢不以任何方式利用主鍵。檢查的行數(大約 160K 行)表明了這一點。所以你認為你
UPDATE
什麼都不做的假設是不正確的:它檢查了幾行。您的 SELECT 更快的原因很清楚。此表達式在查詢執行開始時計算一次:
DATE_SUB(NOW(), INTERVAL 30 DAY)
但是這個是每一行的電腦(這也是這個查詢不能使用索引的原因):
DATE_SUB(created, INTERVAL 30 DAY);
為什麼會
EXPAIN
顯示PRIMARY
?我不知道。但從技術上講,只要不使用二級索引,就始終使用主鍵——因為數據儲存在主鍵中,而不是按特定順序儲存。