Резюме
Проблема в том, что field
не является хорошим кандидатом для индексации из-за природы b-деревьев .
Пояснение
Предположим, у вас есть таблица с результатами бросков 500 000 монет, где бросок равен либо 1
(головы) или 0
(хвосты):
CREATE TABLE toss (
id int NOT NULL AUTO_INCREMENT,
result int NOT NULL DEFAULT '0',
PRIMARY KEY ( id )
)
select result, count(*) from toss group by result order by result;
+--------+----------+
| result | count(*) |
+--------+----------+
| 0 | 250290 |
| 1 | 249710 |
+--------+----------+
2 rows in set (0.40 sec)
Если вы хотите выбрать один жетон (случайным образом), где жетон был хвостом, то вам нужно поискать в вашей таблице, выбирая случайное начальное место.
select * from toss where result != 1 limit 123456, 1;
+--------+--------+
| id | result |
+--------+--------+
| 246700 | 0 |
+--------+--------+
1 row in set (0.06 sec)
explain select * from toss where result != 1 limit 123456, 1;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | toss | ALL | NULL | NULL | NULL | NULL | 500000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
Выобратите внимание, что вы в основном последовательно просматриваете все строки, чтобы найти совпадение.
Если вы создадите индекс в поле toss
, тогда ваш индекс будет содержать два значения, каждое из которых содержит примерно 250 000 записей.
create index foo on toss ( result );
Query OK, 500000 rows affected (2.48 sec)
Records: 500000 Duplicates: 0 Warnings: 0
select * from toss where result != 1 limit 123456, 1;
+--------+--------+
| id | result |
+--------+--------+
| 246700 | 0 |
+--------+--------+
1 row in set (0.25 sec)
explain select * from toss where result != 1 limit 123456, 1;
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | toss | range | foo | foo | 4 | NULL | 154565 | Using where |
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+
Теперь вы ищете меньше записей, но время поиска увеличилось с 0,06 до 0,25 секунды.Зачем?Поскольку последовательное сканирование индекса на самом деле менее эффективно, чем последовательное сканирование таблицы, для индексов с большим количеством строк для данного ключа.
Давайте посмотрим на индексы в этой таблице:
show index from toss;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| toss | 0 | PRIMARY | 1 | id | A | 500000 | NULL | NULL | | BTREE | |
| toss | 1 | foo | 1 | result | A | 2 | NULL | NULL | | BTREE | |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
ПЕРВИЧНЫЙ индекс - хороший индекс: 500 000 строк и 500 000 значений.Расположенный в BTREE, вы можете быстро идентифицировать одну строку на основе идентификатора.
Индекс foo является неверным индексом: существует 500 000 строк, но только 2 возможных значения.Это в значительной степени наихудший возможный случай для BTREE - все накладные расходы на поиск по индексу, и все еще приходится искать результаты.