X

SELECT COUNT и индексы

Сегодня работал с большой таблицей. И столкнулся с очень интересной ситуацией, а именно непонятной мне скоростью выборки.
Может для кого-то это станет сразу понятным, но для меня это было не очевидно..

Итак таблица:

CREATE TABLE IF NOT EXISTS `posts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `url` varchar(255) NOT NULL,
  `cat` varchar(255) NOT NULL,
  `hash` char(32) NOT NULL,
  `title` varchar(255) NOT NULL,
  `text` text NOT NULL,
  `lvl` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `url_hash` (`url`),
  KEY `catpath` (`cat`),
  KEY `lvl` (`lvl`),
  KEY `lvlhash` (`cat`,`lvl`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;

Запрос:

SELECT COUNT( `id` ) FROM `posts` WHERE `lvl` =1

Скорость выполнения: 387 секунд

Explain:

select_type: SIMPLE
table: posts
type: ref
possible_keys:lvl
key: lvl
key_len: 1
ref:const     
rows: 441860
Extra:

Запрос:

SELECT COUNT( `lvl` ) FROM `posts` WHERE `lvl` =1

Скорость выполнения: 0,141 секунда

Explain:

select_type: SIMPLE
table: posts
type: ref
possible_keys:lvl
key: lvl
key_len: 1
ref:const     
rows: 441860
Extra: Using index

Мой вывод:
При эксплейне, mysql делает различия в столбце Extra. Первый раз он пустой, т.е. ничего нам не говорят, второй раз написано Using index. Смотрим в мануале, там написано:

Using index - Для извлечения данных из столбца используется только информация дерева индексов; при этом нет необходимости производить собственно чтение записи. Это применимо для случаев, когда все используемые столбцы таблицы являются частью одного индекса.

Теперь все становится ясно. Итак в первом запросе, для того чтобы подсчитать id, нам нужно все равно пройтись по всем записям, не обращая внимание на индекс, а для этого их нужно поднять из БД. Т.к. у нас есть BLOB поля, то каждая запись и поднимается долго. Соответственно, те 380 секунд, это я думаю именно отсюда. Во втором же случае, мы вообще не используем поднятие данных, и читаем только индекс, отсюда скорость.

Конечно правильно, было бы отделить текстовые поля от полей, которые можно проиндексировать, но когда структуру придумал не ты, все становится сложнее. Самый оптимальный вариант, это использовать для поиска второй вариант, а именно делать выборку по полю которое проиндексировано. Так же, думаю можно получить тот же результат, если сделать составной индекс id+lvl. Но это уже модификация базы.

Категории: MySQL Базы данных