2014-06-08 2 views
0

Изучаю данные индекса MySQL в книге «Высокая производительность MySQL», и я не могу понять одну вещь.Использование индексации индексов MySQL для сортировок

В книге говорится, (с.124 Использование индексных Сканы для Сорта)

MySQL имеет два способа произвести заказанные результатов: он может использовать FileSort, или он может сканировать индекс в порядке.

заказа результаты по индексу работает только тогда, когда порядок индекса является точно так же, как предложения ORDER BY и все столбцы сортируются в одинаковое направление (по возрастанию или по убыванию).

Предложение ORDER BY также имеет те же ограничения, что и запросы поиска: ему необходимо сформировать крайний левый префикс индекса. Во всех остальных случаях MySQL использует файлосад.

далее, авторы дают некоторые примеры использования MySQL Sakila в примере базы данных [http://dev.mysql.com/doc/sakila/en/][1]

Первый пример работает отлично:

прокат стол в стандартной базе данных Sakila образца имеет индекс по (rental_date, inventory_id, customer_id):

CREATE TABLE rental (
... 
PRIMARY KEY (rental_id), 
UNIQUE KEY rental_date (rental_date,inventory_id,customer_id), 
KEY idx_fk_inventory_id (inventory_id), 
KEY idx_fk_customer_id (customer_id), 
KEY idx_fk_staff_id (staff_id), 
... 
); 

MySQL использует индекс rental_date заказать следующий запрос, как вы можете видеть из-за отсутствия FileSort в EXPLAIN:

> mysql> EXPLAIN SELECT 
> rental_id, staff_id FROM sakila.rental 
> -> WHERE rental_date = '2005-05-25' 
> -> ORDER BY inventory_id, customer_id\G 
> *************************** 1. row *************************** 
> type: ref 
> possible_keys: rental_date 
> key: rental_date 
> rows: 1 
> Extra: Using where 

Это работает, даже если ORDER BY статьи не сам a левый префикс индекса, поскольку мы указали условие для первого столбца в индексе.

Важно отметить: они используют столбцы индекса в предложении where, но используют разные столбцы в запросе SELECT.

Второй пример приведены в коротком образом:

Следующий запрос также работает, потому что две колонны в ORDER BY являются крайний левый префикс индекса:

.. ,WHERE rent_date> '2005-05-25' ORDER BY rent_date, inventory_id;

Но здесь вы можете получить другой результат, а селекты содержания колонка:

Первой ситуацию, используются FileSort:

EXPLAIN 
SELECT `rental_id`, `staff_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

Типа: ALL possible_key: rental_date
ключа: NULL Дополнительно: использование где; используя FileSort

Вторая ситуация, используется индекс:

EXPLAIN 
SELECT `rental_id`, `rental_date`, `inventory_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

Тип: Диапазон possible_key: rental_date ключ: rental_date Дополнительно: Использование где; Использование индекса

Почему это работает в этой странной манере? Как показано выше, в первом примере использовалась сортировка индексов, даже если в предложении SELECT были включены разные столбцы с предложением WHERE.

ответ

0

Во втором запросе:

SELECT `rental_id`, `rental_date`, `inventory_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

MySql извлекает данные непосредственно из индекса, и не относится к таблице у всех.
Пожалуйста, обратите внимание на определение индекса и сравнить его с колоннами, на которые ссылается запрос:

UNIQUE KEY rental_date (rental_date,inventory_id,customer_id) 

Индекс содержит все colums, на которые ссылается запрос, кроме rental_id, однако rental_id является первичным ключом, и каждый индекс, кроме столбцы, заданные явно в его определении, всегда содержат также значения первичного ключа.
Это покрытие индекса для этого запроса, смотрите здесь: http://en.wikipedia.org/wiki/Index_%28database%29#Covering_index


Однако в первом запросе:

SELECT `rental_id`, `staff_id` FROM `sakila`.`rental` 
WHERE `rental_date` > '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 

есть staff_id столбец, который не хранится в индексе.
В этом случае MySql должен сначала получить записи индекса, соответствующие условию WHERE, тогда для каждой записи необходимо получить всю запись из таблицы, чтобы получить значение staff_id для этой записи.


Теперь запустите этот запрос к базе данных и исследовать ее результаты:

select count(*) As total, 
      sum(case when `rental_date` > '2005-05-25' then 1 else 0 end) As x1, 
      sum(case when `rental_date` = '2005-05-25' then 1 else 0 end) As x0 
from rental 
; 

В моей копии sakila database этот запрос вернул следующее:

+ ---------- + ------- + ------- + 
| total  | x1  | x0  | 
+ ---------- + ------- + ------- + 
| 16044  | 16036 | 0  | 
+ ---------- + ------- + ------- + 

Как вы видите, почти все записи в таблице - 99,9% - больше, чем 2005-05-25. В этом случае MySql решил не использовать индекс для извлечения строк из таблицы, но предпочитает загружать все содержимое таблицы в память и сортировать его здесь - таблица относительно невелика, содержит только записи 16k.
Однако если вернуть состояние, MySql предпочитает метод индекса Acces:

EXPLAIN 
SELECT `rental_id`, `staff_id` FROM `sakila`.`rental` 
WHERE `rental_date` < '2005-05-25' 
ORDER BY `rental_date`, `inventory_id`; 
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- + 
| id  | select_type  | table  | type  | possible_keys  | key  | key_len  | ref  | rows  | Extra  | 
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- + 
| 1  | SIMPLE   | rental  | range  | rental_date  | rental_date | 5   |   | 8   | Using index condition | 
+ ------- + ---------------- + ---------- + --------- + ------------------ + -------- + ------------ + -------- + --------- + ---------- + 

Почему он не использует индекс в первом случае? Поскольку извлечение записей из таблицы с использованием записей индекса обычно является самым дорогостоящим методом - действительно :)
Указатель хорош только в тех случаях, когда требуется извлечь очень небольшую часть таблицы - несколько процентов, может быть < 10%. Для каждой индексной записи, полученной из индекса, MySql должен получить одну запись с использованием первичного ключа - это случайный доступ к таблице, который в несколько раз медленнее, чем последовательный доступ.
Чтобы получить только одну запись из таблицы id, MySql должен получить всю страницу (блок) данных, содержащую несколько записей. Используя отсортированный индекс, мы должны получать записи - один за другим - из разных мест в таблице, поэтому одни и те же блоки данных извлекаются несколько раз, когда мы хотим получить 90% таблицы, используя индекс. В этом случае проще и дешевле читать их последовательно только один раз и сортировать их в памяти.

Смежные вопросы