• Aufgeworfene Fragen Die Struktur der Quelltabelle t_source ist wie folgt: Artikel-ID int, Erstellungszeit Datum/Uhrzeit, Änderungszeit Datum/Uhrzeit, Artikelname varchar (20), anderer varchar(20) Erfordern: 1. Die Quelltabelle enthält 1 Million Datensätze, von denen 500.000 doppelte Werte für „create_time“ und „item_name“ aufweisen. • Experimentelle Umgebung Virtuelle Linux-Maschine: CentOS Version 6.4; 8 GB physischer Speicher (MySQL mit 4 GB konfiguriert); 100 GB mechanische Festplatte; duale physische Dual-Core-CPU, insgesamt vier Prozessoren; MySQL 8.0.16. • Erstellen Sie Testtabellen und Daten -- Erstellen Sie eine Quelltabelle create table t_source (Artikel-ID int, Erstellungszeit Datum/Uhrzeit, Änderungszeit Datum/Uhrzeit, Artikelname varchar (20), anderer varchar(20) ); - Erstellen Sie die Zieltabelle. Erstellen Sie die Tabelle t_target wie t_source. -- Generieren Sie 1 Million Testdaten, von denen 500.000 „created_time“ und „item_name“ doppelte Trennzeichen haben // Prozedur sp_generate_data() erstellen beginnen setze @i := 1; während @i<=500000 setze @created_time := date_add('2017-01-01',Intervall @i Sekunde); setze @modifizierte_zeit := @erstellte_zeit; setze @item_name := concat('a',@i); in t_source einfügen Werte (@i,@Erstellungszeit,@Änderungszeit,@Artikelname,'andere'); setze @i:=@i+1; Ende während; begehen; setze @last_insert_id := 500000; in t_source einfügen wähle item_id + @last_insert_id, Erstellungszeit, date_add(modifizierte_Zeit,Intervall @last_insert_id Sekunde), Artikelname, 'andere' von t_source; begehen; Ende // Trennzeichen ; rufen Sie sp_generate_data() auf; -- Die Quelltabelle hat keinen Primärschlüssel oder eindeutige Einschränkung und es können zwei identische Daten vorhanden sein. Fügen Sie daher einen weiteren Datensatz ein, um diese Situation zu simulieren. in t_source einfügen, auswählen * aus t_source, wobei item_id=1; Es gibt 1.000.001 Datensätze in der Quelltabelle und die Zieltabelle sollte nach der Deduplizierung 500.000 Datensätze enthalten. mysql> wähle count(*), count(unterschiedliche Erstellungszeit, Elementname) aus t_source; +----------+----------------------------------------+ | Anzahl(*) | Anzahl(unterschiedliche Erstellungszeit, Artikelname) | +----------+----------------------------------------+ | 1000001 | 500000 | +----------+----------------------------------------+ 1 Reihe im Satz (1,92 Sek.) 1. Clevere Verwendung von Indizes und Variablen1. Vergleichstest ohne Index(1) Verwendung korrelierter Unterabfragen t_target abschneiden; in t_target einfügen wähle eindeutig t1.* aus t_source t1, wobei item_id in (Wählen Sie min(item_id) aus t_source t2, wobei t1.created_time=t2.created_time und t1.item_name=t2.item_name); Es dauert lange, bis diese Anweisung Ergebnisse liefert. Schauen Sie sich also einfach den Ausführungsplan an. mysql> erklären select distinct t1.* from t_source t1 where item_id in -> (wählen Sie min(item_id) aus t_source t2, wobei t1.created_time=t2.created_time und t1.item_name=t2.item_name); +----+--------------------+-------+------------+------+---------------+---------+---------+---------+---------+---------+----------+----------+------------------------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+--------------------+-------+------------+------+---------------+---------+---------+---------+---------+---------+----------+----------+------------------------------+ | 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 997282 | 100,00 | Mit Where; Mit Temporär | | 2 | ABHÄNGIGE UNTERABFRAGE | t2 | NULL | ALLE | NULL | NULL | NULL | NULL | 997282 | 1,00 | Verwenden von „where“ | +----+--------------------+-------+------------+------+---------------+---------+---------+---------+---------+---------+----------+----------+------------------------------+ 2 Zeilen im Set, 3 Warnungen (0,00 Sek.) Sowohl die Hauptabfrage als auch die zugehörige Unterabfrage scannen die gesamte Tabelle, was insgesamt das Scannen von 1 Million x 1 Million Datenzeilen erfordert. Kein Wunder, dass keine Ergebnisse geliefert werden. (2) Verwenden von Tabellenverknüpfungen t_target abschneiden; in t_target einfügen wähle eindeutig t1.* aus t_source t1, (Wählen Sie min(item_id) item_id,created_time,item_name aus der t_source-Gruppe nach created_time,item_name) t2 wobei t1.item_id = t2.item_id; Diese Methode dauert 14 Sekunden und der Abfrageplan sieht wie folgt aus: mysql> erklären Sie „select distinct t1.* from t_source t1“, (select min(item_id) item_id,created_time,item_name from t_source group by created_time,item_name) t2, wobei t1.item_id = t2.item_id; +----+----------+------------+------------+------+---------------+--------------+----------+---------+----------------+--------+----------+---------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+----------+------------+------------+------+---------------+--------------+----------+---------+----------------+--------+----------+---------------+ | 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 997282 | 100,00 | Mit „where“; Mit „temporär“ | | 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 5 | test.t1.item_id | 10 | 100,00 | Eindeutig | | 2 | ABGELEITET | t_source | NULL | ALLE | NULL | NULL | NULL | NULL | 997282 | 100,00 | Temporäre | werden verwendet | +----+----------+------------+------------+------+---------------+--------------+----------+----------------+--------+----------+---------------+ 3 Zeilen im Satz, 1 Warnung (0,00 Sek.) •Die innere Abfrage scannt 1 Million Zeilen der t_source-Tabelle, erstellt eine temporäre Tabelle, findet die minimale item_id nach der Deduplizierung und generiert die abgeleitete Tabelle derived2 mit 500.000 Zeilen. (3) Verwendung von Variablen setze @a:='1000-01-01 00:00:00'; setze @b:=' '; setze @f:=0; t_target abschneiden; in t_target einfügen Wählen Sie Artikel-ID, Erstellungszeit, Änderungszeit, Artikelname, Sonstiges aus (wählen Sie t0.*, wenn (@a = Erstellungszeit und @b = Elementname, @f:=0, @f:=1) f, @a:= Erstellungszeit, @b:= Elementname aus (Wählen Sie * aus t_Quelle, sortiert nach Erstellungszeit, Artikelname) t0) t1, wobei f=1; Diese Methode dauert 13 Sekunden und der Abfrageplan sieht wie folgt aus: mysql> erklären wählen Sie item_id,created_time,modified_time,item_name,other -> von -> (wähle t0.*, wenn (@a=Erstellungszeit und @b=Artikelname,@f:=0,@f:=1) f, @a:=Erstellungszeit,@b:=Artikelname -> von -> (Wählen Sie * aus t_source, sortiert nach Erstellungszeit, Artikelname) t0) t1, wobei f=1; +----+-------------+------------+------------+------+---------------+---------------+-------------+---------+---------+-----------+--------+-----------+----------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+-------------+------------+------------+------+---------------+---------------+-------------+---------+---------+-----------+--------+-----------+----------------+ | 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 4 | const | 10 | 100,00 | NULL | | 2 | ABGELEITET | <abgeleitet3> | NULL | ALLE | NULL | NULL | NULL | NULL | 997282 | 100,00 | NULL | | 3 | ABGELEITET | t_source | NULL | ALLE | NULL | NULL | NULL | NULL | 997282 | 100,00 | Dateisortierung wird verwendet | +----+-------------+------------+------------+------+---------------+---------------+-------------+---------+---------+-----------+--------+-----------+----------------+ 3 Zeilen im Set, 5 Warnungen (0,00 Sek.) •Die innerste Abfrage scannt 1 Million Zeilen der t_source-Tabelle und verwendet die Dateisortierung, um die abgeleitete Tabelle derived3 zu generieren. Im Vergleich zur obigen Methode 2 bleibt die Gesamtzahl der gescannten Zeilen unverändert und beträgt 2 Millionen Zeilen. Es gibt nur einen kleinen Unterschied. Dieses Mal liegt der automatisch generierte Index in der konstanten Spalte f, während der automatisch generierte Index für den Tabellenjoin in der Spalte item_id liegt. Die Abfragezeit ist also nahezu gleich. Bisher haben wir keine Indizes für die Quelltabelle erstellt. Unabhängig davon, welche Schreibmethode verwendet wird, müssen die Felder created_time und item_name sortiert werden, um nach Duplikaten zu suchen. Daher ist es naheliegend anzunehmen, dass, wenn ein gemeinsamer Index für diese beiden Felder erstellt wird, die geordnete Natur des Indexes selbst verwendet werden kann, um zusätzliche Sortierungen zu vermeiden und so die Abfrageleistung zu verbessern. 2. Erstellen Sie einen gemeinsamen Indexvergleichstest für created_time und item_name- Erstellen Sie einen gemeinsamen Index der Felder „created_time“ und „item_name“. Erstellen Sie den Index idx_sort für t_source(created_time,item_name,item_id); Tabelle t_source analysieren; (1) Verwendung korrelierter Unterabfragen t_target abschneiden; in t_target einfügen wähle different t1.* aus t_source t1, wobei item_id in (Wählen Sie min(item_id) aus t_source t2, wobei t1.created_time=t2.created_time und t1.item_name=t2.item_name); Diesmal dauerte es 19 Sekunden und der Abfrageplan lautet wie folgt: mysql> erklären select distinct t1.* from t_source t1 where item_id in -> (wählen Sie min(item_id) aus t_source t2, wobei t1.created_time=t2.created_time und t1.item_name=t2.item_name); +----+--------------------+-------+------------+------+---------------+-----------+----------+--------+---------+----------------------------------------+--------+----------+--------------+------------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+--------------------+-------+------------+------+---------------+-----------+----------+--------+---------+----------------------------------------+--------+----------+--------------+------------------+ | 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 997281 | 100,00 | Mit „where“; Mit „temporär“ | | 2 | ABHÄNGIGE UNTERABFRAGE | t2 | NULL | ref | idx_sort | idx_sort | 89 | test.t1.created_time,test.t1.item_name | 2 | 100.00 | Index wird verwendet | +----+--------------------+-------+------------+------+---------------+-----------+----------+--------+---------+----------------------------------------+--------+----------+--------------+------------------+ 2 Zeilen im Set, 3 Warnungen (0,00 Sek.) •Die t_source-Tabelle der äußeren Abfrage ist die treibende Tabelle und muss 1 Million Zeilen scannen. •Für die item_id jeder Zeile in der Treibertabelle werden zwei Datenzeilen über den idx_sort-Index abgefragt. (2) Verwenden von Tabellenverknüpfungen t_target abschneiden; in t_target einfügen wähle eindeutig t1.* aus t_source t1, (Wählen Sie min(item_id) item_id,created_time,item_name aus der t_source-Gruppe nach created_time,item_name) t2 wobei t1.item_id = t2.item_id; Diesmal dauerte es 13 Sekunden und der Abfrageplan ist wie folgt: mysql> erkläre select distinct t1.* from t_source t1, -> (wähle min(item_id) item_id,created_time,item_name aus der t_source-Gruppe nach created_time,item_name) t2 -> wobei t1.item_id = t2.item_id; +----+----------+------------+------------+-------+---------------+---------------+------------+---------+-----------------+--------+----------+---------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+----------+------------+------------+-------+---------------+---------------+------------+---------+-----------------+--------+----------+---------------+ | 1 | PRIMARY | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 997281 | 100,00 | Mit „where“; Mit „temporär“ | | 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 5 | test.t1.item_id | 10 | 100,00 | Eindeutig | | 2 | ABGELEITET | t_source | NULL | index | idx_sort | idx_sort | 94 | NULL | 997281 | 100.00 | Index wird verwendet | +----+----------+------------+------------+-------+---------------+---------------+------------+---------+-----------------+--------+----------+---------------+ 3 Zeilen im Satz, 1 Warnung (0,00 Sek.) Im Vergleich zur Verwendung ohne Index müssen bei der Unterabfrage zwar 1 Million Datensatzzeilen gescannt werden, obwohl sie von einem vollständigen Tabellenscan zu einem vollständigen Indexscan wechselt. Daher ist die Verbesserung der Abfrageleistung nicht offensichtlich. (3) Verwendung von Variablen setze @a:='1000-01-01 00:00:00'; setze @b:=' '; setze @f:=0; t_target abschneiden; in t_target einfügen Wählen Sie Artikel-ID, Erstellungszeit, Änderungszeit, Artikelname, Sonstiges aus (wählen Sie t0.*, wenn (@a = Erstellungszeit und @b = Elementname, @f:=0, @f:=1) f, @a:= Erstellungszeit, @b:= Elementname aus (Wählen Sie * aus t_Quelle, sortiert nach Erstellungszeit, Artikelname) t0) t1, wobei f=1; Dieses Mal dauerte es 13 Sekunden und der Abfrageplan war genau derselbe wie ohne Index. Es ist ersichtlich, dass der Index auf diese Schreibmethode keinen Einfluss hat. Ist es möglich, die Verschachtelung zu eliminieren und die Ergebnisse mit nur einer Ebene abzufragen? (4) Verwenden Sie Variablen und vermeiden Sie verschachtelte Abfragen setze @a:='1000-01-01 00:00:00'; setze @b:=' '; t_target abschneiden; in t_target einfügen wähle * aus t_source, erzwinge Index (idx_sort) wobei (@a!=created_time oder @b!=item_name) und (@a:=created_time) nicht null ist und (@b:=item_name) nicht null ist Sortieren nach Erstellungszeit, Artikelname; Diesmal dauerte es 12 Sekunden und der Abfrageplan ist wie folgt: mysql> erklären select * from t_source force index (idx_sort) -> wobei (@a!=created_time oder @b!=item_name) und (@a:=created_time) nicht null ist und (@b:=item_name) nicht null ist -> Sortierung nach Erstellungszeit, Artikelname; +----+----------+----------+---------+-----------+---------------+-----------+----------+----------+---------+---------+----------+----------+-------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+----------+----------+---------+-----------+---------------+-----------+----------+----------+---------+---------+----------+----------+-------------+ | 1 | SIMPLE | t_source | NULL | index | NULL | idx_sort | 94 | NULL | 997281 | 99.00 | Verwenden von „where“ | +----+----------+----------+---------+-----------+---------------+-----------+----------+----------+---------+---------+----------+----------+-------------+ 1 Zeile im Satz, 3 Warnungen (0,00 Sek.) Diese Anweisung weist die folgenden Merkmale auf: •Die verschachtelte Unterabfrage wird eliminiert und es ist nur ein vollständiger Indexscan der t_source-Tabelle erforderlich, sodass der Abfrageplan optimal ist. Vor MySQL 8 war diese Anweisung die beste Lösung für die einfädige Deduplizierung. Eine sorgfältige Analyse dieser Anweisung zeigt, dass sie die logischen Abfrageverarbeitungsschritte und Indexfunktionen von SQL-Anweisungen geschickt nutzt. Die logischen Schritte einer SQL-Abfrage sind: 1. Führen Sie ein kartesisches Produkt durch (Cross Join) Die logischen Ausführungsschritte jeder Abfrageanweisung sind eine Teilmenge dieser 11 Schritte. Nehmen Sie diese Abfrageanweisung als Beispiel. Die Ausführungsreihenfolge ist: Suche nach Datenzeilen über den Index idx_sort erzwingen -> Where-Filter anwenden -> Auswahlliste verarbeiten -> Order-By-Klausel anwenden. Damit die Variablen in der sortierten Reihenfolge von „created_time“ und „item_name“ zugewiesen und verglichen werden können, müssen die Datenzeilen in der Indexreihenfolge gefunden werden. Der Force-Index-Hinweis (idx_sort) spielt hier diese Rolle und muss auf diese Weise geschrieben werden, damit die gesamte Anweisung zur Duplikatsprüfung gültig ist. Andernfalls kann die Reihenfolge der Variablenzuweisung nicht garantiert und die Richtigkeit der Abfrageergebnisse nicht sichergestellt werden, da die Tabelle zuerst gescannt und dann sortiert wird. Die Order-By-Klausel kann ebenfalls nicht ignoriert werden. Andernfalls führt MySQL trotz der Force-Index-Eingabeaufforderung einen vollständigen Tabellenscan anstelle eines vollständigen Indexscans aus, was zu falschen Ergebnissen führt. Der Index stellt außerdem die Reihenfolge von Erstellungszeit und Elementname sicher und vermeidet so eine Dateisortierung. Initialisieren Sie vor dem Start der Abfrageanweisung die Variable auf einen Wert, der in den Daten nicht vorkommen kann, und geben Sie dann die Where-Klausel ein, um von links nach rechts zu beurteilen. Vergleichen Sie zuerst die Variablen- und Feldwerte, weisen Sie dann der Variablen die Werte „created_time“ und „item_name“ dieser Zeile zu und verarbeiten Sie jede Zeile in der Reihenfolge „created_time“ und „item_name“. item_name ist ein Zeichenfolgentyp und (@b:=item_name) ist kein gültiger Boolescher Ausdruck. Daher sollte es als (@b:=item_name) ist nicht null geschrieben werden. Abschließend möchte ich noch anmerken, dass die Schreibweise von " 2. Verwenden von FensterfunktionenDie neuen Fensterfunktionen in MySQL 8 vereinfachen den bislang lästigen Deduplizierungsvorgang erheblich. t_target abschneiden; in t_target einfügen Wählen Sie item_id, Erstellungszeit, Änderungszeit, item_name, Sonstiges von (select *, row_number() über (Partition nach Erstellungszeit, Artikelname) als rn von t_source) t, wobei rn=1; Die Ausführung dieser Anweisung dauert nur 12 Sekunden und der Text ist klar und leicht verständlich. Der Abfrageplan lautet wie folgt: mysql> erklären select item_id, created_time, modified_time, item_name, other -> von (Auswahl *, Zeilennummer() über (Partition nach Erstellungszeit, Elementname) als rn -> von t_source) t, wobei rn=1; +----+-------------+------------+------------+------+---------------+---------------+-------------+---------+---------+-----------+--------+-----------+----------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+-------------+------------+------------+------+---------------+---------------+-------------+---------+---------+-----------+--------+-----------+----------------+ | 1 | PRIMARY | <derived2> | NULL | ref | <auto_key0> | <auto_key0> | 8 | const | 10 | 100,00 | NULL | | 2 | ABGELEITET | t_source | NULL | ALLE | NULL | NULL | NULL | NULL | 997281 | 100,00 | Dateisortierung wird verwendet | +----+-------------+------------+------------+------+---------------+---------------+-------------+---------+---------+-----------+--------+-----------+----------------+ 2 Zeilen im Satz, 2 Warnungen (0,00 Sek.) Diese Abfrage führt einen vollständigen Tabellenscan der Tabelle „t_source“ durch und verwendet Filesort, um die Tabelle nach den Partitionsfeldern „created_time“ und „item_name“ zu sortieren. Die äußere Abfrage behält ein Datenelement von jeder Partition bei. Da alle Daten mit wiederholter Aus dem Ausführungsplan geht hervor, dass die Anweisung zur Deduplizierung der Fensterfunktion nicht so gut ist wie die Variablendeduplizierung verschachtelter Abfragen, aber tatsächlich ist diese Methode die schnellste. Eine Beschreibung der MySQL-Fensterfunktionen finden Sie unter https://dev.mysql.com/doc/refman/8.0/en/window-functions.html. 3. Parallele Ausführung mit mehreren ThreadsDie einzelne Anweisung zur Duplikatsprüfung wurde auf den optimalen Zustand eingestellt, wird jedoch immer noch einfädig ausgeführt. Können wir mehrere Prozessoren nutzen, um Deduplizierungsvorgänge mithilfe mehrerer Threads parallel auszuführen und so die Geschwindigkeit weiter zu erhöhen? Meine experimentelle Umgebung besteht beispielsweise aus 4 Prozessoren. Wenn ich 4 Threads gleichzeitig verwende, um das SQL zur Duplikatsprüfung auszuführen, sollte sich die Leistung theoretisch um fast das Vierfache verbessern. 1. Daten-ShardingBeim Generieren von Testdaten verwendet created_time die Methode, jedem Datensatz eine Sekunde hinzuzufügen, dh der maximale und minimale Zeitunterschied beträgt 500.000 Sekunden und die Daten sind gleichmäßig verteilt, sodass die Daten zunächst in vier Teile aufgeteilt werden. (1) Fragen Sie die erstellten Zeitgrenzwerte der vier Datensätze ab mysql> select date_add('2017-01-01',Intervall 125000 Sekunden) dt1, -> date_add('2017-01-01',Intervall 2*125000 Sekunden) dt2, -> date_add('2017-01-01',Intervall 3*125000 Sekunden) dt3, -> max(Erstellungszeit) dt4 -> von t_source; +---------------------+---------------------+---------------------+---------------------+ | dt1 | dt2 | dt3 | dt4 | +---------------------+---------------------+---------------------+---------------------+ | 02.01.2017 10:43:20 | 03.01.2017 21:26:40 | 05.01.2017 08:10:00 | 06.01.2017 18:53:20 | +---------------------+---------------------+---------------------+---------------------+ 1 Zeile im Satz (0,00 Sek.) (2) Überprüfen Sie die Anzahl der Datensätze in jedem Datensatz, um sicherzustellen, dass die Daten gleichmäßig verteilt sind mysql> Fall auswählen, wenn Erstellungszeit >= '2017-01-01' -> und Erstellungszeit < '2017-01-02 10:43:20' -> dann '2017-01-01' -> wann Erstellungszeit >= '2017-01-02 10:43:20' -> und Erstellungszeit < '2017-01-03 21:26:40' -> dann '2017-01-02 10:43:20' -> wann Erstellungszeit >= '2017-01-03 21:26:40' -> und Erstellungszeit < '2017-01-05 08:10:00' -> dann '2017-01-03 21:26:40' -> sonst '2017-01-05 08:10:00' -> Ende min_dt, -> Fall, wenn Erstellungszeit >= '2017-01-01' -> und Erstellungszeit < '2017-01-02 10:43:20' -> dann '2017-01-02 10:43:20' -> wann Erstellungszeit >= '2017-01-02 10:43:20' -> und Erstellungszeit < '2017-01-03 21:26:40' -> dann '2017-01-03 21:26:40' -> wann Erstellungszeit >= '2017-01-03 21:26:40' -> und Erstellungszeit < '2017-01-05 08:10:00' -> dann '2017-01-05 08:10:00' -> sonst '2017-01-06 18:53:20' -> Ende max_dt, -> zählen(*) -> von t_source -> nach Fall gruppieren, wenn Erstellungszeit >= '2017-01-01' -> und Erstellungszeit < '2017-01-02 10:43:20' -> dann '2017-01-01' -> wann Erstellungszeit >= '2017-01-02 10:43:20' -> und Erstellungszeit < '2017-01-03 21:26:40' -> dann '2017-01-02 10:43:20' -> wann Erstellungszeit >= '2017-01-03 21:26:40' -> und Erstellungszeit < '2017-01-05 08:10:00' -> dann '2017-01-03 21:26:40' -> sonst '2017-01-05 08:10:00' -> Ende, -> Fall, wenn Erstellungszeit >= '2017-01-01' -> und Erstellungszeit < '2017-01-02 10:43:20' -> dann '2017-01-02 10:43:20' -> wann Erstellungszeit >= '2017-01-02 10:43:20' -> und Erstellungszeit < '2017-01-03 21:26:40' -> dann '2017-01-03 21:26:40' -> wann Erstellungszeit >= '2017-01-03 21:26:40' -> und Erstellungszeit < '2017-01-05 08:10:00' -> dann '2017-01-05 08:10:00' -> sonst '2017-01-06 18:53:20' -> Ende; +---------------------+---------+----------+ | min_dt | max_dt | Anzahl(*) | +---------------------+---------+----------+ | 01.01.2017 | 02.01.2017 10:43:20 | 249999 | | 02.01.2017 10:43:20 | 03.01.2017 21:26:40 | 250000 | | 03.01.2017 21:26:40 | 05.01.2017 08:10:00 | 250000 | | 05.01.2017 08:10:00 | 06.01.2017 18:53:20 | 250002 | +---------------------+---------+----------+ 4 Reihen im Satz (4,86 Sek.) Die Vereinigung der vier Datensätze sollte den gesamten Quelldatensatz abdecken und es sollte keine Duplikate zwischen den Daten geben. Das heißt, die Erstellungszeit der vier Datenkopien muss kontinuierlich und gegenseitig ausschließend sein. Kontinuität stellt sicher, dass alle Daten verarbeitet werden, und gegenseitiger Ausschluss stellt sicher, dass keine zweite Duplikatsprüfung erforderlich ist. Tatsächlich ähnelt dies dem Konzept der Zeitbereichspartitionierung. Vielleicht ist es besser, eine partitionierte Tabelle zu verwenden, aber der Schritt zum erneuten Erstellen der Tabelle wird hier ausgelassen. 2. Erstellen Sie eine gespeicherte Prozedur zur Überprüfung auf doppelte InhalteMit den obigen Informationen können wir 4 Anweisungen schreiben, um alle Daten zu verarbeiten. Um die aufrufende Schnittstelle so einfach wie möglich zu gestalten, erstellen Sie die folgende gespeicherte Prozedur. Trennzeichen // Prozedur sp_unique(i smallint) erstellen beginnen setze @a:='1000-01-01 00:00:00'; setze @b:=' '; wenn (i<4) dann in t_target einfügen wähle * aus t_source, erzwinge Index (idx_sort) wobei Erstellungszeit >= Datum_Hinzufügen('2017-01-01',Intervall (i-1)*125000 Sekunden) und Erstellungszeit < Datum_Hinzufügen('2017-01-01',Intervall i*125000 Sekunden) und (@a!=Erstellungszeit oder @b!=Artikelname) und (@a:=created_time) ist nicht null und (@b:=item_name) ist nicht null Sortieren nach Erstellungszeit, Artikelname; anders in t_target einfügen wähle * aus t_source, erzwinge Index (idx_sort) wobei Erstellungszeit >= Datumsadd('2017-01-01',Intervall (i-1)*125000 Sekunden) und Erstellungszeit <= Datum_Hinzufügen('2017-01-01',Intervall i*125000 Sekunden) und (@a!=Erstellungszeit oder @b!=Artikelname) und (@a:=created_time) ist nicht null und (@b:=item_name) ist nicht null Sortieren nach Erstellungszeit, Artikelname; Ende wenn; Ende // Der Ausführungsplan der Abfrageanweisung lautet wie folgt: mysql> erklären select * from t_source force index (idx_sort) -> wobei Erstellungszeit >= Datum_Hinzufügen('2017-01-01',Intervall (1-1)*125000 Sekunden) -> und Erstellungszeit < date_add('2017-01-01',Intervall 1*125000 Sekunden) -> und (@a!=Erstellungszeit oder @b!=Artikelname) -> und (@a:=created_time) ist nicht null -> und (@b:=item_name) ist nicht null -> Sortierung nach Erstellungszeit, Artikelname; +----+----------+----------+---------+-----------+---------------+-----------+----------+---------+---------+---------+----------+----------+----------+----------------------+ | ID | Auswahltyp | Tabelle | Partitionen | Typ | mögliche Schlüssel | Schlüssel | Schlüssellänge | Ref. | Zeilen | gefiltert | Extra | +----+----------+----------+---------+-----------+---------------+-----------+----------+---------+---------+---------+----------+----------+----------+----------------------+ | 1 | SIMPLE | t_source | NULL | Bereich | idx_sort | idx_sort | 6 | NULL | 498640 | 100,00 | Indexbedingung wird verwendet | +----+----------+----------+---------+-----------+---------------+-----------+----------+---------+---------+---------+----------+----------+----------+-----------------------+ 1 Zeile im Satz, 3 Warnungen (0,00 Sek.) Der MySQL-Optimierer führt Indexbereichsscans durch und verwendet Index Condition Pushdown (ICP), um Abfragen zu optimieren. 3. Parallele AusführungIm Folgenden werden Shell-Hintergrundprozesse und MySQL Schedule Event verwendet, um Parallelität zu erreichen. (1) Shell-Hintergrundprozess •Erstellen Sie die Datei duplicate_removal.sh mit folgendem Inhalt: #!/bin/bash mysql -vvv -u root -p123456 test -e "truncate t_target" &>/dev/null Datum '+%H:%M:%S' für y in {1..4} Tun sql="rufe sp_unique($y) auf" mysql -vvv -u root -p123456 test -e "$sql" &>par_sql1_$y.log & Erledigt Warten Datum '+%H:%M:%S' • Ausführen von Skriptdateien Die Ausführungsausgabe ist wie folgt: [mysql@hdp2~]$./duplicate_removal.sh 14:27:30 14:27:35 Diese Methode dauert 5 Sekunden und die 4 parallel ausgeführten Prozessaufrufe dauern jeweils 4,87 Sekunden, 4,88 Sekunden, 4,91 Sekunden und 4,73 Sekunden: [mysql@hdp2~]$cat par_sql1_1.log | sed '/^$/d' mysql: [Warnung] Die Verwendung eines Passworts in der Befehlszeilenschnittstelle kann unsicher sein. -------------- rufe sp_unique(1) auf -------------- Abfrage OK, 124999 Zeilen betroffen (4,87 Sek.) Tschüss [mysql@hdp2~]$cat par_sql1_2.log | sed '/^$/d' mysql: [Warnung] Die Verwendung eines Passworts in der Befehlszeilenschnittstelle kann unsicher sein. -------------- rufe sp_unique(2) auf -------------- Abfrage OK, 125.000 Zeilen betroffen (4,88 Sek.) Tschüss [mysql@hdp2~]$cat par_sql1_3.log | sed '/^$/d' mysql: [Warnung] Die Verwendung eines Passworts in der Befehlszeilenschnittstelle kann unsicher sein. -------------- rufe sp_unique(3) auf -------------- Abfrage OK, 125.000 Zeilen betroffen (4,91 Sek.) Tschüss [mysql@hdp2~]$cat par_sql1_4.log | sed '/^$/d' mysql: [Warnung] Die Verwendung eines Passworts in der Befehlszeilenschnittstelle kann unsicher sein. -------------- rufe sp_unique(4) auf -------------- Abfrage OK, 125001 Zeilen betroffen (4,73 Sek.) Tschüss [mysql@hdp2~]$ Es ist ersichtlich, dass die Ausführungszeit jedes Prozesses 4,85 Sekunden beträgt. Da er parallel ausgeführt wird, beträgt die Gesamtausführungszeit des Prozesses die langsamste von 4,91 Sekunden, was 2,5-mal schneller ist als die Single-Thread-Geschwindigkeit. (2) MySQL-Schedule-Ereignis • Ereignisverlaufsprotokolltabelle erstellen -- Wird verwendet, um die Ausführungszeit des Ereignisses und andere Informationen anzuzeigen. Erstellen Sie die Tabelle t_event_history ( dbname varchar(128) nicht null Standard '', eventname varchar(128) nicht null Standard '', Startzeit datetime(3) ungleich null Standard '1000-01-01 00:00:00', Endzeit datetime(3) Standard null, issuccess int(11) Standard null, Dauer int(11) Standard null, Fehlermeldung varchar(512) Standard null, randno int(11) Standard null ); • Erstellen Sie ein Ereignis für jeden gleichzeitigen Thread Trennzeichen // Ereignis ev1 nach Zeitplan erstellen bei current_timestamp + Intervall 1 Stunde nach Abschluss beibehalten deaktivieren do beginnen Deklarieren Sie r_code char(5) als Standard „00000“; Deklariere r_msg-Text; Deklarieren Sie v_error als Ganzzahl; Deklariere v_starttime datetime jetzt als Standard (3); Deklarieren Sie v_randno als ganzzahligen Standard-Floor (rand()*100001); einfügen in t_event_history (Datenbankname, Ereignisname, Startzeit, Zufallszahl) #Jobnamewerte(Datenbank(),'ev1', v_Startzeit,v_Randnummer); beginnen #Abschnitt zur Ausnahmebehandlung deklariert den Continue-Handler für SQLexeption beginnen setze v_error = 1; Diagnosezustand 1 abrufen, r_code = returned_sqlstate, r_msg = message_text; Ende; #Dies ist die eigentliche Benutzerprogrammprozedur mit dem Namen call sp_unique(1); Ende; Aktualisiere t_event_history, setze Endzeit=jetzt(3),issuccess=isnull(v_error),Dauer=Zeitstempeldiff(Mikrosekunde,Startzeit,jetzt(3)),Fehlernachricht=concat('Fehler=',r_code,',Nachricht=',r_msg),randno=null, wobei Startzeit=v_starttime und Randno=v_randno; Ende // erstelle Ereignis ev2 nach Zeitplan bei current_timestamp + Intervall 1 Stunde nach Abschluss beibehalten deaktivieren do beginnen Deklarieren Sie r_code char(5) als Standard „00000“; Deklariere r_msg-Text; Deklarieren Sie v_error als Ganzzahl; Deklariere v_starttime datetime jetzt als Standard (3); Deklarieren Sie v_randno als ganzzahligen Standard-Floor (rand()*100001); einfügen in t_event_history (Datenbankname, Ereignisname, Startzeit, Zufallszahl) #Jobnamewerte(Datenbank(),'ev2', v_Startzeit, v_Randnummer); beginnen #Abschnitt zur Ausnahmebehandlung deklariert den Continue-Handler für SQLexeption beginnen setze v_error = 1; Diagnosezustand 1 abrufen, r_code = returned_sqlstate, r_msg = message_text; Ende; #Dies ist die eigentliche Benutzerprogrammprozedur mit dem Namen call sp_unique(2); Ende; Aktualisiere t_event_history, setze Endzeit=jetzt(3),issuccess=isnull(v_error),Dauer=Zeitstempeldiff(Mikrosekunde,Startzeit,jetzt(3)),Fehlernachricht=concat('Fehler=',r_code,',Nachricht=',r_msg),randno=null, wobei Startzeit=v_starttime und Randno=v_randno; Ende // erstelle Ereignis ev3 nach Zeitplan bei current_timestamp + Intervall 1 Stunde nach Abschluss beibehalten deaktivieren do beginnen Deklarieren Sie r_code char(5) als Standard „00000“; Deklariere r_msg-Text; Deklarieren Sie v_error als Ganzzahl; Deklariere v_starttime datetime jetzt als Standard (3); Deklarieren Sie v_randno als ganzzahligen Standard-Floor (rand()*100001); einfügen in t_event_history (Datenbankname, Ereignisname, Startzeit, Zufallszahl) #Jobnamewerte(Datenbank(),'ev3', v_Startzeit,v_Randnummer); beginnen #Abschnitt zur Ausnahmebehandlung deklariert den Continue-Handler für SQLexeption beginnen setze v_error = 1; Diagnosezustand 1 abrufen, r_code = returned_sqlstate, r_msg = message_text; Ende; #Dies ist die eigentliche Benutzerprogrammprozedur mit dem Namen call sp_unique(3); Ende; Aktualisiere t_event_history, setze Endzeit=jetzt(3),issuccess=isnull(v_error),Dauer=Zeitstempeldiff(Mikrosekunde,Startzeit,jetzt(3)),Fehlernachricht=concat('Fehler=',r_code,',Nachricht=',r_msg),randno=null, wobei Startzeit=v_starttime und Randno=v_randno; Ende // Ereignis ev4 nach Zeitplan erstellen bei current_timestamp + Intervall 1 Stunde nach Abschluss beibehalten deaktivieren do beginnen Deklarieren Sie r_code char(5) als Standard „00000“; Deklariere r_msg-Text; Deklarieren Sie v_error als Ganzzahl; Deklariere v_starttime datetime jetzt als Standard (3); Deklarieren Sie v_randno als ganzzahligen Standardwert für floor(rand()*100001); einfügen in t_event_history (Datenbankname, Ereignisname, Startzeit, Zufallszahl) #Jobnamewerte(Datenbank(),'ev4', v_Startzeit, v_Randnummer); beginnen #Abschnitt zur Ausnahmebehandlung deklariert den Continue-Handler für SQLexeption beginnen setze v_error = 1; Diagnosezustand 1 abrufen, r_code = returned_sqlstate, r_msg = message_text; Ende; #Dies ist die eigentliche Benutzerprogrammprozedur mit dem Namen call sp_unique(4); Ende; Aktualisiere t_event_history, setze Endzeit=jetzt(3),issuccess=isnull(v_error),Dauer=Zeitstempeldiff(Mikrosekunde,Startzeit,jetzt(3)),Fehlernachricht=concat('Fehler=',r_code,',Nachricht=',r_msg),randno=null, wobei Startzeit=v_starttime und Randno=v_randno; Ende // Um die Ausführungszeit jedes Ereignisses aufzuzeichnen, wird der Ereignisdefinition die Logik der Operationsprotokolltabelle hinzugefügt. Da in jedem Ereignis nur ein Einfügen und ein Aktualisieren ausgeführt werden, werden für die 4 Ereignisse insgesamt 8 sehr einfache Anweisungen ausgeführt, und die Auswirkungen auf den Test sind vernachlässigbar. Die Ausführungszeit ist auf Millisekunden genau. • Ausführung von Triggerereignissen mysql -vvv -u root -p123456 test -e "t_target abschneiden; Ereignis ev1 planmäßig am aktuellen Zeitstempel ändern aktivieren; Ereignis ev2 planmäßig am aktuellen Zeitstempel ändern aktivieren; Ereignis ev3 planmäßig am aktuellen Zeitstempel ändern aktivieren; Ereignis ev4 planmäßig am aktuellen Zeitstempel ändern aktivieren;" Diese Befehlszeile löst vier Ereignisse nacheinander aus, wartet jedoch nicht, bis das vorherige abgeschlossen ist, bevor das nächste ausgeführt wird. Stattdessen wird das nächste sofort ausgeführt. Dies ist aus der Ausgabe des Befehls deutlich ersichtlich: [mysql@hdp2~]$mysql -vvv -u root -p123456 test -e "truncate t_target;ändere Ereignis ev1 planmäßig am aktuellen Zeitstempel, aktivieren;ändere Ereignis ev2 planmäßig am aktuellen Zeitstempel, aktivieren;ändere Ereignis ev3 planmäßig am aktuellen Zeitstempel, aktivieren;ändere Ereignis ev4 planmäßig am aktuellen Zeitstempel, aktivieren;" mysql: [Warnung] Die Verwendung eines Passworts in der Befehlszeilenschnittstelle kann unsicher sein. -------------- t_target abschneiden -------------- Abfrage OK, 0 Zeilen betroffen (0,06 Sek.) -------------- Ändern Sie das Ereignis ev1 planmäßig um current_timestamp, aktivieren Sie -------------- Abfrage OK, 0 Zeilen betroffen (0,02 Sek.) -------------- Ereignis ev2 planmäßig um current_timestamp ändern aktivieren -------------- Abfrage OK, 0 Zeilen betroffen (0,00 Sek.) -------------- Ereignis ev3 planmäßig um current_timestamp ändern aktivieren -------------- Abfrage OK, 0 Zeilen betroffen (0,02 Sek.) -------------- Ereignis ev4 planmäßig am aktuellen Zeitstempel ändern, aktivieren -------------- Abfrage OK, 0 Zeilen betroffen (0,00 Sek.) Tschüss [mysql@hdp2~]$ • Ereignisausführungsprotokoll anzeigen mysql> wähle * aus test.t_event_history; +--------+-----------+-------------------------+-------------------------+-----------+---------+-----------+--------------+--------+ | +--------+-----------+-------------------------+-----------------------------------------------+----------+--------------+--------+ | | | | +--------+-----------+-------------------------+-----------------------------------------------+----------+--------------+--------+ 4 Zeilen im Satz (0,00 Sek.) Es ist zu erkennen, dass jeder Prozess 4,83 Sekunden dauert, da sie parallel ausgeführt werden, die Gesamtausführungszeit ist die langsamste 5,3 Sekunden. Zusammenfassen Die oben genannte Betriebsmethode, die ich Ihnen vorgestellt habe, um den MySQL -Deduplizierungsvorgang auf das Extrem zu optimieren. Ich möchte auch allen für ihre Unterstützung der Website 123WORDPRESS.COM danken! Das könnte Sie auch interessieren:
|
<<: So ändern Sie den Hostnamen in Linux
>>: Vue realisiert Click-Flip-Effekt
Die Reihenfolge, in der der Browser HTML lädt und ...
Das Docker-Repository selbst ist sehr langsam, ab...
Inhaltsverzeichnis Vorwort Start Schritt Fehlerbe...
Der folgende Befehl wird häufig verwendet: chmod ...
Verwenden Sie natives JS, um einen einfachen Rech...
Wie installiere ich Nginx an einem bestimmten Ort...
Inhaltsverzeichnis Primärschlüsseleinschränkung E...
In diesem Artikel werden einige der Techniken ausf...
Die Datenbank ist wie das Betriebssystem eine gem...
In diesem Artikel wird das grafische Tutorial zur...
Als ich mir heute die Laborprojekte ansah, stieß ...
Vorwort Der Ubuntu-Server des Unternehmens platzi...
Beschränken Sie input Eingabefeld auf reine Zahle...
<div ausrichten="zentrieren"> <...
Das Zählen der Nummer einer Zeichenfolge in einer...