У меня есть эта таблица:
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
Он имеет около 250 миллионов строк в производстве.
Когда я это сделаю:
select id,
rank
from search_engine_rankings
where keyword_id = 19000
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, поэтому он использует этот факт.
Кроме ответа Тима. Индекс в 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;