[英]MySQL query slow when selecting VARCHAR
我有這張桌子:
CREATE TABLE `search_engine_rankings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`keyword_id` int(11) DEFAULT NULL,
`search_engine_id` int(11) DEFAULT NULL,
`total_results` int(11) DEFAULT NULL,
`rank` int(11) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
`indexed_at` date DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`),
KEY `search_engine_rankings_search_engine_id_fk` (`search_engine_id`),
CONSTRAINT `search_engine_rankings_keyword_id_fk` FOREIGN KEY (`keyword_id`) REFERENCES `keywords` (`id`) ON DELETE CASCADE,
CONSTRAINT `search_engine_rankings_search_engine_id_fk` FOREIGN KEY (`search_engine_id`) REFERENCES `search_engines` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=244454637 DEFAULT CHARSET=utf8
它在生產中有大約 2.5 億行。
當我做:
select id,
rank
from search_engine_rankings
where keyword_id = 19
and search_engine_id = 11
and indexed_at = "2010-12-03";
...它運行得非常快。
當我添加 url 列 (VARCHAR) 時:
select id,
rank,
url
from search_engine_rankings
where keyword_id = 19
and search_engine_id = 11
and indexed_at = "2010-12-03";
……它運行得很慢。
有任何想法嗎?
第一個查詢可以僅通過索引來滿足——無需讀取基表來獲取 Select 子句中的值。 第二條語句需要讀取基表,因為 URL 列不是索引的一部分。
UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`),
基表中的行與索引中的行的物理順序不同,因此基表的讀取可能涉及相當大的磁盤抖動。
您可以將其視為一種優化證明——在第一次查詢時避免了磁盤抖動,因為引擎足夠智能,可以查詢索引以獲取 select 子句中請求的值; 它已經為 where 子句將該索引讀入 RAM,因此它利用了這一事實。
除了蒂姆的回答。 Mysql 中的索引只能從左到右使用。 這意味着它只能在 WHERE 子句中使用索引的列,直到您使用它們為止。
目前,您的 UNIQUE 索引是keyword_id
、 search_engine_id
、 rank
、 indexed_at
。 這將能夠過濾列keyword_id
和search_engine_id
,仍然需要掃描剩余的行以過濾indexed_at
但是,如果您將其更改為: keyword_id
、 search_engine_id
、 indexed_at
、 rank
(只是順序)。 這將能夠過濾列keyword_id
、 search_engine_id
和indexed_at
我相信它將能夠充分利用該索引來讀取表的適當部分。
我知道這是一篇舊帖子,但我遇到了同樣的情況,但沒有找到答案。 這確實發生在 MySQL 中,當您有 varchar 列時,它需要花費大量時間進行處理。 我的查詢用了大約 20 秒來處理 1.7M 行,現在大約是 1.9 秒。
好的,首先,從這個查詢創建一個視圖:
CREATE VIEW view_one AS
select id,rank
from search_engine_rankings
where keyword_id = 19000
and search_engine_id = 11
and indexed_at = "2010-12-03";
其次,相同的查詢但具有內部連接:
select v.*, s.url
from view_one AS v
inner join search_engine_rankings s ON s.id=v.id;
TLDR:我通過在表上運行optimize
解決了這個問題。
我剛才也有同樣的經歷。 即使是查找主鍵並只選擇幾行也很慢。 測試了一下,我發現它不僅限於varchar列,選擇一個int也需要相當多的時間。
大致如下所示的查詢大約需要 3 秒:
select someint from mytable where id in (1234, 12345, 123456)
。
雖然大致如下所示的查詢花費了 <10 毫秒:
select count(*) from mytable where id in (1234, 12345, 123456)
。
這里批准的答案是只創建一個也跨越 someint 的索引,它會很快,因為 mysql 可以從索引中獲取它需要的所有信息,而不必觸及表。 這可能在某些設置中有效,但我認為這是一個愚蠢的解決方法 - 顯然是錯誤的,從表中獲取三行不應該花費三秒鍾,此外,大多數應用程序只是執行“select * from mytable”。 在應用程序端進行更改並不總是微不足道的。
optimize table
后,兩個查詢都需要 <10 毫秒。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.