MySQL запрос медленно при выборе VARCHAR

0

У меня есть эта таблица:

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";

... он работает очень медленно.

Любые идеи?

  • 0
    Зависит от данных, если это не опечатка в ключевом слове (19000 против 19)
  • 0
    Да, это опечатка.
Теги:

3 ответа

1
Лучший ответ

Первый запрос может быть удовлетворен только одним индексом - нет необходимости читать базовую таблицу для получения значений в предложении Select. Второй оператор требует чтения базовой таблицы, потому что столбец URL-адреса не является частью индекса.

 UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`),

Строки в базовой таблице не находятся в том же физическом порядке, что и строки в индексе, поэтому чтение базовой таблицы может привести к значительным прерываниям диска.

Вы можете думать об этом как о некотором доказательстве оптимизации - при первом запросе избегайте извлечения диска, потому что движок достаточно умен, чтобы проконсультироваться с индексом для значений, запрошенных в предложении select; он уже прочитал этот индекс в ОЗУ для предложения where, поэтому он использует этот факт.

  • 0
    ... приводя к возможному решению добавления URL к ключу unique_ranking или создания отдельного индекса с соответствующими частями. Однако это приведет к более медленной вставке / обновлению / удалению.
  • 0
    Возможно, нет. Добавление столбца URL может привести к раздуванию индекса, что может привести к общей потере производительности, особенно если учитывать время обновления. Я вижу, вы обращаетесь к этому вопросу. Может быть, мы печатаем одновременно.
Показать ещё 2 комментария
1

Кроме ответа Тима. Индекс в 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

Я считаю, что он сможет полностью использовать этот индекс для чтения соответствующей части вашей таблицы.

  • 0
    Это звучит как первое, что я должен попробовать и посмотреть, что произойдет. Спасибо!
0

Я знаю это старое сообщение, но я испытывал ту же ситуацию, и я не нашел ответа. Это действительно происходит в 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;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню