Come posso forzare MySQL a IGNORARE TUTTI gli indici?


12

Ho letto articoli FORCEsull'indice, ma come posso forzare MySQL agli IGNORE ALLindici?

Ci ho provato SELECT * FROM tbl IGNORE INDEX(*), ma non ci sono riuscito.

Per quanto riguarda il motivo per cui io (e altri) abbiamo bisogno di fare questo: per esempio, dovevo riassumere le statistiche dei referenti in questo modo:

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

... ma devo sempre guardare quali indici sono definiti o determinare quale indice verrà utilizzato tramite Spiega. Sarebbe molto utile semplicemente scrivere IGNORE INDEX ALLe semplicemente non importa.

Qualcuno conosce la sintassi o un hack? (Decine di linee tramite le tabelle di definizione di MySQL non sono in realtà un collegamento).

Aggiunto dalla discussione in chat :

di benchmark:

  • nessun indice = 148,5 secondi

  • con indice = 180 secondi e ancora in esecuzione con l'invio di dati L'array SSD è così potente che quasi non ti interessa la cache dei dati ...

Definizione per benchmark:

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, il test con indice (nessun USE INDEX () o simile) è ancora in esecuzione, 250 secondi, l'ho appena ucciso.

Risposte:


24

Non è assolutamente chiaro il motivo per cui lo si desidera, ma è possibile utilizzare il suggerimento USE INDEX ()per dire all'ottimizzatore di non utilizzare alcun indice. Dai documenti MySQL: suggerimenti sull'indice

È sintatticamente valido per omettere index_listperUSE INDEX , che significa “non usano gli indici.” Omettere index_list per FORCE INDEXo IGNORE INDEXè un errore di sintassi.

La tua query diventa:

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 a margine: l'espressione complessa:

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

può essere semplificato da 4 chiamate di funzione a 1:

SUBSTRING_INDEX(domain_name, '.', -1)

1
È stato utile per me quando l'ottimizzatore MySQL 5.7.10 ha cambiato il suo piano di query per uno peggiore quando LEFT JOINho rimosso alcuni dei miei. `USE INDEX ()` ha reso MySQL eseguendo una scansione della tabella su una tabella di 20K righe e 1 a 1 JOINs invece di incrociare 500 righe tra due indici. Ottenuto 20 volte più veloce.
Xenos,

2

Potresti anche incorporare 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 mi ha appena chiesto

Rolando, l'ottimizzatore di MySQL è così stupido che una semplice condizione sempre vera proibirà l'uso degli indici?

Sì, ma hai dato a MySQL una domanda davvero stupida. 1=1ritornerebbe all'indice cluster. Ciononostante, esiste ancora un altro modo, ma richiede di essere un po 'dannoso per Optimizer.

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

Questo getterà sicuramente tutti gli indici sotto il bus perché il valore di ogni riga per domain_namemolto deve essere verificato. Se domain_nameè indicizzato, devi scegliere una colonna per quella WHERE column_name=column_nameche non è affatto indicizzata.

Ho appena provato questo su una grande tabella in un server di gestione temporanea

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)

Nessun indice selezionato


Rolando, l'ottimizzatore di MySQL è così stupido che una semplice condizione sempre vera proibirà l'uso degli indici?
ypercubeᵀᴹ

@ypercube sì, ma devi sminuire la query abbastanza perché ciò accada.
RolandoMySQLDBA l'

1
Ehi, ho votato io stesso la risposta di Yercube. La mia risposta è solo un altro modo e spiega la scappatoia dell'ottimizzatore.
RolandoMySQLDBA l'

1
Rolando, non vero: verrà utilizzato l'indice: SQLfiddle . Anche se rendi qualcosa di più complicato, come WHERE id+0 = id*1l'indice verrà comunque utilizzato e Using whereverrà visualizzato un extra .
ypercubeᵀᴹ

4
@PaulWhite sarebbe. (è stupido ma non così stupido;) Ed è forse per questo che la query di Roalndo non usa l'indice, la colonna deve essere stata definita come NULL.
ypercubeᵀᴹ

0

Supponendo di avere questi due indici:

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

Quindi non importa cosa fa l'ottimizzatore; deve scansionare essenzialmente una quantità identica di roba.

Caso 1: esegue una scansione della tabella (o utilizza domain_id): esegue la scansione delle coppie (id, name), localizzando tutti i nomi, eseguendo SUBSTRING..LOCATE, GROUP BY e infine ORDER BY. GROUP BY e ORDER BY probabilmente hanno bisogno di una tabella tmp e di un fileort. Controlla EXPLAIN SELECT ...se lo fa.

Caso 2: esegue una scansione dell'indice (di nome_dominio): quell'indice contiene effettivamente coppie (nome, ID) - perché InnoDB mette implicitamente il PK alla fine di qualsiasi chiave secondaria. Il resto dell'elaborazione è parallelo al caso 1.

Una cosa potrebbe essere diversa: la dimensione dei due BTree. Fai SHOW TABLE STATUS LIKE domains_importvedere Data_length (per il caso 1) e Index_length (per il caso 2). Il BTree più grande sarà più lento.

Un'altra cosa potrebbe essere diversa: la memorizzazione nella cache. Qual è il valore di innodb_buffer_pool_size? Quanta RAM hai? I dati (o l'indice) possono essere contenuti nel pool di buffer. (O sarà il 37% di esso, dato che si tratta di una scansione di tabella / indice?) Se si adatta, quindi eseguire la query due volte. La seconda volta sarà circa 10 volte più veloce a causa di non colpire il disco (memorizzazione nella cache).

Se si tratta di un'attività una tantum, SSD ti aiuterà. In caso contrario, e puoi memorizzare nella cache l'intera tabella, non sarà di aiuto dopo il caricamento di buffer_pool.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.