Heim > Datenbank > MySQL-Tutorial > MySQL Internals Optimizer

MySQL Internals Optimizer

黄舟
Freigeben: 2016-12-16 11:01:45
Original
1087 Leute haben es durchsucht

Der Optimierer

Dieser Artikel beschreibt, wie der MySQL-Abfrageoptimierer funktioniert. Der MySQL-Abfrageoptimierer ermittelt hauptsächlich die effizienteste Route (Routine, Richtung) für die ausgeführte Abfrage.

Eins. Quellcode und Konzepte


In diesem Teil werden die Schlüsselkonzepte und die Terminologie des Optimierers sowie deren Übereinstimmung mit dem MySQL-Quellcode erläutert.

1. Definition

Enge Definition: Der Optimierer ist eine Reihe von Routen, die das DBMS verwendet, um zu bestimmen, welchen Ausführungspfad es bei der Abfrage nehmen soll.

MySQL passt die Abfrageroute häufig an, daher müssen Sie die in diesem Artikel beschriebene Logik mit der im Quellcode vergleichen. Um den Vergleich zu erleichtern, werden hier Anmerkungen zu relevanten Dateien und Pfaden eingefügt, z. B. Quellcode/sql/sql_select.cc, Funktion „optimize_cond()“.

Wenn eine Abfrage in eine andere Abfrage konvertiert wird und das Ergebnis dasselbe ist, findet eine Anweisungskonvertierung statt. Die folgende Abfrage

SELECT ... WHERE 5 = a
wird in


SELECT ... WHERE a = 5
umgewandelt. Die offensichtlichste Anweisungstransformation Es gibt nur wenige Anweisungen und einige Anweisungen werden zur schnelleren Ausführung konvertiert.

2. Quellcode des Optimierers

Der folgende Pseudocode zeigt die logische Struktur der Funktion handle_select() in /sql/sql_select.cc. (Quellcode/sql/sql_select.cc verarbeitet SQL-Abfragen)

handle_select()
mysql_select()
JOIN::PRepare()
setup_fields()
JOIN::optimize ()                                                                                  🎜> /* wie vom Benutzer angegeben */
activate_straight_join()
best_access_path ()
/* Finden Sie einen (sub-)optimalen Plan unter allen oder einer Teilmenge* / / * Von allen öffentlichen Abfrageplänen, bei denen der Benutzer * /
/ * die Vollständigkeit der Suche steuert * /
Greedy_Search ()
iTed_search ()
Best_access_path ()
/ * Führen Sie eine umfassende Suche nach einem optimalen Plan durch * /
Find_best ()
Make_join_Select () / * ... to Here * /
Join :: Exec ()
Welche Funktion ruft welche Funktion auf, zum Beispiel ruft die Funktion handle_select() die Funktion mysql_select() auf und die Funktion mysql_select() ruft JOIN::prepare() auf , JOIN::optimize(), JOIN::exec() usw. Der erste Teil der mysql_select()-Funktion besteht darin, JOIN::prepare() aufzurufen. Diese Funktion wird für die Kontextanalyse, die Metadatenerstellung und einige Anweisungstransformationen verwendet. Die Abfrageoptimierungsfunktion JOIN::optimize() und alle ihre Unterrouten im Optimierungsprozess. Nachdem die Funktion JOIN::optimize() ausgeführt wurde, übernimmt JOIN::exec() die Ausführungsarbeit und schließt sie nach der Optimierungsentscheidung der Funktion JOIN::optimize() ab.

Obwohl das Wort JOIN erscheint, verarbeitet der Abfrageoptimierer tatsächlich alle Abfragetypen, nicht nur JOIN-Join-Abfragen.



Zwei. Top-Optimierungen

In diesem Abschnitt werden die wichtigsten vom Server durchgeführten Optimierungen erläutert.

1. Optimieren Sie die konstante Beziehung

Konstante äquivalente Übertragung

Die folgenden Ausdrücke werden einer Anweisungskonvertierung unterzogen:

WHERE Spalte1 = Spalte2 UND Spalte2 = 'x'
Dieser Ausdruck gilt, wie wir alle wissen, wenn A=B && B=C => Der vorherige Satz wird transformiert. Dann wird er zu:

WHERE Column1='x' AND Column2='x'


Genau dann, wenn der Operator ist Eine der folgenden Anweisungskonvertierungen erfolgt in der Bedingung Spalte1 Spalte2:

=, <, >, <=, >=, <>, <= >, LIKE

Hinweis: Die Transformation der gleichwertigen Übertragung ist nicht für BETWEEN geeignet. Es ist vielleicht auch nicht für LIKE geeignet, aber das ist eine Geschichte später.

Eine konstante äquivalente Übertragung erfolgt auch in einer Schleife, und die Ausgabe des vorherigen Schritts wird als Eingabe des nächsten Schritts verwendet.


Siehe /sql/sql_select.cc für Quellcode, Funktion „change_cond_ref_to_const()“. Oder /sql/sql_select.cc, Funktion propagate_cond_constants().

Toten Code beseitigen


Bedingungen, die immer TRUE sind, werden einer Anweisungstransformation unterzogen, wie zum Beispiel:

WHERE 0=0 AND columns1= ' y'
In diesem Fall wird die erste Bedingung eliminiert und die letzte Bedingung lautet:


column1='y'
Den Quellcode finden Sie unter /sql/ sql_select.cc, remove_eq_conds( ).

Die Bedingung, die immer FLASE ist, wird auch einer Anweisungstransformation unterzogen, wie zum Beispiel:

WHERE (0 = AND s1 = OR s1 = 7
Klammern und die Die ersten beiden Die erste Bedingung ist immer FLASE und die letzte lautet:


WHERE s1 = 7

Es gibt auch einige Fälle, in denen die WHERE-Anweisung darstellt eine unmögliche Bedingung. Der Abfrageoptimierer kann alle Anweisungen wie folgt eliminieren:

WHERE (0 = AND s1 = 5)
Da diese Bedingung niemals TRUE ist, wird Unmögliches WHERE in der EXPLAIN-Analyse angezeigt. Einfach ausgedrückt sagt MySQL, dass die WHERE-Bedingung optimiert wurde


Wenn ein Feld nicht NULL sein kann, eliminiert der Optimierer alle irrelevanten IS NULL-Bedingungen, also

WHERE not_null_column IS NOT NULL
Diese Bedingung ist immer FLASE; und


WHERE not_null_column IS NOT NULL
Diese Bedingung ist immer TRUE, daher sind die Bedingungen für diese Feldabfrage ebenfalls eliminiert . Dieses Urteil ist sehr subtil: In einem OUT JOIN-Outer-Join ist das Feld beispielsweise als NOT NULL definiert und enthält in diesem Sonderfall immer noch NULL-Werte >

Der Optimierer prüft nicht alle unmöglichen WHERE-Bedingungen, da es zu viele Möglichkeiten gibt. Zum Beispiel:

CREATE TABLE Table1 (column1 CHAR( 1));

...

SELECT * FROM Table1 WHERE columns1 = 'Popgo';
Der Optimierer eliminiert die Bedingungen für diese Abfrage nicht, auch wenn dies in der CREATE TABLE-Definitionsbedingung


Kombinierbarer konstanter Wert


Der folgende Ausdruck wird einer Anweisungskonvertierung unterzogen:

WHERE Spalte1 = 1 + 2

Abschließend:

WHERE Column1 = 3
Wie bereits erwähnt, kann der Optimierer solche Abfrageanweisungen einfach zusammenführen, was den Vorgang vereinfacht

Konstantwert und Konstantentabelle

Der MySQL-Konstantenwert bezieht sich manchmal nicht nur auf die wörtliche Bedeutung der SQL-Anweisung der Abfrage, sondern auch auf die Konstantentabelle (Konstantentabellen werden wie folgt definiert:

1). Tabelle ohne Datensatz oder mit einem Datensatz

2. Der Ausdruck der Tabelle wird durch die WHERE-Bedingung eingeschränkt und enthält die Ausdrucksform Column = „Konstante“ oder alle Felder des Primärschlüssels der Tabelle oder alle Felder eines beliebigen eindeutigen Schlüssels (das eindeutige Schlüsselfeld ist als NOT NULL definiert)

Beispielsweise enthält die Definition der Tabelle Table0:

... PRIMARY KEY (column1,column2)
Dann der Abfrageausdruck:


FROM Table0. .. WHERE Column1=5 AND Column2=7 ...

gibt konstante Tabellen zurück. Einfacher ausgedrückt: Wenn die Definition der Tabelle Table1 Folgendes enthält:


... unique_not_null_column INT NOT NULL UNIQUE

, dann lautet der Abfrageausdruck:

FROM Table1 . .. WHERE unique_not_null_column=5

gibt auch konstante Tabellen zurück.


Diese Regel bedeutet, dass eine konstante Tabelle höchstens einen Datensatzwert hat. MySQL prüft zunächst, ob es sich um eine konstante Tabelle (konstante Tabellen) handelt, und ermittelt diesen Wert. Auf diese Weise fügt MySQL diesen Wert in die Abfrageanweisung ein. Zum Beispiel:

SELECT Table1.unique_not_null_column, Table2.any_column

FROM Table1, Table2
WHERE Table1.unique_not_null_column = Table2.any_column

AND Table1.unique_not_null_column = 5;

MySQL When Wenn Sie diese Anweisung auswerten, werden Sie zunächst feststellen, dass gemäß der zweiten Definition konstanter Tabellen die Tabelle Table1 mit der Abfragebedingung unique_not_null_column eine konstante Tabelle (konstante Tabellen) ist und diesen Wert erhält.


Wenn der Wert fehlschlägt, das heißt, unique_not_null_column = kein Wert in Tabelle Tabelle1, das Ergebnis nach EXPLAIN:


Unmöglich, WO nach dem Lesen von const-Tabellen bemerkt wurde

Ein im Gegenteil, wenn der Wert erfolgreich abgerufen wurde, das heißt, unique_not_null_column = ist ein Datensatzwert in Tabelle1, konvertiert MySQL ihn in die folgende Anweisung:


SELECT 5, Table2.any_column

FROM Table1, Table2
WHERE 5 = Table2.any_column
AND 5 = 5;



Tatsächlich ist dies ein gutes Beispiel. Der Optimierer führt aufgrund der zuvor erwähnten konstanten äquivalenten Übergabe einige Anweisungstransformationen durch. Warum sollten wir außerdem zuerst die konstante Gleichwertübertragung beschreiben, weil sie erfolgt, bevor MySQL bestätigt, was konstante Tabellen sind? Die Reihenfolge der Optimierungsschritte unterscheidet sich manchmal.

Obwohl viele Abfragen keinen Verweis auf konstante Tabellen haben. Es sollte beachtet werden, dass sich das Wort „Konstante“, wenn es in Zukunft erwähnt wird, auf einen beliebigen Literalwert oder den Inhalt einer Konstantentabelle bezieht.

2. JOIN-Verbindung optimieren


In diesem Abschnitt werden verschiedene Methoden zur Optimierung von JOIN-Verbindungen erläutert. Hinweis: Die JOIN-Verbindung bezieht sich nicht nur auf den JOIN-Typ, sondern auch auf die Typen aller bedingten Abfragen. Manche Leute nennen es lieber Zugriffstyp.

Bestimmen Sie den JOIN-Join-Typ


Bei der Auswertung eines Abfragebedingungsausdrucks bestimmt MySQL, zu welchem ​​JOIN-Join-Typ er gehört.

Die folgenden JOIN-Typen werden in der Datei aufgezeichnet, sortiert vom Besten zum Schlechtesten:

System: Systemtyp der Konstantentabellen (Konstantentabellen)
const: Konstantentabellen (Konstantentabellen)
eq_ref: Der eindeutige oder Primärschlüsselindex der Gleichheitsbeziehung
ref: Der Index der Gleichheitsbeziehung, der Wert dieses Index darf nicht NULL sein
ref_or_null: Der Index der Gleichheitsbeziehung, der Wert von Dieser Index kann NULL sein
Bereich: Zugehörige Indizes wie BETWEEN, IN, >=, LIKE usw.
Index: Sequentielles Scan-Index
ALL: Sequentielles Scannen der gesamten Tabellendaten

Siehe /sql/ für den Quellcode sql_select.h, Enum join_type{}. Darüber hinaus gibt es einen kleinen Teil, der für den JOIN-Verbindungstyp der Unterabfrage nicht in der Datei aufgezeichnet wird.

Der Optimierer verwendet den Verbindungstyp JOIN, um einen treibenden Ausdruck wie folgt auszuwählen:

SELECT *
FROM Table1
WHERE indexed_column = AND unindexed_column = 6
Wenn indexed_column einen besseren JOIN-Verbindungstyp hat, ist es wahrscheinlicher, dass es zu einem treibenden Ausdruck wird. Sie werden auch auf verschiedene Ausnahmen stoßen, aber diese Beschreibung ist die erste einfache Optimierungsregel.


Was ist für den Fahrer am sinnvollsten? Für die folgende Abfrage gibt es zwei Ausführungspfade:


Schlechtester Ausführungsplan: Alle Zeilen der Lesetabelle scannen. Dies wird auch als sequentielles Scannen oder einfaches Tabellenscannen von Tabelle1 bezeichnet. Fragen Sie jede Zeile ab und prüfen Sie, ob die Werte von indexed_column und unindexed_column mit den Abfragebedingungen übereinstimmen.


Der beste Ausführungsplan: Datensätze mit indexed_column = value über den Index abrufen. Dies wird auch als indizierte Suche bezeichnet. Fragen Sie jede Zeile ab und prüfen Sie, ob der Wert der Spalte „unindexed_column“ mit den Abfragebedingungen übereinstimmt.


Indizierte Suchvorgänge erfordern normalerweise weniger Zugriffe als sequentielle Scans. Wenn die Tabelle, auf die zugegriffen wird, sehr groß und der Index eindeutig ist, sind die Tabellenzugriffe sehr gering. Aus diesem Grund sind Zugriffstabellen mit guten Ausführungsplänen besser und daher wird indexed_column häufig als Treiber verwendet.

Join- und Zugriffsmethoden

Bei der Einzeltabellensuche verursachen schlechte JOIN-Join-Ausführungsoptionen mehr Leistungsschäden als schlechte Ausführungsoptionen. Daher verbringen MySQL-Entwickler mehr Zeit damit, sicherzustellen, dass die Tabellen in der Abfrage in einer optimalen Reihenfolge zusammengefügt werden und dass die beste Zugriffsmethode (oft als Zugriffspfad bezeichnet) zur Überprüfung der Tabellendaten ausgewählt wird. Die Kombination aus einer festen Reihenfolge von Tabellenverknüpfungen und den entsprechenden Tabellenzugriffsmethoden für alle Tabellen wird als Abfrageausführungsplan (Query Execution Plan, QEP) bezeichnet. Der Zweck des Abfrageoptimierers besteht darin, unter allen möglichen Plänen den besten QEP zu finden. Es gibt einige allgemeine Konzepte hinter der JOIN-Verbindungspriorität.

Jeder Plan oder Teil eines Plans wird als COST definiert. Die Kosten eines Plans spiegeln in etwa die Ressourcen wider, die erforderlich sind, um die Abfrage wie geplant zu berechnen, wobei der Hauptfaktor die Gesamtzahl der Datensätze ist, auf die bei der Berechnung der Abfrage zugegriffen wird. Sobald wir die Möglichkeit haben, QEPs verschiedenen Kosten zuzuordnen, können wir sie vergleichen. Auf diese Weise besteht der Zweck des Optimierers darin, unter allen möglichen Plänen den QEP mit den niedrigsten Kosten zu finden.

In MySQL wird die beste QEP-Suche von unten nach oben implementiert. Der Optimierer bestätigt zunächst alle Pläne für eine Tabelle, dann alle Pläne für zwei Tabellen usw., bis eine vollständig optimale QEP ermittelt ist. Abfragepläne, die nur einige der Tabellen und Prädikate in der Abfrage enthalten, werden als Teilpläne bezeichnet. Der Optimierer basiert auf der Tatsache, dass die Kosten umso höher sind, je mehr Tabellen zu Teilplänen hinzugefügt werden (Hinweis: Je höher die Kosten, desto geringer die Ausführungseffizienz). Dadurch kann der Optimierer auf mehr Tabellen skalieren und dabei nur kostengünstigere Teilpläne als die derzeit besten Komplettpläne verwenden.
Um den Schlüsselweg der Suche nach einem besten QEP zu vervollständigen, siehe sql/sql_select.cc, find_best(). Es führt eine umfassende Suche aller möglichen Pläne durch und stellt so sicher, dass am Ende ein optimaler Plan gefunden wird.


Nachfolgend beschreiben wir den Pseudocode der find_best()-Methode. Da dies rekursiv ist, werden einige der Eingabevariablen markiert, um anzuzeigen, woher sie bisher aus der vorherigen Iteration stammen.

remaining_tables = {t1, ..., tn}; /* alle Tabellen, auf die in einer Abfrage verwiesen wird */

procedure find_best(
   Partial_plan in,      /* in, Teilplan der bisher verknüpften Tabellen */
   Partial_plan_cost,    /* in, Kosten von Partial_plan */
   verbleibende_Tabellen,     /* in , Tabellensatz, auf den in „partial_plan“ nicht verwiesen wird */
   best_plan_so_far,     /* in/out, bisher bester Plan gefunden */
   best_plan_so_far_cost)/* in/out, Kosten von best_plan_so_far */
{
   für jede Tabelle T aus verbleibenden_Tabellen
   {
     /* Berechnen Sie die Kosten für die Verwendung von Tabelle T. Zu den Faktoren, die das
       -Optimierer berücksichtigt, können gehören:
          Viele Zeilen in der Tabelle (fehlerhaft)
Viele Schlüsselteile haben bisherige Gemeinsamkeiten mit Tabellen (sehr gut)
          In der WHERE-Klausel erwähnte Einschränkung (gut)
          Langer Schlüssel (gut)
          Eindeutiger oder Primärschlüssel (gut)
          Volltextschlüssel (schlecht)
        Andere Faktoren, die irgendwann eine Überlegung wert sein können:
          Viele Spalten im Schlüssel
          Kurze durchschnittliche/maximale Schlüssellänge
          Kleine Tabellendatei
          Wenige Ebenen im Index
         Alle Die Spalten ORDER BY / GROUP stammen aus dieser Tabelle */
     cost = complex-series-of-calculations;
     /* Addieren Sie die Kosten zu den bisherigen Kosten. */
     „partial_plan_cost+= cost;“ Erweitern Sie Partial_Plan um Best_Access_Method;

  s,

                 best_plan_so_far, best_plan_so_far_cost) 🎜>
这里优化器利用了一种深度优先搜索算法.它完成了在FROM语句中评估所有的表.如果评估比起目前为止最好的评估, 变得更差, 它将停止搜索.扫描的顺序依赖于出现FROM语句中的表的顺序

源代码见:/sql/table.h, struct st_table。


 分析表(ANALYZE. TABLE)可能会影响到一些优化器决断的因素。源代码见:/sql/sql_sqlect.cc,make_join_statistics()。


 find_best() und greedy_search()的直截了当(straightforward)使用将不会用于LEFT JOIN或RIGHT JOIN.Bei MySQL 4.0.14 wird die Funktion LEFT JOIN verwendet Optimierung。


 

 RANGE联接类型

 有些条件可以使用索引,但是在一个键的范围(range)或宽度内。这些称为范围条件,最常看到的是带> ;,>=,<,<=,IN,LIKE,BETWEEN的查询表达式。

 

 对优化器来说,如下表达式:

column1 IN (1,2,3)

和这个是一样的:

column1 = OR Column1 = OR Column1 = 3
 MySQL同样对待这种语句,无需对查询条件的IN到OR或OR到IN做转变.


 

 如下语句,优化器也会用到索引(范围查询Bereichssuche)

column1 LIKE 'x%'

 但这种就不行:


column1 LIKE. '%x%'

 也就是说,如果匹配条件的第一个字符是通配符,那就没范围查询.

 

 同样,如下两个语句也是一样的


column1 BETWEEN 5 AND 7


Spalte1 >= UND Spalte1 <= 7

Wenn die Abfragebedingungen zu viele Indexschlüssel abrufen, ändert der Optimierer möglicherweise den RANGE-Join-Typ in den ALL JOIN-Join-Typ. Solche Transformationen sind insbesondere in <-Bedingungen und mehrstufigen Sekundärindizes möglich. Siehe Quellcode: /myisam/mi_range.c, mi_records_in_range() (MyISAM-Index).


INDEX-Join-Typ

Betrachten Sie diese Abfrage

SELECT Column1 FROM Table1;
Wenn Spalte1 indiziert ist, dann ist der Optimierer es Es besteht die Möglichkeit, den Wert aus dem hinzugefügten Index statt aus der Tabelle abzurufen (vollständiger Tabellenscan). Solche Indizes werden im Allgemeinen als abdeckende Indizes (COVERING INDEX) bezeichnet. In der EXPLAIN-Extra-Beschreibung verwendet MySQL einfach das Wort INDEX, um den abdeckenden Index (COVERING INDEX) darzustellen.


Anweisung:

SELECT Spalte1, Spalte2 FROM Tabelle1; Nur wenn der Index wie folgt definiert ist, verwendet der Optimierer den JOIN-Verbindungstyp INDEX: Join-Typ = Index CREATE INDEX. . ON Tabelle1 (Spalte1, Spalte2);
Mit anderen Worten, die abgefragten Felder (z. B. Spalte1, Spalte2) müssen indiziert sein, und die mehreren indizierten Felder sind nicht in der richtigen Reihenfolge. Daher ist es sinnvoller, einen mehrspaltigen Index strikt als COVERING INDEX zu definieren, der unabhängig von Suchaspekten verwendet werden soll.


INDEX MERGE-Join-Typ


Übersicht

Verwendung der Indexzusammenführung (INDEX MERGE), wenn die Tabellenbedingungen konvertiert werden können in Wie folgt:


cond_1 ODER cond_2 ... ODER cond_N
Die Bedingungen für die Konvertierung sind: Jede cond_i-Bedingung (cond_1, cond_2...) kann für die Bereichssuche verwendet werden, und zwar dort ist kein Bedingungspaar ( cond_i, cond_j) mit demselben Index. Wenn die Bedingungen cond_i und cond_j denselben Index verwenden, können die Bedingungen cond_i oder cond_j in einem einzigen Bereichsscan kombiniert werden, und es ist keine Zusammenführung erforderlich.


Die folgende Abfrage verwendet Indexzusammenführung (INDEX MERGE):

SELECT * FROM t WHERE key1=c1 OR key2

SELECT * FROM t WHERE (key1=c1 OR key2 Die Indexzusammenführung (INDEX MERGE) wird als Bereichsschlüssel implementiert und ein mit cond_i ( cond_1, cond_2...) Bedingungen. Bei einer Indexzusammenführung (INDEX MERGE) ruft MySQL die Zeilen für jeden Keyscan ab und führt sie dann durch einen Deduplizierungsprozess. Derzeit wird die Klasse Unique verwendet, um Duplikate zu entfernen.

INDEX MERGE Optimizer


Ein einzelnes SEL_TREE-Objekt kann nicht so konstruiert werden, dass es Schlüssel mit unterschiedlichen Mitgliedern in einer OR-Anweisung hat, wie diese Bedingung:

key1 < c1 OR key2 < c2


Ab MySQL 5.0 werden diese Bedingungen von der INDEX MERGE-Methode und dem Bereichsoptimierer indiziert. SEL_IMERGE stellt die Disjunktion mehrerer SEL_TREE-Objekte dar, die ausgedrückt wird als:

sel_imerge_cond = (t_1 OR t_1 OR ... OR t_n)
Jedes t_i (t_1, t_2...) stellt einen SEL_TREE dar , kann kein Paar (t_i, t_j) verschiedener SEL_TREE-Objekte zu einem einzigen SEL_TREE-Objekt zusammengeführt werden.


Die aktuelle Implementierungsmethode erstellt SEL_IMERGE nur, wenn kein einzelnes SEL_TREE-Objekt als Teil der analysierten Abfrage erstellt werden kann. Wenn festgestellt wird, dass ein einzelnes SEL_TREE-Objekt erstellt werden kann, wird es sofort verworfen . Dies stellt tatsächlich eine Einschränkung dar und kann dazu führen, dass Zeilenabrufstrategien im ungünstigsten Fall verwendet werden. Die folgende Abfrage:


SELECT * FROM t WHERE (goodkey1=c1 OR goodkey1=c2) AND badkey=c3
Der Scan auf badkey wird ausgewählt, auch auf (goodkey1, goodkey1) Index Zusammenführung (INDEX MERGE) wird schneller sein.


Indexzusammenführung (INDEX MERGE) Der Optimierer sammelt eine Liste aller möglichen Routen für die Indexzusammenführung (INDEX MERGE), um auf Zeilen zuzugreifen. Diese SEL_IMERGE-Strukturliste stellt die folgenden Bedingungen dar:


(t_11 OR t_12 OR ... OR t_1k) AND
(t_21 OR t_22 OR ... OR t_2l) AND
. . 

SEL_IMERGE-Objekt mit minimalen Kosten, das zum Abrufen von Zeilen verwendet wird.


Ausführliche Informationen zum Indexzusammenführungskonstruktor (INDEX MERGE) finden Sie unter: Quellcode sql/opt_range.cc, imerge_list_and_list(), imerge_list_or_list() und Mitgliedsfunktionen der SEL_IMERGE-Klasse.

RANGE-Optimierer

Für Bereichs-RANGE-Abfragen erstellt der MySQL-Optimierer ein SEL_TREE-Objekt in der folgenden Form:

range_cond = (cond_key_1 AND cond_key_2 AND ... UND cond_key_N)

Jeder cond_key_i ist eine Bedingung, die Bestandteil eines Schlüssels ist. MySQL erstellt für jeden nützlichen Schlüssel eine cond_key_i-Bedingung. Dann wird diese günstigste Bedingung cond_key_i für das Range-RANGE-Scannen verwendet.


Eine einzelne cond_key_i-Bedingung wird durch ein zeigerverknüpftes Netzwerk von SEL_ARG-Objekten dargestellt. Jedes SEL_ARG-Objekt verweist auf einen bestimmten Teil des Schlüssels und stellt die folgenden Bedingungen dar:


sel_arg_cond= (inf_val < key_part_n AND key_part_n < sup_val) (1)
(2)
>

1. Das Implementierungsintervall darf keine oberen oder unteren kritischen Werte haben oder kritische Werte enthalten oder auch nicht.

2. Implementieren Sie ein SEL_ARG-Objekt mit Bedingung für die nächste Schlüsselkomponente (gilt für ein SEL_ARG-Objekt mit Bedingung für die nächste Schlüsselkomponente).

3. Implementieren Sie ein SEL_ARG-Objekt mit einem Intervall im selben Feld wie dieses SEL_ARG-Objekt (gilt für ein SEL_ARG-Objekt mit einem Intervall im selben Feld wie dieses SEL_ARG-Objekt). Die Intervalle zwischen dem aktuellen Objekt und dem linken Objekt sind disjunkt. left_sel_arg_cond.sup_val <= inf_val.


4. Implementieren Sie ein beabstandetes SEL_ARG-Objekt im selben Bereich wie dieses SEL_ARG-Objekt. Die Intervalle zwischen dem aktuellen Objekt und dem rechten Objekt sind disjunkt. left_sel_arg_cond.min_val >= max_val.


MySQL wandelt verschachtelte UND-ODER-Bedingungen beliebiger Tiefe in die oben angegebene verbundene Form um.

Zeilenabrufalgorithmus

Die Indexzusammenführung (INDEX MERGE) umfasst die folgenden zwei Schritte:


Vorbereitungsphase:

aktiviere 'nur Index';

foreach key_i in (key_scans clustered_pk_scan)

{
while (nächstes (Schlüssel-, Rowid-)Paar von key_i abrufen)
{
if (kein Clustered PK Scan ||
                                                           jede Zeilen-ID in Unique
{
Zeile abrufen und übergeben es soll ausgegeben werden;
}
if (clustered_pk_scan)
{
while (nächste Zeile für Clustered_pk_scan abrufen)

Zeile an Ausgabe übergeben;

}

Für den Quellcode: siehe: sql/opt_range.cc, der Zeilenabrufcode für die Indexzusammenführung (INDEX MERGE) der Klassenfunktion QUICK_INDEX_MERGE_SELECT.



3. Transpositionen

MySQL unterstützt die Transposition einfacher Anweisungsausdrücke (Umkehrung der Reihenfolge der Operanden von Vergleichsoperatoren). Mit anderen Worten:

WHERE - 5 = Spalte1
Diese Aussage kann umgewandelt werden in:

WHERE Spalte1 = -5


Allerdings unterstützt MySQL keine Transpositionen mit Operationen wie:

WHERE 5 = -column1

Und dieser Satz kann nicht gleich behandelt werden:


WHERE Column1 = -5


Die Transposition von Ausdrücken wie dieser „column = konstant“ dient der besseren Indexabfrage. Wenn diese Anweisungsform indizierte Felder hat, verwendet MySQL unabhängig von der Größe der Tabelle immer die indizierten Felder. (Ausnahme: Wenn die Tabelle keine Datensätze oder nur eine Zeile mit Datensätzen enthält, handelt es sich um eine Konstantentabelle und erfordert eine besondere Behandlung. Siehe Konstantenwerte und Konstantentabellen.)

UND-Beziehung

Eine UND-Abfrageform ist Bedingung1 UND Bedingung2 wie folgt:


WHERE Spalte1 = 'x' UND Spalte2 = 'y'

Dieser Schritt beschreibt den Entscheidungsprozess des Optimierers:

1. Wenn keine der Bedingungen indiziert ist, wird ein sequenzieller Scan (vollständiger Tabellenscan) verwendet.

2. Wenn eine der Bedingungen einen besseren JOIN-Join-Typ hat, wählen Sie zusätzlich zum vorherigen Punkt einen Treiber mit dem JOIN-Join-Typ aus. (Siehe Bestimmen des JOIN-Verbindungstyps)

3. Zusätzlich zu den ersten beiden Punkten wird ein Treiber basierend auf dem zuerst erstellten Index ausgewählt, wenn beide Bedingungen indizierte und gleiche JOIN-Verbindungstypen haben (Hinweis: JON-Verbindungstypen haben gute oder schlechte Auswirkungen).

Der Optimierer wählt auch die Indexzusammenführung (INDEX MERGE) basierend auf der Indexkreuzung aus, siehe INDEX MERGE-Join-Typ. Beispiele sind wie folgt:

CREATE TABLE Table1 (s1 INT, s2 INT);

CREATE INDEX Index1 ON Table1 (s2);

CREATE INDEX Index2 ON Table1 (s1);
...

SELECT * FROM Table1 WHERE s1 = AND s2 = 5;

Bei der Auswahl einer Strategie zur Lösung dieser Abfrage wählt der Optimierer s2 = 5 als Treiber, da der Index auf s2 zuerst erstellt wird . Betrachten Sie es als einen zufälligen Effekt, nicht als eine Regel, die sich jederzeit ändern kann.

ODER-Beziehung


Eine ODER-Abfrageform ist Bedingung1 ODER Bedingung2, wie folgt:

WHERE Spalte1 = 'x' ODER Spalte2 = 'y'
Die Entscheidung dieses Abfrageoptimierers besteht darin, einen sequentiellen vollständigen Tabellenscan zu verwenden.

Unter bestimmten Umständen besteht auch die Möglichkeit, die Indexzusammenführung (INDEX MERGE) zu verwenden. Weitere Informationen finden Sie unter INDEX MERGE Optimizer und Index Merge Optimization.


Die oben genannte spezifische Situation kann nicht verwendet werden, wenn die Felder der beiden Bedingungen identisch sind. Wie folgt:

WHERE Column1 = 'x' OR Column1 = 'y'
Da es sich in diesem Fall um eine RANG-Abfrage handelt, wird die Anweisung indiziert. Dieses Thema wird in der Diskussion der IN-Prädikate noch einmal auftauchen.


UNION-Abfrage

Alle SELECT-Abfrageanweisungen, die UNION enthalten, werden separat optimiert. Daher lautet diese Abfrage:

SELECT * FROM Table1 WHERE Column1 = 'x'
UNION ALL
SELECT * FROM TABLE1 WHERE Column2 = 'y'
Wenn sowohl Column1 als auch Column2 indiziert sind, Bei jedem SELECT wird ein Indexscan durchgeführt und die entsprechenden Ergebnismengen werden zusammengeführt. Hinweis: Diese Abfrage erzeugt möglicherweise denselben Ergebnissatz, wenn die Abfrage das sequenzielle Scan-OR-Beispiel verwendet.


NICHT (<>)-Beziehung

Eine logische Bedingung lautet wie folgt:


Spalte1 <> 5
Entspricht:


Spalte1 < 5 ODER Spalte1 > 5
Allerdings führt MySQL für diese Bedingung keine Konvertierungsanweisungen durch. Wenn Sie der Meinung sind, dass die Verwendung von RANG zum Abfragen zu besseren Ergebnissen führt, müssen Sie die Anweisungen manuell selbst konvertieren.

Es gibt auch eine logische Bedingung wie folgt:

WHERE NOT (column1 != 5)
ist äquivalent zu:


WHERE Column1 = 5
In diesem Fall führt MySQL keine Anweisungskonvertierung durch.


Wir freuen uns darauf, neue Optimierungsmethoden für die beiden oben genannten Situationen hinzuzufügen.

ORDER BY-Anweisung


Wenn der Optimierer feststellt, dass die Zeilendatensätze trotzdem geordnet sind, springt er normalerweise auch in die ORDER BY-Anweisung SORT-Prozess. Es sind jedoch noch einige Ausnahmen zu überprüfen.


Beispiel:

SELECT Column1 FROM Table1 ORDER BY 'x';
Der Optimierer verwirft die ORDER BY-Anweisung, die auch ein Beispiel für das Löschen von totem Code ist.


Beispiel:


SELECT Column1 FROM Table1 ORDER BY Column1;
Der Optimierer verwendet den Index von Column1, falls vorhanden.


Beispiel:

SELECT Column1 FROM Table1 ORDER BY Column1+1;
Der Optimierer verwendet den Index von Spalte1, falls vorhanden. Aber seien Sie nicht verwirrt, Indizes werden nur zum Auffinden von Datensatzwerten verwendet. Darüber hinaus sind die Kosten für das sequentielle Scannen des Index geringer als die Kosten für das sequentielle Scannen der gesamten Tabelle (im Allgemeinen ist die Größe des Index kleiner als die Größe des Datenwerts), weshalb der Verbindungstyp INDEX JOIN höher ist optimiert als der ALL-Typ. Siehe Bestimmen des JOIN-Join-Typs.


Es gibt auch eine Sortierung SORT für alle Ergebnismengen, zum Beispiel:


SELECT * FROM Table1
WHERE Column1 > x' UND Spalte2 > 'x'
ORDER BY columns2;
Wenn Spalte1 und Spalte2 beide Indizes haben, verwendet der Optimierer den Index für Spalte1. In dieser Abfrage hat die Reihenfolge der Spalte2-Werte keinen Einfluss auf die Treiberauswahl.


Siehe den Quellcode: /sql/sql_select.cc, test_if_order_by_key() und /sql/sql_select.cc, test_if_skip_sort_order().


ORDER BY Optimization beschreibt den Inhaltsmechanismus des SORT-Sortierprozesses und wird hier nicht noch einmal erläutert. Wir empfehlen Ihnen jedoch dringend, es zu lesen, da es die Funktionsweise der Pufferungs- und Quicksort-Mechanismen beschreibt.

GROUP BY und verwandte Bedingungen


GROUP BY und verwandte Bedingungen (HAVING, COUNT(), MAX(), MIN(), Wichtige Optimierungen für SUM (), AVG(), DISTINCT()).


GROUP BY verwendet den Index, wenn ein Index vorhanden ist.
GROUP BY verwendet die Sortierung, wenn kein Index vorhanden ist. Der Optimierer kann sich für die Verwendung der HASH-Tabellensortierung entscheiden.
Im Fall von GROUP BY x ORDER BY x geht der Optimierer davon aus, dass ORDER BY unnötig ist, da GROUP BY nach x sortiert wird.
Der Optimierer enthält in der WHERE-Anweisung Code, der bestimmte HAVING-Bedingungen überträgt. Dieser Code ist jedoch zum Zeitpunkt des Schreibens nicht gültig. Siehe Quellcode: /sql/sql_select.cc, JOIN::optimize(), nach #ifdef HAVE_REF_TO_FIELDS.
Wenn der Tabellenhandler eine gültige schnelle Zeilenanzahl hat, dann diese Abfrage:
SELECT COUNT(*) FROM Table1;
Sie können die Zeilenanzahl erhalten, ohne den Wert aller Zeilen zu scannen. Dies gilt nur für MyISAM-Tabellen, nicht jedoch für InnoDB-Tabellen. Darüber hinaus weist diese Abfrage


SELECT COUNT(column1) FROM Table1;
nicht die gleiche Optimierung auf, es sei denn, Spalte1 ist als NOT NULL definiert.


Neue Optimierungsmethoden für MAX() und MIN(). Beispiel:
SELECT MAX(column1)
FROM Table1
WHERE columns1 < 'a';
Wenn Spalte1 indiziert ist, ist es einfach, den Maximalwert zu finden, indem man den Wert 'a' abfragt der Index Und davor wird der Indexschlüssel zurückgegeben.


Optimieren Sie die Abfrage in der folgenden Form und führen Sie eine Anweisungstransformation durch:
SELECT DISTINCT Column1 FROM Table1;
wird zu:


SELECT Column1 FROM Table1 GROUP BY Column1;
Nur ​​wenn diese beiden Bedingungen zutreffen:


* GROUP BY kann über den Index vervollständigt werden. Dies bedeutet, dass die FROM-Anweisung nur eine Tabelle und keine WHERE-Anweisung enthält.
* Es gibt keine LIMIT-Anweisung.

Da DISTINCT-Anweisungen nicht immer in GROUP BY konvertiert werden, können Sie nicht damit rechnen, dass Abfrageanweisungen, die DISTINCT enthalten, immer eine sortierte Ergebnismenge haben. Sie können sich jedoch auf die Optimierungsregeln von GROUP BY verlassen, es sei denn, die Abfrage enthält ORDER BY NULL.


Drei. Andere Optimierungen


In diesem Abschnitt werden andere, speziellere Optimierungsmethoden besprochen.

1. NULL-Wertfilterungszugriff für ref und eq_ref


In diesem Abschnitt wird die Optimierungsmethode für die NULL-Wertfilterung für ref- und eq_ref-Join-Typen erläutert.

Frühe NULL-Wertefilterung

Angenommen, wir haben eine Join-Sequenz wie folgt:

..., tblX, ..., tblY , . ..
Fortgeschrittenere Annahmen: Auf die Tabelle tblY wird über einen ref- oder eq_ref-Union-Typ zugegriffen:


tblY.key_column = tblX.column
Alternativ können Sie einen ref-Typ mit mehreren Schlüsselteilen verwenden Zugriff:


... AND tblY.key_partN = tblX.column AND ...
tblX.column kann NULL sein. Beim Zugriff auf den Typ ref (oder eq_ref) wird die NULL-Filterung in der frühen Phase angewendet. Wir ziehen die folgende Schlussfolgerung:


(tblY.key_partN = tblX.column) => (tblX.column IS NOT NULL)
Die ursprüngliche Gleichung kann nur nach dem Lesen der Tabellen tblX und überprüft werden tblY, nachdem die aktuelle Zeile aufgezeichnet wurde. Das Prädikat IS NOT NULL wird erst nach dem Lesen des aktuellen Zeilendatensatzes in der Tabelle tblX überprüft. Wenn es


andere Tabellen in der Unionsart der Tabellen tblX und tblY gibt, ermöglicht uns die Prädikatprüfung IS NOT NULL, den Zugriff auf diese Tabellen zu überspringen.

Der Implementierungscode dieser Funktion lautet wie folgt:


Ref-Analysator (mit der Methode update_ref_and_keys()) prüft und markiert den oben genannten Typ einer Abfragegleichung .
Nach Auswahl der JOIN-Verbindungssortierung fügt add_not_null_conds() das entsprechende IS NOT NULL-Prädikat zu den relevanten Bedingungen der entsprechenden Tabelle hinzu.

Das Hinzufügen des IS NOT NULL-Prädikats zu allen Gleichungen kann von Ref-Zugriffstypen verwendet werden (und nicht von denen, die tatsächlich verwendet werden). Dies geschieht derzeit jedoch nicht.

Späte (späte) NULL-Filterung

Angenommen, wir haben einen Tabellen-tblX-Abfrageplan, auf den über den Ref-Zugriffstyp zugegriffen wird:

tblX. key_part1 = expr1 AND tblX.key_part2 = expr2 AND ...
Bevor wir den Indexabruf aufrufen, ermitteln wir, ob ein expr-Wert (expr1, expr2, expr3...) NULL ist. Wenn dies der Fall ist, rufen wir die Suche nicht auf, sondern geben sofort ein Array ohne gefundene Übereinstimmungen zurück.


Diese Optimierungsmethode verwendet das durch die frühe NULL-Filterung generierte null_rejecting-Attribut wieder. Den Quellcode dieser Prüfung finden Sie unter: function join_read_always_key().

2. Partitionsbezogene Optimierungen

In diesem Teil werden MySQL-Partitionsbezogene Optimierungen erläutert. Konzepte und Implementierungen im Zusammenhang mit der MySQL5.1-Partitionierung finden Sie unter: Partitionierung.

Partitionsbereinigung (Pruning)

Der Vorgang der Partitionsbereinigung ist wie folgt definiert:

„Stellt eine Abfrage für eine partitionierte Tabelle bereit, z Die DDL-Anweisung dieser partitionierten Tabelle und jede WHERE- oder ON-Anweisung in der Abfrage und den minimalen Partitionssatz ermitteln, auf den diese Abfrage zugreift. „


Das resultierende Partitionssatzverhältnis Die Menge aller Partitionen der Tabelle ist viel kleiner und diese Partitionsmenge wird auch in nachfolgenden Abfrageanweisungen verwendet. Auf andere Partitionen, die diesem Partitionssatz nicht hinzugefügt wurden, wird nicht zugegriffen, d. h. die Partitionen wurden bereinigt. Aus diesem Grund werden Abfragen schneller ausgeführt.


Nicht-transaktionale Tabellen-Engines Wenn MyISAM keine transaktionale Speicher-Engine hat, werden Sperren zur gesamten Partitionstabelle hinzugefügt. Theoretisch ist es möglich, die Parallelität durch Partitionsbereinigung zu verbessern, indem nur den verwendeten Partitionen Sperren hinzugefügt werden. Diese Funktion wurde jedoch noch nicht implementiert.

Die Partitionsbereinigung hängt nicht von der Speicher-Engine der Tabelle ab, daher ist diese Funktion Teil des MySQL-Abfrageoptimierers. In den folgenden Abschnitten werden die Details der Partitionsbereinigung beschrieben.

Übersicht über die Partitionsbereinigung

Die Implementierungsschritte der Partitionsbereinigung sind wie folgt:

1. Analysieren Sie die Bedingungen der WHERE-Anweisung und erstellen Sie ein Intervalldiagramm, um die Ergebnisse der Analyse zu beschreiben.

2. Ermitteln Sie anhand des Intervalldiagramms die Menge der besuchten Partitionen (einschließlich Unterpartitionen) für jedes Intervall.

3. Konstruieren Sie die für die Abfrage erforderlichen Partitionen.


Das Intervalldiagramm ist von unten nach oben aufgebaut und stellt die Beschreibung der oben genannten Schritte dar. Im Anschluss an die Diskussion definieren wir zunächst den Begriff Intervalldiagramm, beschreiben dann, wie Partitionsintervalle zur Bildung eines Intervalldiagramms verwendet werden, und beschreiben schließlich den Arbeitsablauf des Intervalldiagramms.

Partitionierungsintervalle

Einzelpunktintervalle

Ausgehend vom einfachsten Fall nehmen wir an, dass es N gibt. Die Partitionstabelle der Spalten wird durch die Partition dargestellt Geben Sie p_type und die Partitionsfunktion p_func wie folgt ein:

CREATE TABLE t (columns)
PARTITION BY p_type(p_func(col1, col2,... colN)...);
Angenommen, die WHERE-Bedingung der Abfrage hat die folgende Form:


WHERE t.col1=const1 AND t.col2=const2 AND ... t.colN=constN
Wir können p_func(const1, const2 ... constN) berechnen und herausfinden, welche Partition enthält Der Datensatz ist das Gleiche wie die WHERE-Bedingung. Hinweis: Dieser Vorgang funktioniert auf allen Partitionstypen und allen Partitionsfunktionen.


Hinweis: Dieser Prozess funktioniert nur, wenn die WHERE-Bedingung die obige Form hat und jede Spalte der Tabelle auf Übereinstimmung mit einer beliebigen Konstante überprüft werden muss (nicht). muss für jede Spalte die gleiche Konstante sein). Wenn es beispielsweise in der WHERE-Anweisung des obigen Beispiels kein col1=const1 gibt, berechnen wir den Wert der p_func-Partitionsfunktion nicht und beschränken den tatsächlich verwendeten Partitionssatz nicht.


Intervallgehen (Gehen)

Angenommen, eine Partitionstabelle t ist als Spaltensatz definiert, der Partitionstyp p_type und die Partitionsfunktion p_func verwendet die ganzzahliges Typfeld int_col wie folgt:

CREATE TABLE t (columns)
PARTITION BY
p_type(p_func(int_col))
...
Angenommen, wir haben eine WHERE-Bedingung Abfrage der folgenden Form:


WHERE const1 <= int_col <= const2
Wir können die Bedingungen dieser Situation in eine Reihe von Einzelpunktintervallen (Einzelpunktintervalle) reduzieren. , wie folgt, indem Sie diese WHERE-Anweisung in die folgende Beziehung umwandeln:


int_field = const1 oder
int_field = const1 + 1 oder
int_field = const1 + 2 oder
🎜
int_field = const2

>

Im Quellcode wird diese Transformation als Intervallgehen (Walking) bezeichnet. . Die Kosten für das Reisen in kurzen Abständen sind nicht hoch, sodass wir die Anzahl der Partitionen reduzieren können, um kleine Partitionen zu scannen. Das Durchqueren großer Bereiche ist jedoch nicht sehr effizient und erfordert die Überprüfung einer großen Anzahl von Partitionen. In diesem Fall können alle Partitionen gescannt werden.

Die folgenden Parameter bestimmen den Wert des Intervallgehens:

#define MAX_RANGE_TO_WALK=10

Hinweis: Die folgenden bedingten Beziehungen verwenden auch die oben genannten Intervalllogik des Gehens:

const1 >= int_col >= const2



Intervallzuordnung (Mapping)

Nehmen Sie die folgende Definition der Partitionstabelle an:

CREATE TABLE t (columns)
PARTITION BY RANGE|LIST(unary_ascending_function(column))

Angenommen, die WHERE-Anweisung unserer Abfrage für Tabelle t hat eine der folgenden Formen:

const1 <= t.column <= const2

t.column <= const2

const1 <= t.column

Die Selbstpartitionierungsfunktion ist In Sehen Sie sich in aufsteigender Reihenfolge die folgende Beziehung an:

const1 <= t.col <= const2


=> t. Spalte) <= p_func(const2)
Indem wir A und B verwenden, um die Teile ganz links und ganz rechts dieser Beziehung darzustellen, können wir die Beziehung wie folgt umschreiben:


A <= p_func (t .column) <= B

Hinweis: In diesem Beispiel ist das Intervall geschlossen und hat zwei Grenzen. Ähnliche Schlussfolgerungen können jedoch auf andere Intervalltypen ausgeweitet werden.


Wie bei der Bereichspartitionierung (RANGE-Partitionierung) belegt jede Partition ein Intervall auf der Achse des Partitionsfunktionswerts und jedes Intervall ist nicht verbunden, wie folgt:


p0 p1 p2
Tabellenpartitionen ------x------x--------x-------->

Suchintervall ----x=============x----------->
Auf eine Partition muss zugegriffen werden, wenn und nur wenn sein Intervall und das Suchintervall [A, B] haben keine leeren Schnittpunkte.


Bei der LIST-Partitionierung enthält beispielsweise jede Partition einen auf der Achse des Partitionsfunktionswerts festgelegten Punkt, und jede Partition erzeugt unterschiedliche Schnittpunkte wie folgt:

p0 p1 p2 p1 p1 p0Tabellenpartitionen --+---+----+----+----+----+---->

Suchintervall ----x===================x------>
Besuch, mindestens eine Kreuzung liegt in der Suchintervall [A, B]. Der verwendete Partitionssatz bestimmt die Partitionen, die von A nach B verlaufen und ihre Punkte innerhalb dieses Suchbereichs sammeln.


Unterpartitionierungsintervalle

Im vorherigen Abschnitt haben wir verschiedene Möglichkeiten beschrieben, um aus grundlegenden WHERE-Bedingungen auf den aktiven Partitionssatz zu schließen. Alles zeigt, dass die Inferenzmethoden dieser Partitionen für Unterpartitionen geeignet sind, mit Ausnahme der Unterpartitionen der Bereichspartitionierung (RANGE-Partitionierung) und der Aufzählungspartitionierung (LIST-Partitionierung).

Da jede Partition auf die gleiche Weise molekülpartitioniert ist, werden wir herausfinden, auf welche Unterpartition innerhalb jeder Partition zugegriffen wird.


Von WHERE-Klauseln zu Intervallen

Im vorherigen Kapitel wurde beschrieben, wie aus der WHERE-Anweisung auf Partitions- und Unterpartitionsintervalle eines Partitionssatzes geschlossen werden kann. Sehen wir uns nun an, wie man einen Bereich aus einer beliebigen WHERE-Anweisung extrahiert.

Der extrahierte Prozess verwendet den Bereichsanalysator (RANGE Analyzer), der Teil des MySQL-Optimierers ist, und generiert Pläne für den Bereichs-RANGE-Zugriff. Dies liegt daran, dass die Aufgaben ähnlich sind. Zwei Formen von WHERE-Anweisungen: Der RANGE-Zugriffstyp verwendet das Scannen des Indexbereichs (Intervall); das Partitionsbereinigungsmodul verwendet Partitionsintervalle, um zu bestimmen, welche Partition verwendet wird.

Für die Partitionsbereinigung wird der RANGE-Analysator mit der WHERE-Anweisung aufgerufen, einer Liste von Tabellenspalten, die von den Partitionierungs- und Unterpartitionierungsfunktionen verwendet werden:

( part_col1, part_col2, ... part_colN,

subpart_col1, subpart_col2, ... subpart_colM)
Das Ergebnis der Arbeit des Bereichsanalysators (RANGE Analyzer) wird als SEL_ARG-Diagramm bezeichnet. Dies ist eine sehr komplexe Struktur und wir werden sie hier nicht beschreiben. Der aktuelle Schwerpunkt dieser kulturellen Diskussion liegt darin, dass wir durch alle Partitionen reisen und die Bereiche der Partitionen und Unterpartitionen erfassen können.


Das folgende Beispiel verdeutlicht den Aufbau und den Reiseablauf. Angenommen, Tabelle t ist wie folgt partitioniert:

CREATE TABLE t (..., pf INT, sp1 CHAR(5), sp2 INT, ... )

PARTITION BY LIST (pf)
UNTERTEILUNG DURCH HASH(sp1, sp2) (
PARTITION p0 WERTE IN (1),
PARTITION p1 WERTE IN (2),
PARTITION p2 WERTE IN (3),
PARTITION p3 WERTE IN ( 4),
PARTITION p4 VALUES IN (5),
);
Nehmen wir nun eine sehr komplexe WHERE-Anweisungsabfrage für Tabelle t an:

pf=1 AND ( sp1= 'foo' AND sp2 IN (40,50))

OR

(pf1=3 OR pf1=4) AND sp1='bar' AND sp2=33

ODER

((pf=3 ODER pf=4) UND sp1=5)

ODER

p=8

Das SEL_ARG-Bild sieht wie folgt aus:

(root)
   |               :
   | Partitionierung  :         Unterpartitionierung
   |               :
   |               :
   |               :
   |   +------+    :     +-----------+   +--------+
   ---| pf=1 |----:-----| sp1='foo' |---| sp2=40 |
       +------+    :     +-----------+   +--------+
          |        :                         |
          |        :                     +--------+
          |        :                     | sp2=50 |
          |        :                     +--------+
          |        :
          |        :
       +------+    :     +-----------+   +--------+
       | pf=3 |----:--+--| sp1='bar' |---| sp2=33 |
       +------+    :  |  +-----------+   +--------+
          |        :  |
       +------+    :  |
       | pf=4 |----:--+
       +------+    :
          |        :
          |        :
       +------+    :     +-----------+
       | pf=8 |----:-----| sp1='baz' |
       +------+    :     +-----------+

 上述图表,竖的边界(|)代表OR,横的(-)代表AND,横的和竖的线也代表AND。


 

 分区裁剪(partition pruning)代码游历从图上方到下方,从左边到右边,并做了如下的推论

1。在最上和最左的区间,从使用分区的空集合开始游历:

2。 
执行pf=1的区间分析,找到分区P0的相应集合,右移到sp1='foo' 
再次右移, 到sp2=40 
分析sp1='foo' AND. sp2=40区间, 在某SP1子分区找到行.推论一:在每个分区组成集合P0,标识子分区SP1“被使用“ 
下移到sp2=50 
分析sp1='foo'区间和sp2= 50 Tage, 1 Jahr vor SP2移回到pf=1, 然后下称到pf=3 
3.

执行pf=3的区间分析,找到分区P1的相应集合,右移到sp1='bar' 

再次右移,到sp2=33

分析sp1='bar' AND. sp2=33区间在某SP3子分区找到行.推论三:在每个分区组成集合P1,标识子分区SP3“被使用“

移回到pf=3,然后下移到pf=4

4...推论已在pf=3验证正确.这样的效果是比较差的, 因为我们将再次分析sp1='bar' AND sp2=33区间, 但这个操作不会很大影响到整体性能.

移回到pf=3,然后下称到pf=8

5.


执行pf=8的区间分析,找到分区P3的相应集合,右移到sp1='baz'

이제 sp1='baz'에 도달했으므로 더 이상 오른쪽으로 이동할 수 없으며 하위 파티션 간격을 구축할 수도 없습니다. pf=8을 기록하고 반환합니다
이전 프로세스에서는 더 이상 하위 파티션 범위를 제한할 수 없으므로 추론: P3 파티션 세트의 각 파티션에서 모든 하위 파티션이 유효하고 사용 중이라고 가정합니다. .

6. pf=9에서 아래로 이동하여 끝에 도달한 것을 확인하면 투어 그래프가 완성됩니다.

참고: 특정 상황에서 범위 분석기(RANGE 분석기)의 결과에는 OR 또는 AND 연산자로 구성된 여러 SEL_ARG 그래프가 있습니다. 이는 매우 복잡하거나 단일 범위 목록 구성을 허용하지 않는 WHERE 문에서 발생합니다. 이 상황에서 파티션 정리 코드는 적절한 작업을 사용합니다. 예:


SELECT * FROM t1 WHERE partition_id=10 OR subpartition_id=20
이 예에서는 단일 범위가 없습니다. 구성되었지만 파티션 정리 코드는 사용된 파티션 세트가 합집합이라고 올바르게 추론합니다.


파티션의 모든 하위 파티션에는 partition_id=10인 행이 포함되어 있고, 각 파티션의 하위 파티션에는 subpartition_id=인 행이 포함되어 있습니다. 20.


소스 코드에서 파티션 정리 구현

소스 코드에 대한 간단한 설명:

sql/opt_range.cc:
이것은 코드에는 WHERE 절에서 간격까지, 메소드 prune_partitions()의 구현이 포함되어 있습니다. PartitionPruningModule 코드에서 시작하여 파티션 정리에 대한 자세한 줄별 코드 주석이 있습니다.

sql/partition_info.h:

class partition_info {
...
/*
사용된(즉, 정리되지 않은) 파티션의 비트맵입니다. 여기에는 파티션 정리 결과
가 저장됩니다.
*/
MY_BITMAP Used_partitions;

/*
"가상 함수"는 이 분할된 테이블에서 간격 분석
을 수행하는 함수에 대한 포인터(opt_range.cc의 코드에서 사용됨)
*/
get_partitions_in_range_iter get_part_iter_for_interval;
get_partitions_in_range_iter get_subpart_iter_for_interval;

};
sql/sql_partition.cc:

이 코드에는 모든 파티션 간격 분석 유형을 구현하는 방법이 포함되어 있습니다.

파티션 검색

일련의 인덱스 검색(예: ref, eq_ref, ref_or_null 조인 액세스 모드)으로 파티션 테이블에 액세스하는 경우 MySQL은 모든 파티션이 있는지 확인합니다. 인덱싱이 필요합니다. 특정 파티션을 검색하거나 액세스를 제한합니다. 예:

CREATE TABLE t1 (a INT, b INT);

INSERT INTO t1 VALUES (1,1),(2,2),(3,3);

CREATE TABLE t2 (
keypart1 INT,
keypart2 INT,
KEY(keypart1, keypart2)
)
PARTITION BY HASH(keypart2);

INSERT INTO t2 VALUES (1,1),(2,2),(3,3);

쿼리 조건은 다음과 같습니다.

SELECT * FROM t1, t2
WHERE t2.keypart1=t1.a
AND t2.keypart2=t1.b;
다음 알고리즘을 사용하여 실행합니다.


(t1의 각 레코드에 대해: )
{
t2->index_read({현재 값-of(t1.a), 현재 값-of(t1.b)});
while( t2->index_next_same( ) )
행 조합을 쿼리 출력에 전달합니다.
}
index_read() 호출에서 파티션 테이블 핸들은 식별된 모든 파티션 열의 ​​​​값을 찾아냅니다. 이 예에서는 단일 열 b를 찾은 다음 파티션 액세스를 찾습니다. 이 파티션을 정리하면 다른 파티션에 액세스할 수 없습니다.

위 내용은 MySQL Internals Optimizer 내용입니다. 더 많은 관련 글은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!


Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage