¿Cómo puedo forzar a MySQL a IGNORAR TODOS los índices?

12

He leído artículos sobre FORCEíndices, pero ¿cómo puedo forzar a MySQL a IGNORE ALLindexar?

Lo intenté SELECT * FROM tbl IGNORE INDEX(*), pero no tuve éxito.

En cuanto a por qué yo (y otros) necesitamos hacer esto: por ejemplo, necesitaba resumir las estadísticas de los árbitros de la siguiente manera:

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

... pero siempre tengo que mirar qué índices están definidos o determinar qué índice se usará a través de Explain. Sería muy útil simplemente escribir IGNORE INDEX ALLy simplemente no importarle.

¿Alguien sabe la sintaxis o un truco? (Decenas de líneas a través de tablas de definición de MySQL realmente no son un atajo).

Agregado de la discusión del chat :

Bechmark:

  • sin índice = 148.5 segundos

  • con índice = 180 segundos y aún ejecutándose con el envío de datos La matriz SSD es tan poderosa que casi no le importa el caché de datos ...

Definición de referencia:

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, la prueba con índice (sin USE INDEX () o similar) todavía se está ejecutando, 250 segundos, simplemente la maté.

mvorisek
fuente

Respuestas:

24

No está claro por qué quiere esto, pero puede usar la sugerencia USE INDEX ()para decirle al optimizador que no use ningún índice. De documentos de MySQL: sugerencias de índice

Es sintácticamente válida para omitir index_listparaUSE INDEX , que significa “no utilizan índices.” Omitir index_list para FORCE INDEXo IGNORE INDEXes un error de sintaxis.

Su consulta se convierte en:

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 ;

Nota al margen: la expresión compleja:

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

se puede simplificar de 4 llamadas a funciones a 1:

SUBSTRING_INDEX(domain_name, '.', -1)
ypercubeᵀᴹ
fuente
1
Fue útil para mí cuando el optimizador MySQL 5.7.10 cambió su plan de consultas por uno peor al eliminar algunos de los LEFT JOINque tenía. `USE INDEX ()` hizo que MySQL escaneara una tabla en una tabla de 20K filas y 1 a 1 JOINs en lugar de cruzar 500 filas entre dos índices. Obtuve 20 veces más rápido.
Xenos
2

También podrías insertar 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 me acaba de preguntar

Rolando, ¿el optimizador de MySQL es tan tonto que una condición simple siempre verdadera prohibirá el uso de índices?

Sí, pero le has dado a MySQL una consulta realmente tonta. 1=1volvería al índice agrupado. No obstante, todavía hay otra forma, pero requiere ser un poco malicioso para el Optimizador.

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

Esto arrojará cada índice debajo del bus con seguridad porque se verificará el valor de cada fila para domain_namemucho. Si domain_nameestá indexado, debe elegir una columna para la WHERE column_name=column_nameque no está indexada en absoluto.

Acabo de probar esto en una mesa grande en un servidor de ensayo

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)

No se eligen índices

RolandoMySQLDBA
fuente
Rolando, ¿el optimizador de MySQL es tan tonto que una condición simple siempre verdadera prohibirá el uso de índices?
ypercubeᵀᴹ
@ypercube sí, pero tienes que anular la consulta lo suficiente para que eso suceda.
RolandoMySQLDBA
1
Oye, yo mismo voté por la respuesta de Yercube. Mi respuesta es solo otra forma y explica la escapatoria del Optimizador.
RolandoMySQLDBA
1
Rolando, no es cierto: se utilizará el índice: SQLfiddle . Incluso si hace algo más complicado, como WHERE id+0 = id*1el índice todavía se usará, y Using whereaparecerá un extra .
ypercubeᵀᴹ
44
@PaulWhite lo haría. (es tonto pero no tan tonto;) Y tal vez por eso la consulta de Roalndo no usa índice, la columna debe haberse definido como NULL.
ypercubeᵀᴹ
0

Suponiendo que tiene estos dos índices:

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

Entonces no importa lo que haga el optimizador; debe escanear esencialmente una cantidad idéntica de cosas.

Caso 1: realiza un escaneo de tabla (o usa domain_id): escaneará pares (id, nombre), ubicará todos los nombres, realizará la SUBSTRING..LOCATE, GROUP BY y finalmente ORDER BY. El GROUP BY y el ORDER BY probablemente necesitan una tabla tmp y una clasificación de archivos. Comprueba EXPLAIN SELECT ...si es así.

Caso 2: realiza una exploración de índice (de nombre_dominio): ese índice realmente contiene pares (nombre, id), porque InnoDB pone implícitamente la PK al final de cualquier clave secundaria. El resto del procesamiento es paralelo al caso 1.

Una cosa podría ser diferente: el tamaño de los dos BTrees. Haga SHOW TABLE STATUS LIKE domains_importpara ver Data_length (para el caso 1) y Index_length (para el caso 2). El BTree más grande será más lento.

Otra cosa podría ser diferente: el almacenamiento en caché. ¿Cuál es el valor de innodb_buffer_pool_size? ¿Cuánta RAM tienes? ¿Pueden los datos (o índice) estar contenidos dentro de la agrupación de almacenamiento intermedio. (¿O será el 37% debido a que se trata de una exploración de tabla / índice?) Si se ajusta, ejecute la consulta dos veces. La segunda vez será aproximadamente 10 veces más rápido debido a no golpear el disco (almacenamiento en caché).

Si se trata de una tarea única, SSD ayudará. Si no es así, y puede almacenar en caché toda la tabla, entonces no ayudará después de cargar buffer_pool.

Rick James
fuente