web-dev-qa-db-fra.com

Comment puis-je forcer MySQL à IGNORER TOUS les index?

J'ai lu des articles sur l'index FORCE, mais comment puis-je forcer MySQL à indexer IGNORE ALL?

J'ai essayé SELECT * FROM tbl IGNORE INDEX(*), mais je n'ai pas réussi.

Quant à savoir pourquoi j'ai (et d'autres) besoin de le faire: Par exemple, j'avais besoin de résumer les statistiques des référents par tld comme ceci:

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

... mais je dois toujours regarder quels index sont définis ou déterminer quel index sera utilisé via Explain. Il serait très pratique d'écrire simplement IGNORE INDEX ALL Sans s'en soucier.

Est-ce que quelqu'un connaît la syntaxe ou un hack? (Des dizaines de lignes via les tables de définition MySQL ne sont vraiment pas un raccourci).

Ajouté de discussion par chat :

Bechmark:

  • pas d'index = 148,5 secondes

  • avec index = 180 secondes et toujours en cours d'exécution avec l'envoi de données La baie SSD est si puissante que vous ne vous souciez presque pas du cache de données ...

Définition de référence:

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, le test avec index (pas USE INDEX () ou similaire) est toujours en cours d'exécution, 250 secondes, je viens de le tuer.

12
mvorisek

Il n'est absolument pas clair pourquoi vous le souhaitez, mais vous pouvez utiliser l'indice USE INDEX () pour dire à l'optimiseur de ne pas utiliser d'index. Depuis les documents MySQL: index hints

Il est syntaxiquement valide pour omettre index_list pour USE INDEX, ce qui signifie "n'utilisez aucun index". Omettre index_list pour FORCE INDEX ou IGNORE INDEX est une erreur de syntaxe.

Votre requête devient:

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 ;

Note complémentaire: l'expression complexe:

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

peut être simplifié de 4 appels de fonction à 1:

SUBSTRING_INDEX(domain_name, '.', -1)
24
ypercubeᵀᴹ

Vous pouvez également intégrer 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 vient de me demander

Rolando, l'optimiseur de MySQL est-il si stupide qu'une simple condition toujours vraie interdira l'utilisation des index?

Oui, mais vous avez donné à MySQL une requête vraiment stupide. 1=1 reviendrait à l'index clusterisé. Nonobstant, il existe encore un autre moyen, mais cela nécessite d'être un peu malveillant pour l'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

Cela jettera chaque index sous le bus à coup sûr parce que la valeur de chaque ligne pour domain_name beaucoup à vérifier. Si domain_name est indexé, vous devez choisir une colonne pour le WHERE column_name=column_name qui n'est pas du tout indexé.

Je viens d'essayer cela sur une grande table dans un serveur de transfert

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)

Aucun index n'est choisi

2
RolandoMySQLDBA

En supposant que vous ayez ces deux index:

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

Peu importe alors ce que fait l'optimiseur; il doit balayer essentiellement une quantité identique de choses.

Cas 1: il effectue une analyse de table (ou utilise domain_id): il analysera les paires (id, nom), localisera tous les noms, effectuera SUBSTRING..LOCATE, GROUP BY et enfin ORDER BY. Le GROUP BY et le ORDER BY ont probablement besoin chacun d'une table tmp et d'un tri de fichiers. Vérifier EXPLAIN SELECT ... pour voir si c'est le cas.

Cas 2: il effectue un balayage d'index (de nom_domaine): cet index contient en fait des paires (nom, id) - car InnoDB place implicitement le PK à la fin de toute clé secondaire. Le reste du traitement est parallèle au cas 1.

Une chose pourrait être différente - la taille des deux BTrees. Faire SHOW TABLE STATUS LIKE domains_import pour voir la Data_length (pour le cas 1) et Index_length (pour le cas 2). Le plus grand BTree sera plus lent.

Une autre chose pourrait être différente - la mise en cache. Quelle est la valeur de innodb_buffer_pool_size? Combien de RAM avez-vous? Les données (ou index) peuvent-elles être contenues dans le pool de tampons. (Ou sera-t-il 37%, car il s'agit d'un scan de table/index? ) Si cela convient, exécutez la requête deux fois. Le temps seconde sera environ 10 fois plus rapide car il ne frappe pas le disque (mise en cache).

S'il s'agit d'une tâche unique, le SSD vous aidera. Si ce n'est pas le cas, et que vous pouvez mettre en cache la table entière, cela n'aidera pas après le chargement du buffer_pool.

0
Rick James