2016-07-14 10 views
2

Ich habe die Ergebnisse von einem Algorithmus geschrieben, der Abstände zwischen Kunden in einer InnoDB-Tabelle berechnet. Zum Beispiel, wenn meine Kunden A, B, C und D in der Datenbank die Tabelle sieht wie folgt aus, unter anderem Spalten:In diesem Fall ist MyISAM dramatisch schneller als InnoDB in mysql

From | To | Distance 
    A  B  344 
    A  C  274 
    A  D  182 
    B  C  338 

Und so weiter ... Es ist eine Menge von Zeilen denke ich, werde ich Treffer 50 Millionen.

Die anderen Spalten sind product_type und value. Diese sagen mir, wie viel der Kunde B (customer_to in den Spalten) dieses product_type kauft. Das bedeutet, dass ich jedes Paar mehrere Male habe, abhängig davon, wie viele Produkttypen der Kunde B kauft.

Ich musste zu einer Abfrage, um jeden Kunden mit den Produkten zu gruppieren, die seine Nachbarn kaufen und den Wert. Die Abfrage sieht so aus:

select customer_from, product_type, avg(value) as opportunity 
from customer_distances 
where distance < 500 
group by customer_from, product_type 
order by opportunity desc; 

Die innodb Tabelle konnte mir diese Abfrage nicht beantworten. Obwohl ich net_read_timeout auf 28800 änderte, ging die mysql-Verbindung während der Abfrage verloren.

Ich hatte es schwer mit Innodb Build für Transaktionsverarbeitung und nicht für intensive Abfragen zu tun. Also habe ich eine neue Tabelle mit MyIsam als Engine erstellt und füge alle Datensätze aus der innodb-Tabelle ein.

Wie erwartet, war die Auswahl sehr schnell (70 segs) und alle anderen wählt wie count (distinct customer_from), wo fast sofort.

Nur aus Neugier habe ich versucht, den Prozess der Einfügung der Abstände in der Myisam-Tabelle fortzusetzen. Es war eine Überraschung für mich, als das Programm mindestens 100-mal schneller lief als bei der Arbeit am innodb-Tisch - für INSERTS!

Für jeden Kunden fügt das Programm etwa 3000 Zeilen ein (eine für jeden Nachbarn für jeden product_type. Etwas wie 300 Nachbarn und 10 product_types pro Kunde). Mit dem innodb-Tisch hat das Einfügen eines einzelnen Kunden zwischen 40 und 60 Sekunden (ca. 3000 Zeilen) gedauert. Mit der Myisam-Tabelle dauert es 1 Sekunde, um 3 Kunden einzufügen (9000 Zeilen aprox).

Einige zusätzliche Informationen:

  • Die MySQL-Datenbank in meinem PC ist (localhost).
  • Das Programm in Java geschrieben und läuft von meinem PC.
  • Ich verwende vorbereitete Anweisungen und ich ändere nur die Daten zwischen jeder Reihe und der nächsten. Dies wird auf diese Frage im Zusammenhang Why is myisam storage engine is faster than Innodb storage engine

Also zusammenfassend die Frage ist: Warum ist MyISAM, die schnell mit Insert-Anweisungen? Was denkst du?

EDIT 1: Ich bin die create-Anweisungen für beide Tabellen hinzufügen, die Innodb und Myisam. EDIT 2: Ich habe einige unbrauchbare Informationen gelöscht und ein bisschen hier und da formatiert.

/* INNODB TABLE */ 
CREATE TABLE `customer_distances` (
    `customer_from` varchar(50) NOT NULL, 
    `customer_from_type` varchar(50) DEFAULT NULL, 
    `customer_from_segment` varchar(50) DEFAULT NULL, 
    `customer_from_district` int(11) DEFAULT NULL, 
    `customer_from_zone` int(11) DEFAULT NULL, 
    `customer_from_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_from_latitud` decimal(15,6) DEFAULT NULL, 
    `customer_to` varchar(50) NOT NULL, 
    `customer_to_type` varchar(50) DEFAULT NULL, 
    `customer_to_segment` varchar(50) DEFAULT NULL, 
    `customer_to_district` int(11) DEFAULT NULL, 
    `customer_to_zone` int(11) DEFAULT NULL, 
    `customer_to_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_to_latitud` decimal(15,6) DEFAULT NULL, 
    `distance` decimal(10,2) DEFAULT NULL, 
    `product_business_line` varchar(50) DEFAULT NULL, 
    `product_type` varchar(50) NOT NULL, 
    `customer_from_liters` decimal(10,2) DEFAULT NULL, 
    `customer_from_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_from_units` decimal(10,2) DEFAULT NULL, 
    `customer_to_liters` decimal(10,2) DEFAULT NULL, 
    `customer_to_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_to_units` decimal(10,2) DEFAULT NULL, 
    `liters_opportunity` decimal(10,2) DEFAULT NULL, 
    `dollars_opportunity` decimal(10,2) DEFAULT NULL, 
    `units_oportunity` decimal(10,2) DEFAULT NULL, 
    PRIMARY KEY (`cliente_desde`,`cliente_hasta`,`grupo`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

/* MYISAM TABLE */ 
CREATE TABLE `customer_distances` (
    `customer_from` varchar(50) NOT NULL, 
    `customer_from_type` varchar(50) DEFAULT NULL, 
    `customer_from_segment` varchar(50) DEFAULT NULL, 
    `customer_from_district` int(11) DEFAULT NULL, 
    `customer_from_zone` int(11) DEFAULT NULL, 
    `customer_from_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_from_latitud` decimal(15,6) DEFAULT NULL, 
    `customer_to` varchar(50) NOT NULL, 
    `customer_to_type` varchar(50) DEFAULT NULL, 
    `customer_to_segment` varchar(50) DEFAULT NULL, 
    `customer_to_district` int(11) DEFAULT NULL, 
    `customer_to_zone` int(11) DEFAULT NULL, 
    `customer_to_longitud` decimal(15,6) DEFAULT NULL, 
    `customer_to_latitud` decimal(15,6) DEFAULT NULL, 
    `distance` decimal(10,2) DEFAULT NULL, 
    `product_business_line` varchar(50) DEFAULT NULL, 
    `product_type` varchar(50) NOT NULL, 
    `customer_from_liters` decimal(10,2) DEFAULT NULL, 
    `customer_from_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_from_units` decimal(10,2) DEFAULT NULL, 
    `customer_to_liters` decimal(10,2) DEFAULT NULL, 
    `customer_to_dollars` decimal(10,2) DEFAULT NULL, 
    `customer_to_units` decimal(10,2) DEFAULT NULL, 
    `liters_opportunity` decimal(10,2) DEFAULT NULL, 
    `dollars_opportunity` decimal(10,2) DEFAULT NULL, 
    `units_oportunity` decimal(10,2) DEFAULT NULL, 
    PRIMARY KEY (`cliente_desde`,`cliente_hasta`,`grupo`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 
+0

Waren die Tabellen ansonsten identisch (Indizes, etc ...)? Außerdem gibt es separate Servereinstellungen (z. B. Speichercachegrößen) für die Engines, die die Leistung beeinträchtigen könnten. – Uueerdo

+0

Sie sollten die Produktverkaufsdaten normalisieren. Die Tabelle macht keinen Sinn, wie sie derzeit entworfen ist. – EJP

+0

Identische Tabellen, gleiche Server. –

Antwort

3

Einsätze

  • InnoDB standardmäßig sofort jede INSERT "verpflichtet". Dies kann durch Verklumpen von 100-1000 Reihen gleichzeitig behoben werden.
  • Dosiereinsätze beschleunigen sowohl MyISAM als auch InnoDB - vielleicht um das 10-fache.
  • Erfahren Sie mehr über autocommit und BEGIN..COMMIT.

Select

  • InnoDB verbraucht mehr Speicherplatz als MyISAM - in der Regel 2x-3x; Dies wirkt sich auf Tabellen-Scans aus, bei denen es sich wahrscheinlich um
  • handelt. Für diese Abfrage würde ein zusammengesetzter Index für (customer_from, product_type, distance) wahrscheinlich beiden Engines helfen.

Tuning

  • Wenn MyISAM nur ausgeführt wird, legen key_buffer_size-20% RAM und innodb_buffer_pool_size=0.
  • Wenn nur InnoDB ausgeführt wird, setzen Sie key_buffer_size auf nur 10M und innodb_buffer_pool_size auf 70% des RAM.

Normalisierungs und Platz sparend

  • Kleiner -> mehr zwischenspeicherbar -> weniger E/A -> schneller (entweder Motor)
  • DECIMAL(10,2) ist nicht die beste in den meisten Fälle. Betrachten Sie FLOAT für Nicht-Geld (wie distance). Berücksichtige weniger Ziffern; das verarbeitet bis 99.999.999,99 und benötigt 5 Bytes.
  • Es ist normalerweise keine gute Idee, replizierte Spalten zu haben, wie die 10 Spalten customer_from und customer_to. Haben Sie eine Customers Tabelle, mit beiden drin.
  • Ihre Latitud und Longitud sind 7 Bytes und haben eine unnötige Auflösung. Empfehlen Sie latidud DECIMAL(6,4) und longitud (7,4), für einen insgesamt von 7 Bytes. (Diese geben 16m/52ft Auflösung.)

Ergebnis

Nach diesen Vorschlägen, die 50M-row Tabelle wird sehr viel kleiner, und läuft sehr viel schneller in den beiden Motoren. Führen Sie den Vergleich dann erneut aus.

+0

Vielen Dank für Ihre Antwort. Ich mache die Änderungen und dokumentiere hier die Ergebnisse. –

+0

@DagoBorda - Ergebnisse noch nicht? –

Verwandte Themen