Detaillierte Erläuterung der MySQL-Indexauswahl und -Optimierung

Detaillierte Erläuterung der MySQL-Indexauswahl und -Optimierung

Indexmodell

Hash-Tabelle

  • Gilt für Szenarien mit ausschließlich gleichwertigen Abfragen. Der Standardindex der Speicher-Engine
  • InnoDB unterstützt adaptive Hash-Indizes, in die nicht eingegriffen werden kann. Die Engine entscheidet, ob sie erstellt werden.

Geordnetes Array: Die Leistung sowohl bei Abfragen mit gleichen Werten als auch bei Bereichsabfragen ist ausgezeichnet, das Einfügen und Löschen von Daten erfordert jedoch eine Datenverschiebung, die zu kostspielig ist. Daher gilt es nur für statische Speicher-Engines.

Binär ausgeglichener Baum: Das linke Kind jedes Knotens ist kleiner als der übergeordnete Knoten und der übergeordnete Knoten ist kleiner als das rechte Kind. Die zeitliche Komplexität beträgt O(log(N))

Ausgeglichener Baum mit mehreren Zweigen: Der Index wird nicht nur im Speicher gespeichert, sondern auch auf die Festplatte geschrieben. Um die Festplattenlesevorgänge für eine Abfrage zu minimieren, muss der Abfrageprozess auf so wenig Datenblöcke wie möglich zugreifen. Daher wird ein „N-ärer“ Baum verwendet.

B+Baum

B-Baum und B+Baum

B-Baum

B+Baum

InnoDB verwendet das B+-Baumindexmodell. Angenommen, wir haben eine Tabelle mit einer Primärschlüsselspalte „ID“, einem Feld „k“ in der Tabelle und einem Index für „k“, wie unten dargestellt:

  • Primärschlüsselindex: auch als Clusterindex bekannt. Der Blattknoten speichert die gesamte Datenzeile
  • Nicht-Primärschlüsselindex: Wird auch als Sekundärindex bezeichnet. Der Inhalt des Blattknotens ist der Wert des Primärschlüssels.

Vorsichtsmaßnahmen

  • Indizes basieren auf der geordneten Speicherung von Datenseiten. Daher können Datenseiten aufgeteilt (unzureichender Seitenspeicherplatz) oder zusammengeführt (geringe Seitenauslastung aufgrund von Datenlöschung) werden.
  • Das ungeordnete Einfügen von Daten führt zu Datenbewegungen und sogar zur Aufteilung von Datenseiten.
  • Je kleiner die Primärschlüssellänge, desto kleiner ist der Blattknoten des gemeinsamen Index und desto kleiner ist der vom gemeinsamen Index belegte Speicherplatz.
  • Je kleiner das Indexfeld, desto mehr Daten können in einer einzelnen Ebene gespeichert werden, was die Festplatten-E/A reduzieren kann.
// Nehmen wir eine Datenseite von 16 KB, eine Datenzeile von 1 KB, einen Indexzeiger von 6 Bytes und ein Indexfeld vom Typ Bigint (8 Bytes) an.

// Anzahl der Indizes K = 16*1024/(8+6) = 1170

// Anzahl der Datensätze in einem einzelnen Blattknoten N = 16/1 = 16

// Schicht 3 B+ Datensatznummer V = K*K*N = 21902400

MyISAM verwendet ebenfalls B+Tree-Indizes. Der Unterschied besteht darin, dass es nicht zwischen Primärschlüssel- und Nicht-Primärschlüssel-Indizes unterscheidet. Beide sind nicht gruppierte Indizes. Die Blattknoten speichern Zeiger auf Datendateien.

Indexauswahl

Der Zweck des Optimierers, der einen Index auswählt, besteht darin, einen optimalen Ausführungsplan zu finden und die Anweisung zu den niedrigsten Kosten auszuführen. In der Datenbank ist die Anzahl der gescannten Zeilen einer der Faktoren, die die Ausführungskosten beeinflussen. Weniger gescannte Zeilen bedeuten weniger Festplattenzugriffe und eine geringere CPU-Auslastung.

Natürlich ist die Anzahl der gescannten Zeilen nicht das einzige Beurteilungskriterium. Der Optimierer trifft auch eine umfassende Beurteilung basierend auf Faktoren wie der Verwendung temporärer Tabellen und der Sortierung.

So berechnen Sie die Anzahl der Scanzeilen

Je mehr unterschiedliche Werte ein Index hat, desto diskriminativer ist der Index. Die Anzahl der unterschiedlichen Werte in einem Index wird als Kardinalität bezeichnet.

-- Zeigen Sie die aktuelle Indexbasis an mysql> show index from test;
+-------+------------+----------+--------------+--------------+--------------+--------------+-------------+----------+---------+---------+---------+---------+---------+---------+---------+
| Tabelle | Nicht_eindeutig | Schlüsselname | Sequenz_im_Index | Spaltenname | Sortierung | Kardinalität | Unterteil | Gepackt | Null | Indextyp | Kommentar | Indexkommentar |
+-------+------------+----------+--------------+--------------+--------------+--------------+-------------+----------+---------+---------+---------+---------+---------+---------+---------+
| Test | 0 | PRIMARY | 1 | ID | A | 100256 | NULL | NULL | | BTREE | | |
| Test | 1 | index_a | 1 | a | A | 98199 | NULL | NULL | JA | BTREE | | |
+-------+------------+----------+--------------+--------------+--------------+--------------+-------------+----------+---------+---------+---------+---------+---------+---------+---------+

Aus Leistungssicht verwendet InnoDB Stichprobenstatistiken. Standardmäßig wählt es N Datenseiten aus, zählt die unterschiedlichen Werte auf diesen Seiten, ermittelt einen Durchschnittswert und multipliziert ihn dann mit der Anzahl der Seiten dieses Indexes, um die Kardinalität dieses Indexes zu erhalten. Daher weisen die beiden obigen Indizes nicht die gleiche Kardinalität auf.

Die Datentabelle wird kontinuierlich aktualisiert und die Indexstatistiken bleiben nicht unverändert. Wenn daher die Anzahl der geänderten Datenzeilen 1/M überschreitet (der Standardwert ist 10, wenn innodb_stats_persistent=on ist, und andernfalls 16), wird automatisch eine Neuindizierung der Statistiken ausgelöst.

mysql> Variablen wie „%innodb_stats_persistent%“ anzeigen;
+--------------------------------------+-------------+
| Variablenname | Wert |
+--------------------------------------+-------------+
-- Ob die Aktualisierung der Statistiken automatisch ausgelöst werden soll. Wenn die geänderten Daten 10 % überschreiten, werden die Statistiken neu berechnet | innodb_stats_auto_recalc | EIN |
-- Steuert, ob zum Löschen markierte Datensätze bei der Neuberechnung von Statistiken berücksichtigt werden | innodb_stats_include_delete_marked | OFF |
-- Statistische Methode für Nullwerte. Wenn die Variable auf nulls_equal gesetzt ist, werden alle NULL-Werte als gleich betrachtet | innodb_stats_method | nulls_equal | 
-- Ob die Aktualisierung der Statistiken beim Bearbeiten von Metadaten ausgelöst werden soll | innodb_stats_on_metadata | OFF |
--Ob Statistiken dauerhaft gespeichert werden | innodb_stats_persistent | EIN |
-- innodb_stats_persistent=on, Anzahl der Beispielseiten für die dauerhafte Statistikstichprobenerhebung | innodb_stats_persistent_sample_pages | 20 |
-- Veraltet, ersetzt durch innodb_stats_transient_sample_pages | innodb_stats_sample_pages | 8 |
-- Vorübergehende Beispielseitennummer | innodb_stats_transient_sample_pages | 8 |
+--------------------------------------+-------------+
  • Zusätzlich zur ungenauen statistischen Kardinalität aufgrund der Stichprobennahme kann MVCC auch zu ungenauen Kardinalitätsstatistiken führen. Beispielsweise wird Transaktion A vor Transaktion B gestartet und nicht festgeschrieben. Transaktion B löscht einige Daten. Beim wiederholbaren Lesen kann Transaktion A die gelöschten Daten immer noch abfragen. Derzeit gibt es mindestens zwei Versionen dieser Daten, von denen eine als gelöscht markiert ist.
  • Der Primärschlüssel wird direkt anhand der Zeilenanzahl in der Tabelle geschätzt. Der Optimierer verwendet den Wert von „show table status“ wie „t“ direkt für die Zeilenanzahl in der Tabelle.
  • Indexstatistiken manuell auslösen:
-- Indexinformationen neu berechnen mysql> analyze table t;

Auswirkung der Sortierung auf die Indexauswahl

-- Erstellen Sie eine Tabelle mysql> CREATE TABLE `t` (
`id` int(11) NICHT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
Primärschlüssel (`id`),
SCHLÜSSEL `a` (`a`),
SCHLÜSSEL `b` (`b`)
)ENGINE=InnoDB;

- Definieren Sie das Testdatenspeicherverfahren mysql> Trennzeichen;
PROZEDUR ERSTELLEN idata ()
BEGINNEN

Erklären Sie, dass ich INT;
Setze i = 1;
WÄHREND (i <= 100000) TUN
 EINFÜGEN IN t
WERTE
 (ich, ich, ich);
Setze i = i + 1;
ENDE
WÄHREND ;
ENDE;
Trennzeichen ;

-- Führen Sie die gespeicherte Prozedur aus und fügen Sie Testdaten ein mysql> CALL idata ();

- Zeigen Sie den Ausführungsplan an, indem Sie den Index auf Feld a verwenden. mysql> erklären Sie „select * from t“, wobei a zwischen 10000 und 20000 liegt;
+----+--------------+-------+-----------+---------------+-----+-----------+------+----------+-----------+---------------------------+
| ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra |
+----+--------------+-------+-----------+---------------+-----+-----------+------+----------+-----------+---------------------------+
| 1 | SIMPLE | t | Bereich | a | a | 5 | NULL | 10000 | Indexbedingung verwenden |
+----+--------------+-------+-----------+---------------+-----+-----------+------+----------+-----------+---------------------------+

-- Da Feld b sortiert werden muss, wird es geordnet, obwohl Index b mehr Zeilen scannen muss. Unter Berücksichtigung der Anzahl der zu scannenden Zeilen und der Sortierung wählt der Optimierer Index b, da er glaubt, dass die Kosten geringer sind. mysql> erklärt select * from t where (a zwischen 1 und 1000) und (b zwischen 50000 und 100000) order by b limit 1;
+----+--------------+-------+-----------+---------------+-----+------+------+------+------+------+------------------------------------+
| ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra |
+----+--------------+-------+-----------+---------------+-----+------+------+------+------+------+------------------------------------+
| 1 | SIMPLE | t | Bereich | a,b | b | 5 | NULL | 50128 | Indexbedingung verwenden; Where verwenden |
+----+--------------+-------+-----------+---------------+-----+------+------+------+------+------+------------------------------------+

-- Lösung 1: Verwenden Sie „Force Index“, um Index a zu erzwingen und die falsche Auswahl des Optimierers zu korrigieren. Dies wird nicht empfohlen (nicht universell, und die Anweisung muss geändert werden, wenn der Indexname geändert wird).
mysql> erklären Sie „select * from t force index(a), wobei (a zwischen 1 und 1000) und (b zwischen 50000 und 100000) sortieren nach b, Grenze 1;
+----+-----------+----------+-----------+---------------+-----+------+------+------+----------------------------------------------------+
| ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra |
+----+-----------+----------+-----------+---------------+-----+------+------+------+----------------------------------------------------+
| 1 | SIMPLE | t | Bereich | a | a | 5 | NULL | 999 | Indexbedingung verwenden; Where verwenden; Filesort verwenden |
+----+-----------+----------+-----------+---------------+-----+------+------+------+----------------------------------------------------+

-- Lösung 2: Weisen Sie MySQL an, den gewünschten Index zu verwenden und nach b, a zu sortieren. Der Optimierer muss die Kosten für die Sortierung von a berücksichtigen. mysql> erläutern Sie „select * from t“, wobei (a zwischen 1 und 1000) und (b zwischen 50000 und 100000) nach b, a, Limit 1 sortiert ist;
+----+-----------+----------+-----------+---------------+-----+------+------+------+----------------------------------------------------+
| ID | Auswahltyp | Tabelle | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | Extra |
+----+-----------+----------+-----------+---------------+-----+------+------+------+----------------------------------------------------+
| 1 | SIMPLE | t | Bereich | a,b | a | 5 | NULL | 999 | Indexbedingung verwenden; Where verwenden; Filesort verwenden |
+----+-----------+----------+-----------+---------------+-----+------+------+------+----------------------------------------------------+

-- Lösung 3: In einigen Szenarien können wir einen geeigneteren Index erstellen, um ihn dem Optimierer zur Auswahl bereitzustellen, oder den falsch verwendeten Index löschen ALTER TABLE `t`
DROP INDEX `a`,
DROP INDEX `b`,
INDEX HINZUFÜGEN `ab` (`a`,`b`);

Indexoptimierung

Indexselektivität

Indexselektivität = Kardinalität / Gesamtzahl der Zeilen

- Selektiver Index des Felds xxx in Tabelle t: select count(distinct xxx)/count(id) from t;

Unter der Selektivität eines Index versteht man das Verhältnis von eindeutigen Indexwerten (Kardinalität) zur Anzahl der Tabellendatensätze. Die Selektivität ist ein Indikator für die Screening-Fähigkeit des Index. Der Indexwertbereich liegt zwischen 0 und 1. Je höher die Selektivität, desto höher der Indexwert.

Bei Verwendung einer normalen Indexabfrage wird zuerst der normale Index geladen, der Primärschlüssel der tatsächlichen Zeile wird über den normalen Index abgefragt und dann wird der Primärschlüssel verwendet, um die entsprechende Zeile über den gruppierten Index abzufragen, um so alle Zeilen in einer Schleife abzufragen. Wenn Sie den gesamten Clusterindex direkt durchsuchen, müssen Sie nicht zwischen dem normalen Index und dem Clusterindex hin- und herwechseln. Verglichen mit dem Gesamtaufwand der beiden Vorgänge ist das Durchsuchen der gesamten Tabelle möglicherweise effizienter.

In der tatsächlichen Arbeit hängt es immer noch von der Geschäftssituation ab. Wenn die Datenverteilung ungleichmäßig ist, fragen die tatsächlichen Abfragebedingungen immer den Teil mit weniger Daten ab. Das Hinzufügen eines Index für die Spalte mit niedrigerer Indexauswahl kann ebenfalls einen guten Effekt haben.

Abdeckungsindex

Durch das Abdecken von Indizes kann die Anzahl der Baumsuchvorgänge verringert und die Abfrageleistung erheblich verbessert werden. Daher ist die Verwendung von Abdeckindizes eine gängige Methode zur Leistungsoptimierung.

-- Sie müssen nur den Wert der ID überprüfen, und der Wert der ID befindet sich bereits im k-Indexbaum, sodass Sie das Abfrageergebnis direkt bereitstellen können, ohne zur Tabelle zurückkehren zu müssen. Wählen Sie ID aus T aus, wobei k zwischen 3 und 5 liegt.

-- Feld V hinzufügen. V muss für jede Abfrage zurückgegeben werden. Sie können erwägen, k und v zu einem gemeinsamen Index zusammenzufassen. select ID,V from T, wobei k zwischen 3 und 5 liegt.

Prinzip des ganz linken Präfixes + Index-Pushdown

-- Erstellen Sie einen gemeinsamen Index für die drei Spalten „ID“, „Name“ und „Alter“. -- Erfüllen Sie das Prinzip des ganz linken Präfixes, und sowohl Name als auch Alter werden indiziert. Wählen Sie „* aus T“, wobei „Name“ = „xxx“ und „Alter“ = „12“ lautet.

-- Mysql optimiert und passt die Reihenfolge von Name und Alter automatisch an. Sowohl Name als auch Alter werden indiziert. select * from T where age=12 and name='xxx'

-- Wenn der Name das Prinzip des am weitesten links stehenden Präfixes erfüllt, wird der Index verwendet. MySQL 5.6 führt eine Pushdown-Optimierung für Indexbedingungen ein, d. h. die Datensätze, die age=12 nicht erfüllen, werden zuerst im Index herausgefiltert und dann an die Tabelle zurückgegeben. select * from T where name like 'xxx%' and age=12

-- Erfüllt nicht das Prinzip des ganz linken Präfixes, es wird kein Index verwendet. Wählen Sie * aus T aus, wobei der Name beispielsweise „%xxx%“ und das Alter 12 ist.

-- Erfüllt das Prinzip des ganz linken Präfixes. Der Name verwendet den Index select * from T, wobei name='xxx' ist.

-- Erfüllt nicht das Prinzip des ganz linken Präfixes und verwendet nicht den Index „select * from T where age=12“

Grundsätze für die Erstellung gemeinsamer Indizes:

  • Wenn durch die Anpassung der Reihenfolge der Pflegeaufwand für einen Index reduziert werden kann, muss dieser Reihenfolge häufig Priorität eingeräumt werden.
  • Platz: Priorisieren Sie das separate Erstellen von Indizes für kleine Felder, wie Name und Alter. Sie können einen gemeinsamen Index (Name, Alter) und einen Einzelfeldindex (Alter) erstellen.

Präfixindex

mysql> Tabelle SUser erstellen(
ID Bigint vorzeichenloser Primärschlüssel,
Name varchar(64),  
E-Mail varchar(64),
...
)Engine=innodb;

-- Das folgende Abfrageszenariomysql> select name from SUser where email='xxx';

-- Lösung 1: Volltextindex. Die Anzahl der Tabellenrückgaben wird durch die Datenmenge bestimmt, die die Bedingungen erfüllt. mysql> alter table SUser add index index1(email);

-- Lösung 2: Präfixindex. Die Anzahl der Tabellenrückgaben wird durch das Präfixübereinstimmungsergebnis bestimmt. mysql> alter table SUser add index index2(email(6));

Präfixindizes können Platz sparen, aber Sie müssen auf die Definition der Präfixlänge achten. Während Sie Platz sparen, sollten Sie die Abfragekosten nicht zu sehr erhöhen, d. h. die Anzahl der Überprüfungen der Tabellenrückgaben reduzieren.

Wie stelle ich die entsprechende Präfixlänge ein?

-- Stellen Sie ein akzeptables Unterscheidungsverlustverhältnis vor und wählen Sie die kleinste Präfixlänge, die die Bedingungen erfüllt. Wählen Sie count(distinct left(email,n))/count(distinct email) von SUser.

Was passiert, wenn die entsprechende Präfixlänge länger ist?

Wenn beispielsweise die ID-Kartennummer die Anforderungen zur Unterscheidung erfüllt, kann ein Präfixindex mit mehr als 12 Ziffern erforderlich sein. Der eingesparte Platz ist begrenzt und die Abfragekosten erhöhen sich. Daher ist die Verwendung eines Präfixindex nicht erforderlich. An diesem Punkt können wir den Einsatz der folgenden Methoden in Betracht ziehen:

Rückwärtsspeicherung

-- String-Umkehrabfrage während der Abfrage mysql> select field_list from t where id_card = reverse('input_id_card_string');

Verwenden von Hash-Feldern

- Erstellen Sie ein Integer-Feld zum Speichern des ID-Karten-Verifizierungscodes und erstellen Sie einen Index für dieses Feld mysql> alter table t add id_card_crc int unsigned, add index(id_card_crc);

-- Verwenden Sie das Hash-Feld, um nach Index zu suchen, und verwenden Sie dann die ursprüngliche Feldgenauigkeit, um MySQL zu filtern. Wählen Sie „field_list“ aus t aus, wobei id_card_crc=crc32('input_id_card_string') und id_card='input_id_card_string'

Nachteile der beiden oben genannten Methoden:

  • Bereichsabfragen werden nicht unterstützt
  • Die Verwendung von Hash-Feldern erfordert zusätzlichen Platz, daher wird ein neues Feld hinzugefügt
  • Beim Lesen und Schreiben sind zusätzliche Verarbeitungsvorgänge erforderlich, z. B. Reverse oder crc32

Welchen Einfluss hat der Präfixindex auf den Überdeckungsindex?

-- Wenn Sie einen Präfixindex verwenden, benötigen Sie keinen überdeckenden Index, um die Abfrageleistung zu optimieren. select id,email from SUser where email='xxx';

Eindeutiger Index

Es wird empfohlen, einen normalen Index zu verwenden. Der eindeutige Index kann den Änderungspuffer nicht verwenden und weist eine niedrige Speichertrefferrate auf.

Indexfehler

  • Führen Sie keine Spaltenoperationen durch, einschließlich der Verwendung von Funktionen, die die Reihenfolge der Indexwerte zerstören könnten.
  • Vermeiden Sie %xxx-Abfragen, die Indizes ungültig machen
  • Die or-Anweisung verwendet nicht gleichzeitig Indizes. Wenn nur eines der Abfragefelder auf der linken und rechten Seite der or-Anweisung ein Index ist, wird der Index ungültig.
  • Zusammengesetztes Index-ABC-Problem, Prinzip des ganz linken Präfixes
  • Implizite Typkonvertierung
  • Implizite Zeichenkodierungskonvertierung
  • Der Optimierer verlässt den Index. Faktoren wie Tabellenrücklauf und Sortierkosten wirken sich auf den Optimierer aus. Er wechselt zu anderen Indizes oder durchsucht den gesamten Index.

Zusammenfassen

Dies ist das Ende dieses Artikels über MySQL-Indexauswahl und -Optimierung. Weitere relevante Inhalte zur MySQL-Indexauswahl und -Optimierung finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Dieser Artikel zeigt Ihnen, wie Sie MySQL-Joins ohne Indizes optimieren
  • Detaillierte Erläuterung des MySQL-Indexprinzips und der Abfrageoptimierung
  • MySQL-Datenoptimierung - Mehrschichtiger Index
  • MySQL-Indexoptimierung: Detaillierte Einführung in die Paging-Erkundung
  • MySQL-Leistungsoptimierungs-Index-Pushdown
  • Optimierung der Abfragegeschwindigkeit von MySQL mit mehreren zehn Millionen Daten mithilfe von Indizes
  • MySQL-Optimierung und Indexanalyse

<<:  Webdesign-TabIndex-Element

>>:  Verwenden Sie Elasticsearch, um Indexdaten regelmäßig zu löschen

Artikel empfehlen

Beispiele für ES6-Schleifen und iterierbare Objekte

Dieser Artikel untersucht die ES6-for...of-Schlei...

Beispiel für die Installation und Bereitstellung von Docker unter Linux

Nachdem Sie den folgenden Artikel gelesen haben, ...

Einführung in den B-Tree-Einfügeprozess

Im vorherigen Artikel https://www.jb51.net/articl...

Tutorial zur Installation und Nutzungskonfiguration von Docker+nacos+seata1.3.0

Ich habe vorher einen Tag damit verbracht. Obwohl...

React-Internationalisierung react-i18next ausführliche Erklärung

Einführung react-i18next ist ein leistungsstarkes...

Kurze Analyse der geplanten MySQL-Sicherungsaufgaben

Einführung Um Datenverlust in einer Produktionsum...

js, um einen Boden-Scrolling-Effekt zu erzielen

In diesem Artikel wird jQuery verwendet, um den E...

So verwenden Sie js zur Kommunikation zwischen zwei HTML-Fenstern

Szenario: Wenn Seite A Seite B öffnet, muss Seite...

Vue-CLI - Aufzeichnung der Schritte zur mehrseitigen Verzeichnisverpackung

Seitenverzeichnisstruktur Beachten Sie, dass Sie ...