Я запускаю EXPLAIN
:
mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Индексы в моей таблице:
mysql> show index from employees;
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | subsidiary_id | A | 6 | NULL | NULL | | BTREE | | |
| employees | 0 | PRIMARY | 2 | employee_id | A | 10031 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_last_name | 1 | last_name | A | 10031 | 700 | NULL | | BTREE | | |
| employees | 1 | date_of_birth | 1 | date_of_birth | A | 10031 | NULL | NULL | YES | BTREE | | |
| employees | 1 | date_of_birth | 2 | subsidiary_id | A | 10031 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.02 sec)
На last_name есть индекс, но оптимизатор его не использует.
Итак, я делаю:
mysql> explain select last_name from employees force index(idx_last_name) order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Но все же индекс не используется! Что я здесь не так делаю?
Связано ли это с тем, что индекс есть NON_UNIQUE
? Кстати, фамилияVARCHAR(1000)
Обновление запрошено @RolandoMySQLDBA
mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
+---------------+
| DistinctCount |
+---------------+
| 10000 |
+---------------+
1 row in set (0.05 sec)
mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
+----------+
| COUNT(1) |
+----------+
| 0 |
+----------+
1 row in set (0.15 sec)
SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
2)SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
. Каков результат каждого подсчета?SELECT COUNT(1) FullTableCount FROM employees;
и 2)SELECT * FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A LIMIT 10;
.Ответы:
ПРОБЛЕМА № 1
Посмотрите на запрос
Я не вижу значимого предложения WHERE, равно как и MySQL Query Optimizer. Нет стимула использовать индекс.
ПРОБЛЕМА № 2
Посмотрите на запрос
Вы дали ему индекс, но Quit Opitmizer вступил во владение. Я видел это поведение раньше ( Как заставить JOIN использовать определенный индекс в MySQL? )
Почему это должно случиться?
Без
WHERE
предложения Query Optimizer говорит следующее:WHERE
пункта?Оптимизатор запросов выбрал путь наименьшего сопротивления.
Вас ожидает небольшой шок, но здесь все сказано: знаете ли вы, что Оптимизатор запросов будет обрабатывать MyISAM совершенно по-другому?
Вы, наверное, говорите ХАХ ???? КАК ????
MyISAM хранит данные в
.MYD
файле и все индексы в.MYI
файле.Тот же запрос создаст другой план EXPLAIN, поскольку индекс находится в файле, отличном от данных. Почему ? Вот почему:
last_name
столбец) уже упорядочены в.MYI
last_name
из индекса.Как можно быть в этом уверенным? Я проверил эту рабочую теорию о том, как использование другого хранилища приведет к созданию другого плана EXPLAIN (иногда лучшего): должен ли индекс охватывать все выбранные столбцы, чтобы его можно было использовать для ORDER BY?
источник
На самом деле проблема здесь в том, что это выглядит как префиксный индекс. Я не вижу определения таблицы в вопросе, но
sub_part
= 700? Вы не проиндексировали весь столбец, поэтому индекс не может использоваться для сортировки и также бесполезен в качестве индекса покрытия. Его можно использовать только для поиска строк, которые «могут» соответствоватьWHERE
а серверному уровню (над механизмом хранения) придется дополнительно фильтровать сопоставленные строки. Вам действительно нужно 1000 символов для фамилии?Обновление, чтобы проиллюстрировать: у меня есть тестовая таблица таблиц, содержащая более 500 строк, каждая с именем домена веб-сайта в столбце
domain_name VARCHAR(254) NOT NULL
и без индексов.С индексированным полным столбцом запрос использует индекс:
Итак, теперь я отброшу этот индекс и просто индексирую первые 200 символов имени_домена.
Вуаля.
Также обратите внимание, что индекс длиной 200 символов длиннее самого длинного значения в столбце ...
... но это не имеет никакого значения. Индекс, объявленный с длиной префикса, может использоваться только для поиска, но не для сортировки и не в качестве индекса покрытия, поскольку по определению он не содержит полного значения столбца.
Кроме того, вышеупомянутые запросы были выполнены для таблицы InnoDB, но их выполнение в таблице MyISAM дает практически идентичные результаты. Только разница в данном случае является то , что InnoDB рассчитывать на
rows
немного смещено (541) , тогда как MyISAM показывает точное число строк (563) , который является нормальным поведением , так как два двигателя для хранения ручки индекса погружения очень по- разному.Я по-прежнему утверждаю, что столбец last_name, вероятно, больше, чем необходимо, но все же можно проиндексировать весь столбец, если вы используете InnoDB и работаете с MySQL 5.5 или 5.6:
источник
varchar(1000)
но он превышает максимально допустимый для индекса, который составляет ~ 750EXPLAIN SELECT ...
, а такжеSHOW CREATE TABLE ...
иSELECT @@VERSION;
так как изменения в оптимизаторе в разных версиях могут иметь значение.Я ответил об этом, потому что комментарий не будет поддерживать форматирование, и администратор RolandoMySQL рассказал о gen_clust_index и innodb. И это очень важно для таблицы на основе innodb. Это идет дальше, чем обычные знания DBA, потому что вам нужно уметь анализировать код на C ..
Вы должны ВСЕГДА делать ПЕРВИЧНЫЙ КЛЮЧ или УНИКАЛЬНЫЙ КЛЮЧ, если вы используете Innodb. Если вы не используете innodb, вы будете использовать собственный сгенерированный ROW_ID, который может принести вам больше вреда, чем пользы.
Я постараюсь объяснить это легко, потому что доказательство основано на C-коде.
Первая проблема
mutex_enter (& (dict_sys-> мьютекс));
Эта строка гарантирует, что только один поток может одновременно обращаться к dict_sys-> mutex. Что если уже значение было взаимно изменено ... да, поток должен ждать, чтобы вы получили что-то вроде хорошей случайной функции, такой как блокировка потока, или если у вас есть больше таблиц без вашего собственного PRIMARY KEY или UNIQUE KEY, тогда у вас будет хорошая функция с innodb ' блокировка таблицы ' - это не причина, по которой MyISAM был заменен на InnoDB из-за замечательной функции, называемой блокировкой на основе записей / строк.
Вторая проблема
(0 == (id% DICT_HDR_ROW_ID_WRITE_MARGIN))
Вычисления по модулю (%) медленные, не очень хорошие, если вы выполняете пакетную вставку, потому что их нужно каждый раз пересчитывать ... и поскольку DICT_HDR_ROW_ID_WRITE_MARGIN (значение 256) представляет собой степень двойки, это можно сделать намного быстрее ..
(0 == (id & (DICT_HDR_ROW_ID_WRITE_MARGIN - 1)))
Примечание: если компилятор C настроен для оптимизации и является хорошим оптимизатором, оптимизатор C исправит «тяжелый» код в более легкой версии
Девиз истории - всегда создавайте свой ПЕРВИЧНЫЙ КЛЮЧ или убедитесь, что у вас есть УНИКАЛЬНЫЙ индекс, когда вы создаете таблицу с самого начала
источник
UNIQUE
достаточно - необходимо также включить только столбцы, отличные от NULL, для уникального индекса, который будет повышен до PK.INSERT
тратится на эту функцию. Я подозреваю, это незначительно. Сравните усилия по смещению столбцов, выполняйте операции BTree, включая случайное разбиение на блоки, различные мьютексы в buffer_pool, изменения в буфере и т. Д.