Dies ist meine langsame Frage:
SELECT `products_counts`.`cid`
FROM
`products_counts` `products_counts`
LEFT OUTER JOIN `products` `products` ON (
`products_counts`.`product_id` = `products`.`id`
)
LEFT OUTER JOIN `trademarks` `trademark` ON (
`products`.`trademark_id` = `trademark`.`id`
)
LEFT OUTER JOIN `suppliers` `supplier` ON (
`products_counts`.`supplier_id` = `supplier`.`id`
)
WHERE
`products_counts`.product_id IN
(159, 572, 1075, 1102, 1145, 1162, 1660, 2355, 2356, 2357, 3236, 6471, 6472, 6473, 8779, 9043, 9095, 9336, 9337, 9338, 9445, 10198, 10966, 10967, 10974, 11124, 11168, 16387, 16689, 16827, 17689, 17920, 17938, 17946, 17957, 21341, 21352, 21420, 21421, 21429, 21544, 27944, 27988, 30194, 30196, 30230, 30278, 30699, 31306, 31340, 32625, 34021, 34047, 38043, 43743, 48639, 48720, 52453, 55667, 56847, 57478, 58034, 61477, 62301, 65983, 66013, 66181, 66197, 66204, 66407, 66844, 66879, 67308, 68637, 73944, 74037, 74060, 77502, 90963, 101630, 101900, 101977, 101985, 101987, 105906, 108112, 123839, 126316, 135156, 135184, 138903, 142755, 143046, 143193, 143247, 144054, 150164, 150406, 154001, 154546, 157998, 159896, 161695, 163367, 170173, 172257, 172732, 173581, 174001, 175126, 181900, 182168, 182342, 182858, 182976, 183706, 183902, 183936, 184939, 185744, 287831, 362832, 363923, 7083107, 7173092, 7342593, 7342594, 7342595, 7728766)
ORDER BY
products_counts.inflow ASC,
supplier.delivery_period ASC,
trademark.sort DESC,
trademark.name ASC
LIMIT
0, 3;
Die durchschnittliche Abfragezeit in meinem Datensatz beträgt 4,5 Sekunden. Dies ist nicht akzeptabel.
Lösungen, die ich sehe:
Fügen Sie alle Spalten aus der order-Klausel zur products_counts
Tabelle hinzu. Aber ich habe ~ 10 Auftragstypen in der Anwendung, also sollte ich viele Spalten und Indizes erstellen. Außerdem habe products_counts
ich sehr intensive Updates / Einfügungen / Löschungen, so dass ich sofort alle auftragsbezogenen Spalten aktualisieren muss (mit Triggern?).
Gibt es eine andere Lösung?
Erklären:
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | products_counts | range | product_id_supplier_id,product_id,pid_count | product_id_supplier_id | 4 | NULL | 227 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | products | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.product_id | 1 | |
| 1 | SIMPLE | trademark | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products.trademark_id | 1 | |
| 1 | SIMPLE | supplier | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.supplier_id | 1 | |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
Tabellenstruktur:
CREATE TABLE `products_counts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) unsigned NOT NULL,
`supplier_id` int(11) unsigned NOT NULL,
`count` int(11) unsigned NOT NULL,
`cid` varchar(64) NOT NULL,
`inflow` varchar(10) NOT NULL,
`for_delete` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `cid` (`cid`),
UNIQUE KEY `product_id_supplier_id` (`product_id`,`supplier_id`),
KEY `product_id` (`product_id`),
KEY `count` (`count`),
KEY `pid_count` (`product_id`,`count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`category_id` int(11) unsigned NOT NULL,
`trademark_id` int(11) unsigned NOT NULL,
`photo` varchar(255) NOT NULL,
`sort` int(11) unsigned NOT NULL,
`otech` tinyint(1) unsigned NOT NULL,
`not_liquid` tinyint(1) unsigned NOT NULL DEFAULT '0',
`applicable` varchar(255) NOT NULL,
`code_main` varchar(64) NOT NULL,
`code_searchable` varchar(128) NOT NULL,
`total` int(11) unsigned NOT NULL,
`slider` int(11) unsigned NOT NULL,
`slider_title` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`),
KEY `category_id` (`category_id`),
KEY `trademark_id` (`trademark_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `trademarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`country_id` int(11) NOT NULL,
`sort` int(11) unsigned NOT NULL DEFAULT '0',
`sort_list` int(10) unsigned NOT NULL DEFAULT '0',
`is_featured` tinyint(1) unsigned NOT NULL,
`is_direct` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `suppliers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`code` varchar(64) NOT NULL,
`name` varchar(255) NOT NULL,
`delivery_period` tinyint(1) unsigned NOT NULL,
`is_default` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Informationen zum MySQL-Server:
mysqld Ver 5.5.45-1+deb.sury.org~trusty+1 for debian-linux-gnu on i686 ((Ubuntu))
(inflow, product_id)
?innodb_buffer_pool_size
. Normalerweise sind etwa 70% des verfügbaren Arbeitsspeichers gut.Antworten:
Das Überprüfen Ihrer Tabellendefinitionen zeigt, dass die Indizes für alle beteiligten Tabellen übereinstimmen. Dies sollte dazu führen, dass die
MySQL's
Verknüpfungen innerhalb der Grenzen der Verknüpfungslogik so schnell wie möglich erfolgen .Das Sortieren aus mehreren Tabellen ist jedoch komplexer.
2007 beschrieb Sergey Petrunia die 3
MySQL
Sortieralgorithmen in der Reihenfolge ihrer Geschwindigkeit fürMySQL
: http://s.petrunia.net/blog/?m=201407filesort()
auf der 1. nicht konstanten Tabellefilesort()
esAnhand der oben gezeigten Tabellendefinitionen und Verknüpfungen können Sie erkennen, dass Sie niemals die schnellste Sortierung erhalten . Das bedeutet, dass Sie für die von Ihnen verwendeten Sortierkriterien abhängig
filesort()
sind.Wenn Sie jedoch eine materialisierte Ansicht entwerfen und verwenden , können Sie den schnellsten Sortieralgorithmus verwenden.
Für
MySQL 5.5
(in diesem Beispiel) zu erhöhenORDER BY
Geschwindigkeit , wenn Sie nicht bekommen könnenMySQL
Indizes eher zu verwenden , als eine zusätzliche Sortierphase, versuchen Sie die folgenden Strategien:• Erhöhen Sie den
sort_buffer_size
Variablenwert.• Erhöhen Sie den
read_rnd_buffer_size
Variablenwert.• Verwenden Sie weniger RAM pro Zeile, indem Sie Spalten nur so groß deklarieren, wie es für die zu speichernden tatsächlichen Werte erforderlich ist. [ZB Reduzieren Sie einen Varchar (256) auf Varchar (ActualLongestString)]
• Ändern Sie die
tmpdir
Systemvariable so, dass sie auf ein dediziertes Dateisystem mit viel freiem Speicherplatz verweist. (Weitere Details finden Sie unter dem obigen Link.)Materialisierte Ansichten - Ein anderer Ansatz zum Sortieren verbundener Tabellen
Sie haben mit Ihrer Frage zur Verwendung von Triggern auf materialisierte Ansichten angespielt. MySQL verfügt nicht über integrierte Funktionen zum Erstellen einer materialisierten Ansicht , Sie verfügen jedoch über die erforderlichen Tools. Durch die Verwendung von Triggern zum Verteilen der Last können Sie die materialisierte Ansicht auf dem neuesten Stand halten.
Die materialisierte Ansicht ist eigentlich eine Tabelle, die durch Prozedurcode zum Erstellen oder Neuerstellen der materialisierten Ansicht gefüllt und von Triggern verwaltet wird , um die Daten auf dem neuesten Stand zu halten.
Da Sie eine Tabelle mit einem Index erstellen, kann die materialisierte Ansicht bei Abfrage die schnellste Sortiermethode verwenden : Verwenden Sie eine indexbasierte Zugriffsmethode, die eine geordnete Ausgabe erzeugt
Da
MySQL 5.5
zum Verwalten einer materialisierten Ansicht Trigger verwendet werden, benötigen Sie auch einen Prozess, ein Skript oder eine gespeicherte Prozedur, um die anfängliche materialisierte Ansicht zu erstellen .Dies ist jedoch offensichtlich ein zu schwerer Prozess, um nach jedem Update der Basistabellen, in denen Sie die Daten verwalten, ausgeführt zu werden. Hier kommen die Trigger ins Spiel, um die Daten bei Änderungen auf dem neuesten Stand zu halten. Auf diese Weise jeder
insert
,update
unddelete
ihre Änderungen propagieren, indem Sie Ihre Trigger, auf die materialisierte Ansicht .Die FROMDUAL-Organisation unter http://www.fromdual.com/ verfügt über Beispielcode zum Verwalten einer materialisierten Ansicht . Anstatt meine eigenen Samples zu schreiben, werde ich Sie auf ihre Samples verweisen:
http://www.fromdual.com/mysql-materialized-views
Beispiel 1: Erstellen einer materialisierten Ansicht
Dadurch erhalten Sie die materialisierte Ansicht zum Zeitpunkt der Aktualisierung. Da Sie jedoch über eine sich schnell bewegende Datenbank verfügen, möchten Sie diese Ansicht auch so aktuell wie möglich halten.
Daher müssen die betroffenen Basisdatentabellen Trigger haben, um die Änderungen von einer Basistabelle in die Tabelle Materialized View zu übertragen . Als ein Beispiel:
Beispiel 2: Einfügen neuer Daten in eine materialisierte Ansicht
Natürlich benötigen Sie auch Trigger, um das Löschen von Daten aus einer materialisierten Ansicht und das Aktualisieren von Daten in einer materialisierten Ansicht aufrechtzuerhalten . Auch für diese Trigger stehen Beispiele zur Verfügung.
SCHLIESSLICH: Wie macht das Sortieren verbundener Tabellen schneller?
Die materialisierte Ansicht wird ständig erstellt, während die Aktualisierungen daran vorgenommen werden. Daher können Sie den Index (oder die Indizes ) definieren, die Sie zum Sortieren der Daten in der materialisierten Ansicht oder Tabelle verwenden möchten .
Wenn der Aufwand für die Pflege der Daten nicht zu hoch ist, müssen Sie für jede relevante Datenänderung einige Ressourcen (CPU / E / A / usw.) aufwenden, um die materialisierte Ansicht zu erhalten. Daher sind die Indexdaten aktuell und leicht verfügbar. Daher ist die Auswahl schneller, da Sie:
Abhängig von Ihren Umständen und Ihrer Meinung zum Gesamtprozess möchten Sie die materialisierten Ansichten möglicherweise jede Nacht in einem langsamen Zeitraum neu erstellen.
quelle
Hier gibt es nicht viel zu tun, aber ich vermute, das Hauptproblem ist, dass Sie jedes Mal eine ziemlich große temporäre Tabelle erstellen und die Datei auf der Festplatte sortieren. Der Grund ist:
Dies bedeutet, dass Ihre temporäre Tabelle und Sortierdatei ziemlich groß sein kann, da beim Erstellen der temporären Tabelle die Felder mit der maximalen Länge erstellt werden und beim Sortieren der Datensätze alle mit der maximalen Länge (und UTF8 3 Bytes pro Zeichen). Diese schließen wahrscheinlich auch die Verwendung einer temporären In-Memory-Tabelle aus. Weitere Informationen finden Sie unter Details zu internen temporären Tabellen .
Das LIMIT nützt uns auch hier nichts, da wir die gesamte Ergebnismenge materialisieren und ordnen müssen, bevor wir die ersten drei Zeilen kennen.
Haben Sie versucht, Ihr tmpdir in ein tmpfs- Dateisystem zu verschieben? Wenn / tmp nicht bereits tmpfs verwendet (MySQL verwendet
tmpdir=/tmp
standardmäßig * nix), können Sie / dev / shm direkt verwenden. In Ihrer my.cnf-Datei:Dann müssten Sie mysqld neu starten.
Das könnte einen großen Unterschied machen. Wenn Sie wahrscheinlich unter Speicherdruck auf dem System geraten, möchten Sie wahrscheinlich die Größe begrenzen (normalerweise beschränken Linux-Distributionen tmpfs standardmäßig auf 50% des gesamten Arbeitsspeichers), um zu vermeiden, dass Speichersegmente auf die Festplatte oder sogar ausgelagert werden schlimmer eine OOM-Situation . Sie können dies tun, indem Sie die Zeile in bearbeiten
/etc/fstab
:Sie können die Größe auch "online" ändern. Beispielsweise:
Sie können auch ein Upgrade auf MySQL 5.6 durchführen, das performante Unterabfragen und abgeleitete Tabellen enthält, und ein bisschen mehr mit der Abfrage herumspielen. Ich glaube nicht, dass wir auf diesem Weg große Gewinne sehen werden, so wie ich es sehe.
Viel Glück!
quelle