Как я могу заставить MySQL игнорировать все индексы?

12

Я прочитал статьи об FORCEиндексе, но как я могу заставить MySQL IGNORE ALLиндексировать?

Я пытался SELECT * FROM tbl IGNORE INDEX(*), но мне не удалось.

Что касается того, почему я (и другие) должны сделать это: например, мне нужно было суммировать статистику рефереров по tld следующим образом:

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
    IGNORE INDEX(domain_name)
GROUP BY tld
ORDER BY c desc
LIMIT 100

... но я всегда должен смотреть, какие индексы определены, или определить, какой индекс будет использоваться с помощью объяснения. Было бы очень удобно просто написать IGNORE INDEX ALLи просто пофиг.

Кто-нибудь знает синтаксис или хак? (Десятки строк через таблицы определений MySQL на самом деле не являются ярлыком).

Добавлено из чата :

Bechmark:

  • без индекса = 148,5 с

  • с индексом = 180 секунд и все еще работает с отправкой данных Массив SSD настолько мощен, что вам почти нет дела до кеша данных ...

Определение для эталона:

CREATE TABLE IF NOT EXISTS `domains_import` (
`domain_id` bigint(20) unsigned NOT NULL,
`domain_name` varchar(253) CHARACTER SET ascii COLLATE ascii_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `domains_import`
ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);

ALTER TABLE `domains_import`
MODIFY `domain_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT;

InnoDB, тест с индексом (без USE INDEX () или аналогичным) все еще выполняется, 250 секунд, я просто убил его.

mvorisek
источник

Ответы:

24

Совершенно непонятно, почему вы этого хотите, но вы можете использовать подсказку, USE INDEX ()чтобы сказать оптимизатору не использовать какой-либо индекс. Из документов MySQL: индексные подсказки

Это синтаксически действует до опускаем index_listдляUSE INDEX , что означает «не используют индексы.» Пропуск индекса FORCE INDEXили IGNORE INDEXдля синтаксической ошибки.

Ваш запрос становится:

SELECT count(*) AS c, 
       substring_index(domain_name, '.', -1) AS tld
FROM domains_import 
       USE INDEX ()        -- use no indexes
GROUP BY tld
ORDER BY c DESC
LIMIT 100 ;

Примечание: сложное выражение:

SUBSTRING(domain_name, LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2) 

можно упростить с 4 вызовов функций до 1:

SUBSTRING_INDEX(domain_name, '.', -1)
ypercubeᵀᴹ
источник
1
Это было полезно для меня, когда оптимизатор MySQL 5.7.10 изменил свой план запросов на худший при удалении некоторых из LEFT JOINних. `USE INDEX ()` заставил MySQL выполнить сканирование таблицы в таблице строк по 20 КБ и 1 к 1 JOINс вместо пересечения 500 строк между двумя индексами. Получил в 20 раз быстрее.
Xenos,
2

Вы также можете вставлять WHERE 1=1

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
WHERE 1=1
GROUP BY tld
ORDER BY c desc
LIMIT 100

ypercube только что спросил меня

Роландо, настолько ли глуп глуп оптимизатор MySQL, что простое всегда истинное условие запрещает использование индексов?

Да, но вы дали MySQL действительно тупой запрос. 1=1вернется к кластерному индексу. Несмотря на это, есть еще один способ, но он требует, чтобы оптимизатор был немного вредоносным.

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
WHERE domain_name = domain_name
GROUP BY tld
ORDER BY c desc
LIMIT 100

Это будет отбрасывать каждый индекс под шиной наверняка, потому что значение каждой строки для domain_nameмногих будет проверено. Если domain_nameиндексирован, вы должны выбрать столбец для того, WHERE column_name=column_nameкоторый вообще не индексируется.

Я только что попробовал это на большом столе в промежуточном сервере

mysql > explain SELECT COUNT(1) FROM VIDEO WHERE EMBEDDED_FLG=EMBEDDED_FLG;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | VIDEO | ALL  | NULL          | NULL | NULL    | NULL | 354327 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

Индексы не выбраны

RolandoMySQLDBA
источник
Роландо, настолько ли глуп глуп оптимизатор MySQL, что простое всегда истинное условие запрещает использование индексов?
ypercubeᵀᴹ
@ypercube да, но вы должны сделать запрос пустым, чтобы это произошло.
RolandoMySQLDBA
1
Эй, я сам проголосовал за ответ yercube. Мой ответ - это просто другой способ, объясняющий лазейку в Оптимизаторе.
RolandoMySQLDBA
1
Rolando, не соответствует действительности: будет использоваться индекс: SQLfiddle . Даже если вы сделаете что-то более сложное, например, WHERE id+0 = id*1индекс все равно будет использоваться, и появится дополнительный Using where.
ypercubeᵀᴹ
4
@PaulWhite это будет. (это глупо, но не так глупо;) И, возможно, поэтому в запросе Роалндо не используется индекс, столбец должен быть определен как NULL.
ypercubeᵀᴹ
0

Предполагая, что у вас есть эти два индекса:

ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);

Тогда не имеет значения, что делает оптимизатор; он должен сканировать по существу одинаковое количество материала.

Случай 1: он выполняет сканирование таблицы (или использует domain_id): он сканирует пары (id, name), находит все имена, выполняет SUBSTRING..LOCATE, GROUP BY и, наконец, ORDER BY. Каждому из GROUP BY и ORDER BY, вероятно, нужны таблица tmp и файловая сортировка. Проверьте, EXPLAIN SELECT ...если это так.

Случай 2: он выполняет сканирование индекса (имя_домена): этот индекс фактически содержит пары (имя, идентификатор) - потому что InnoDB неявно помещает PK в конец любого вторичного ключа. Остальные параллели обработки Случай 1.

Одна вещь может отличаться - размер двух BTrees. Есть , SHOW TABLE STATUS LIKE domains_importчтобы увидеть Data_length (для случая 1) и Index_length (для случая 2). Чем больше BTree будет медленнее.

Другое дело может быть иначе - кеширование. Какова стоимость innodb_buffer_pool_size? Сколько у вас оперативной памяти? Могут ли Данные (или Индекс) содержаться в пуле буферов. (Или это будет 37% из-за того, что это сканирование таблицы / индекса?) Если оно подходит, тогда выполните запрос дважды. Второй раз будет примерно в 10 раз , как быстро из - за не ударять диск (кэширование).

Если это разовая задача, SSD поможет. Если нет, и вы можете кэшировать всю таблицу, то это не поможет после загрузки buffer_pool.

Рик Джеймс
источник