Wie kann ich MySQL zwingen, ALLE Indizes zu ignorieren?


12

Ich habe Artikel über FORCEIndex gelesen , aber wie kann ich MySQL zu IGNORE ALLIndizes zwingen ?

Ich habe es versucht SELECT * FROM tbl IGNORE INDEX(*), aber ich war nicht erfolgreich.

Warum ich (und andere) dies tun müssen: Zum Beispiel musste ich die Referenzstatistiken wie folgt zusammenfassen:

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

... aber ich muss immer schauen, welche Indizes definiert sind oder bestimmen, welcher Index über Explain verwendet wird. Es wäre sehr praktisch, einfach zu schreiben IGNORE INDEX ALLund sich einfach nicht darum zu kümmern.

Kennt jemand die Syntax oder einen Hack? (Dutzende Zeilen über MySQL-Definitionstabellen sind wirklich keine Abkürzung).

Aus der Chat-Diskussion hinzugefügt :

Bechmark:

  • kein Index = 148,5 Sekunden

  • mit Index = 180 Sekunden und immer noch mit Daten senden Das SSD-Array ist so leistungsfähig, dass Sie sich fast nicht um den Datencache kümmern ...

Definition für 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, der Test mit Index (kein USE INDEX () oder ähnliches) läuft noch, 250 Sekunden, ich habe ihn gerade beendet.

Antworten:


24

Es ist absolut nicht klar, warum Sie dies möchten, aber Sie können den Hinweis verwenden USE INDEX (), um dem Optimierer mitzuteilen, dass er keinen Index verwenden soll. Aus MySQL-Dokumenten: Indexhinweise

Es ist syntaktisch gültig weglassen index_listfürUSE INDEX , was bedeutet „keine Indizes verwenden.“ Das Weglassen von index_list für FORCE INDEXoder IGNORE INDEXist ein Syntaxfehler.

Ihre Anfrage wird:

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 ;

Randnotiz: der komplexe Ausdruck:

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

kann von 4 Funktionsaufrufen auf 1 vereinfacht werden:

SUBSTRING_INDEX(domain_name, '.', -1)

1
Es war nützlich für mich, als das MySQL 5.7.10-Optimierungsprogramm seinen Abfrageplan für einen schlechtesten geändert hat, als einige der von LEFT JOINmir vorhandenen entfernt wurden. `USE INDEX ()` hat MySQL veranlasst, einen Tabellenscan für eine 20K-Zeilentabelle und 1-zu-1- JOINs durchzuführen, anstatt 500 Zeilen zwischen zwei Indizes zu kreuzen. 20x schneller geworden.
Xenos

2

Sie können auch einbetten 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 hat mich gerade gefragt

Rolando, ist der Optimierer von MySQL so dumm, dass eine einfache, immer zutreffende Bedingung die Verwendung von Indizes verbietet?

Ja, aber Sie haben MySQL eine wirklich dumme Frage gestellt. 1=1würde zum Clustered Index zurückkehren. Trotzdem gibt es noch einen anderen Weg, aber es erfordert, dass der Optimierer ein wenig bösartig ist.

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

Dies wird sicher jeden Index unter den Bus werfen, da der Wert jeder Zeile für domain_namevieles überprüft werden muss. Wenn domain_nameindiziert ist, müssen Sie eine Spalte für die auswählen WHERE column_name=column_name, die überhaupt nicht indiziert ist.

Ich habe es gerade an einem großen Tisch in einem Staging-Server versucht

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)

Es werden keine Indizes ausgewählt


Rolando, ist der Optimierer von MySQL so dumm, dass eine einfache, immer zutreffende Bedingung die Verwendung von Indizes verbietet?
Ypercubeᵀᴹ

@ypercube ja, aber Sie müssen die Abfrage so weit dummy-down, dass dies geschieht.
RolandoMySQLDBA

1
Hey, ich habe die Antwort von yercube selbst positiv bewertet. Meine Antwort ist nur ein anderer Weg und erklärt die Lücke des Optimierers.
RolandoMySQLDBA

1
Rolando, nicht wahr: Der Index wird verwendet: SQLfiddle . Selbst wenn Sie etwas komplizierter machen, wird WHERE id+0 = id*1der Index weiterhin verwendet und ein zusätzliches Using wherewird angezeigt.
Ypercubeᵀᴹ

4
@PaulWhite würde es. (Es ist dumm, aber nicht so dumm;) Und deshalb verwendet Roalndos Abfrage möglicherweise keinen Index. Die Spalte muss als NULL definiert worden sein.
Ypercubeᵀᴹ

0

Angenommen, Sie haben diese beiden Indizes:

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

Dann spielt es keine Rolle, was der Optimierer tut; Es muss im Wesentlichen eine identische Menge an Material scannen.

Fall 1: Es führt einen Tabellenscan durch (oder verwendet domain_id): Es scannt (ID, Name) Paare, sucht alle Namen, führt den SUBSTRING..LOCATE, GROUP BY und schließlich ORDER BY aus. Das GROUP BY und das ORDER BY benötigen wahrscheinlich jeweils eine tmp-Tabelle und einen Dateisort. Überprüfen Sie EXPLAIN SELECT ..., ob dies der Fall ist.

Fall 2: Es wird ein Index-Scan (von Domänenname) durchgeführt: Dieser Index enthält tatsächlich (Name, ID) Paare - da InnoDB die PK implizit an das Ende eines Sekundärschlüssels setzt. Der Rest der Verarbeitung entspricht Fall 1.

Eines könnte anders sein - die Größe der beiden BTrees. Hat SHOW TABLE STATUS LIKE domains_importdie Data_length (für Fall 1) und Index_length (für Fall 2) zu sehen. Der größere BTree wird langsamer sein.

Eine andere Sache könnte anders sein - Caching. Was ist der Wert von innodb_buffer_pool_size? Wie viel RAM hast du? Können die Daten (oder der Index) im Pufferpool enthalten sein? (Oder sind es 37% davon, da es sich um einen Tabellen- / Index-Scan handelt?) Wenn dies passt, führen Sie die Abfrage zweimal aus. Das zweite Mal ist ungefähr zehnmal so schnell, da die Festplatte nicht getroffen wird (Caching).

Wenn dies eine einmalige Aufgabe ist, hilft SSD. Wenn nicht, und Sie können die gesamte Tabelle zwischenspeichern, hilft dies nicht, nachdem der buffer_pool geladen wurde.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.