Почему index сделал этот запрос медленнее? - PullRequest
4 голосов
/ 31 июля 2010

Таблица содержит 1 500 000 записей, 1 250 000 из них имеют поле = 'z'.
Мне нужно выбрать случайное поле, а не 'z'.

$random = mt_rand(1, 250000);  
$query = "SELECT field FROM table WHERE field != 'z' LIMIT $random, 1";

Работает нормально.

Тогда я решил оптимизировать его и проиндексировал field в таблице.

Результат был странным - он был медленнее ~ в 3 раза .Я проверил это.
Почему это медленнее?Разве такая индексация не должна сделать это быстрее?

мой ISAM

explain with index:  
id  select_type  table  type  possible_keys  key   key_len  ref  rows     Extra  
1   SIMPLE       table  range field          field 758      NULL 1139287  Using  

explain without index:  
id  select_type  table  type  possible_keys  key  key_len  ref  rows     Extra  
1   SIMPLE       table  ALL   NULL           NULL NULL     NULL 1484672  Using where

Ответы [ 2 ]

20 голосов
/ 31 июля 2010

Резюме

Проблема в том, что 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 - все накладные расходы на поиск по индексу, и все еще приходится искать результаты.

0 голосов
/ 31 июля 2010

В отсутствие предложения order by этот LIMIT $random, 1 начинается в каком-то неопределенном месте.

И, согласно вашему объяснению, индекс даже не используется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...