Wie kann MyISAM "schneller" als InnoDB sein, wenn
- MyISAM muss Festplattenlesevorgänge für die Daten durchführen?
- InnoDB verwendet den Pufferpool für Indizes und Daten und MyISAM nur für den Index?
Wie kann MyISAM "schneller" als InnoDB sein, wenn
Antworten:
Nur so kann MyISAM schneller sein als InnoDB unter diesen besonderen Umständen
Beim Lesen können die Indizes einer MyISAM-Tabelle einmal aus der .MYI-Datei gelesen und in den MyISAM-Schlüssel-Cache geladen werden (Größe nach key_buffer_size ). Wie können Sie die .MYD-Datei einer MyISAM-Tabelle schneller lesen lassen? Mit diesem:
ALTER TABLE mytable ROW_FORMAT=Fixed;
Ich habe darüber in meinen letzten Beiträgen geschrieben
OK, was ist mit InnoDB? Führt InnoDB Festplatten-E / A für Abfragen durch? Überraschenderweise tut es das ja !! Sie denken wahrscheinlich, ich bin verrückt danach, das zu sagen, aber es ist absolut wahr, auch für SELECT-Abfragen . An dieser Stelle wundern Sie sich wahrscheinlich: "Wie in aller Welt führt InnoDB Festplatten-E / A-Vorgänge für Abfragen durch?"
Alles geht zurück auf InnoDB als ACID- reklamierte Transactional Storage Engine. Um InnoDB Transactional zu sein, hat es die Unterstützung I
in ACID
, die Isolation ist. Die Technik zur Aufrechterhaltung der Isolierung für Transaktionen erfolgt über MVCC, Multiversion Concurrency Control . In einfachen Worten, InnoDB zeichnet auf, wie Daten aussehen, bevor Transaktionen versuchen, sie zu ändern. Wo wird das aufgenommen? In der System-Tablespace-Datei besser bekannt als ibdata1. Das erfordert Disk - I / O .
Welche zufälligen Faktoren bestimmen, wer schneller ist, da sowohl InnoDB als auch MyISAM Festplatten-E / A durchführen?
DELETEs
undUPDATEs
In einer Umgebung mit vielen Lesevorgängen ist es daher möglich, dass eine MyISAM-Tabelle mit einem festen Zeilenformat besser als InnoDB-Lesevorgänge aus dem InnoDB-Pufferpool abschneidet, wenn in die in ibdata1 enthaltenen Rückgängig-Protokolle genügend Daten geschrieben werden, um das Transaktionsverhalten zu unterstützen InnoDB-Daten auferlegt.
Planen Sie Ihre Datentypen, Abfragen und Speicher-Engines sorgfältig. Sobald die Daten wachsen, kann es sehr schwierig werden, Daten zu verschieben. Fragen Sie einfach Facebook ...
In einer einfachen Welt ist MyISAM schneller beim Lesen und InnoDB schneller beim Schreiben.
Sobald Sie gemischte Lese- / Schreibvorgänge einführen, ist InnoDB dank seines Zeilensperrmechanismus auch beim Lesen schneller.
Ich habe vor einigen Jahren einen Vergleich von MySQL-Speicher-Engines geschrieben, der bis heute gültig ist und die einzigartigen Unterschiede zwischen MyISAM und InnoDB aufzeigt.
Nach meiner Erfahrung sollten Sie InnoDB für alles verwenden, außer für Cachetabellen mit hohem Leseaufwand, bei denen der Verlust von Daten aufgrund von Korruption nicht so kritisch ist.
Um die Antworten hier auf die mechanischen Unterschiede zwischen den beiden Motoren zu ergänzen, präsentiere ich eine empirische Geschwindigkeitsvergleichsstudie.
In Bezug auf die reine Geschwindigkeit ist MyISAM nicht immer schneller als InnoDB, aber meiner Erfahrung nach ist es für PURE READ-Arbeitsumgebungen in der Regel um den Faktor 2,0 bis 2,5 schneller. Offensichtlich ist dies nicht für alle Umgebungen geeignet - wie andere geschrieben haben, fehlen MyISAM Dinge wie Transaktionen und Fremdschlüssel.
Ich habe unten ein bisschen Benchmarking durchgeführt - ich habe Python für Loops und die Timeit-Bibliothek für Timing-Vergleiche verwendet. Aus Gründen des Interesses habe ich auch die Speicher-Engine integriert, die die beste Leistung auf der ganzen Linie bietet, obwohl sie nur für kleinere Tabellen geeignet ist (Sie stoßen immer wieder darauf, The table 'tbl' is full
wenn Sie das MySQL-Speicherlimit überschreiten). Die vier Arten der Auswahl, die ich ansehe, sind:
Zunächst habe ich drei Tabellen mit der folgenden SQL erstellt
CREATE TABLE
data_interrogation.test_table_myisam
(
index_col BIGINT NOT NULL AUTO_INCREMENT,
value1 DOUBLE,
value2 DOUBLE,
value3 DOUBLE,
value4 DOUBLE,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8
mit 'MyISAM' anstelle von 'InnoDB' und 'memory' in der zweiten und dritten Tabelle.
Abfrage: SELECT * FROM tbl WHERE index_col = xx
Ergebnis: Unentschieden
Die Geschwindigkeit ist im Großen und Ganzen gleich und wie erwartet linear in der Anzahl der auszuwählenden Spalten. InnoDB scheint etwas schneller zu sein als MyISAM, aber das ist wirklich marginal.
Code:
import timeit
import MySQLdb
import MySQLdb.cursors
import random
from random import randint
db = MySQLdb.connect(host="...", user="...", passwd="...", db="...", cursorclass=MySQLdb.cursors.DictCursor)
cur = db.cursor()
lengthOfTable = 100000
# Fill up the tables with random data
for x in xrange(lengthOfTable):
rand1 = random.random()
rand2 = random.random()
rand3 = random.random()
rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)
cur.execute(insertString2)
cur.execute(insertString3)
db.commit()
# Define a function to pull a certain number of records from these tables
def selectRandomRecords(testTable,numberOfRecords):
for x in xrange(numberOfRecords):
rand1 = randint(0,lengthOfTable)
selectString = "SELECT * FROM " + testTable + " WHERE index_col = " + str(rand1)
cur.execute(selectString)
setupString = "from __main__ import selectRandomRecords"
# Test time taken using timeit
myisam_times = []
innodb_times = []
memory_times = []
for theLength in [3,10,30,100,300,1000,3000,10000]:
innodb_times.append( timeit.timeit('selectRandomRecords("test_table_innodb",' + str(theLength) + ')', number=100, setup=setupString) )
myisam_times.append( timeit.timeit('selectRandomRecords("test_table_myisam",' + str(theLength) + ')', number=100, setup=setupString) )
memory_times.append( timeit.timeit('selectRandomRecords("test_table_memory",' + str(theLength) + ')', number=100, setup=setupString) )
Abfrage: SELECT count(*) FROM tbl
Ergebnis: MyISAM gewinnt
Dieser zeigt einen großen Unterschied zwischen MyISAM und InnoDB - MyISAM (und Speicher) protokolliert die Anzahl der Datensätze in der Tabelle, sodass diese Transaktion schnell und 0 (1) ist. Die Zeit, die InnoDB benötigt, um zu zählen, steigt superlinear mit der Tabellengröße in dem von mir untersuchten Bereich. Ich vermute, dass viele der in der Praxis beobachteten Beschleunigungen von MyISAM-Abfragen auf ähnliche Effekte zurückzuführen sind.
Code:
myisam_times = []
innodb_times = []
memory_times = []
# Define a function to count the records
def countRecords(testTable):
selectString = "SELECT count(*) FROM " + testTable
cur.execute(selectString)
setupString = "from __main__ import countRecords"
# Truncate the tables and re-fill with a set amount of data
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:
truncateString = "TRUNCATE test_table_innodb"
truncateString2 = "TRUNCATE test_table_myisam"
truncateString3 = "TRUNCATE test_table_memory"
cur.execute(truncateString)
cur.execute(truncateString2)
cur.execute(truncateString3)
for x in xrange(theLength):
rand1 = random.random()
rand2 = random.random()
rand3 = random.random()
rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)
cur.execute(insertString2)
cur.execute(insertString3)
db.commit()
# Count and time the query
innodb_times.append( timeit.timeit('countRecords("test_table_innodb")', number=100, setup=setupString) )
myisam_times.append( timeit.timeit('countRecords("test_table_myisam")', number=100, setup=setupString) )
memory_times.append( timeit.timeit('countRecords("test_table_memory")', number=100, setup=setupString) )
Abfrage: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
Ergebnis: MyISAM gewinnt
Hier arbeiten MyISAM und Arbeitsspeicher ungefähr gleich und übertreffen InnoDB bei größeren Tabellen um etwa 50%. Diese Art von Abfrage scheint die Vorteile von MyISAM zu maximieren.
Code:
myisam_times = []
innodb_times = []
memory_times = []
# Define a function to perform conditional selects
def conditionalSelect(testTable):
selectString = "SELECT * FROM " + testTable + " WHERE value1 < 0.5 AND value2 < 0.5 AND value3 < 0.5 AND value4 < 0.5"
cur.execute(selectString)
setupString = "from __main__ import conditionalSelect"
# Truncate the tables and re-fill with a set amount of data
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:
truncateString = "TRUNCATE test_table_innodb"
truncateString2 = "TRUNCATE test_table_myisam"
truncateString3 = "TRUNCATE test_table_memory"
cur.execute(truncateString)
cur.execute(truncateString2)
cur.execute(truncateString3)
for x in xrange(theLength):
rand1 = random.random()
rand2 = random.random()
rand3 = random.random()
rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
insertString3 = "INSERT INTO test_table_memory (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)
cur.execute(insertString2)
cur.execute(insertString3)
db.commit()
# Count and time the query
innodb_times.append( timeit.timeit('conditionalSelect("test_table_innodb")', number=100, setup=setupString) )
myisam_times.append( timeit.timeit('conditionalSelect("test_table_myisam")', number=100, setup=setupString) )
memory_times.append( timeit.timeit('conditionalSelect("test_table_memory")', number=100, setup=setupString) )
Ergebnis: InnoDB gewinnt
Für diese Abfrage habe ich einen zusätzlichen Tabellensatz für die Unterauswahl erstellt. Jedes ist einfach zwei Spalten von BIGINTs, eine mit einem Primärschlüsselindex und eine ohne Index. Aufgrund der großen Tischgröße habe ich die Memory Engine nicht getestet. Der Befehl zum Erstellen einer SQL-Tabelle lautete
CREATE TABLE
subselect_myisam
(
index_col bigint NOT NULL,
non_index_col bigint,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
Dabei wird in der zweiten Tabelle erneut 'InnoDB' durch 'MyISAM' ersetzt.
In dieser Abfrage belasse ich die Größe der Auswahltabelle bei 1000000 und verändere stattdessen die Größe der unterausgewählten Spalten.
Hier gewinnt die InnoDB leicht. Nachdem wir eine vernünftige Größentabelle erreicht haben, skalieren beide Motoren linear mit der Größe der Unterauswahl. Der Index beschleunigt den MyISAM-Befehl, hat jedoch interessanterweise nur geringe Auswirkungen auf die InnoDB-Geschwindigkeit. subSelect.png
Code:
myisam_times = []
innodb_times = []
myisam_times_2 = []
innodb_times_2 = []
def subSelectRecordsIndexed(testTable,testSubSelect):
selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT index_col FROM " + testSubSelect + " )"
cur.execute(selectString)
setupString = "from __main__ import subSelectRecordsIndexed"
def subSelectRecordsNotIndexed(testTable,testSubSelect):
selectString = "SELECT * FROM " + testTable + " WHERE index_col in ( SELECT non_index_col FROM " + testSubSelect + " )"
cur.execute(selectString)
setupString2 = "from __main__ import subSelectRecordsNotIndexed"
# Truncate the old tables, and re-fill with 1000000 records
truncateString = "TRUNCATE test_table_innodb"
truncateString2 = "TRUNCATE test_table_myisam"
cur.execute(truncateString)
cur.execute(truncateString2)
lengthOfTable = 1000000
# Fill up the tables with random data
for x in xrange(lengthOfTable):
rand1 = random.random()
rand2 = random.random()
rand3 = random.random()
rand4 = random.random()
insertString = "INSERT INTO test_table_innodb (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
insertString2 = "INSERT INTO test_table_myisam (value1,value2,value3,value4) VALUES (" + str(rand1) + "," + str(rand2) + "," + str(rand3) + "," + str(rand4) + ")"
cur.execute(insertString)
cur.execute(insertString2)
for theLength in [3,10,30,100,300,1000,3000,10000,30000,100000]:
truncateString = "TRUNCATE subselect_innodb"
truncateString2 = "TRUNCATE subselect_myisam"
cur.execute(truncateString)
cur.execute(truncateString2)
# For each length, empty the table and re-fill it with random data
rand_sample = sorted(random.sample(xrange(lengthOfTable), theLength))
rand_sample_2 = random.sample(xrange(lengthOfTable), theLength)
for (the_value_1,the_value_2) in zip(rand_sample,rand_sample_2):
insertString = "INSERT INTO subselect_innodb (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"
insertString2 = "INSERT INTO subselect_myisam (index_col,non_index_col) VALUES (" + str(the_value_1) + "," + str(the_value_2) + ")"
cur.execute(insertString)
cur.execute(insertString2)
db.commit()
# Finally, time the queries
innodb_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString) )
myisam_times.append( timeit.timeit('subSelectRecordsIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString) )
innodb_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_innodb","subselect_innodb")', number=100, setup=setupString2) )
myisam_times_2.append( timeit.timeit('subSelectRecordsNotIndexed("test_table_myisam","subselect_myisam")', number=100, setup=setupString2) )
Ich denke, die Botschaft zum Mitnehmen ist, dass Sie, wenn Sie wirklich auf Geschwindigkeit bedacht sind, die von Ihnen ausgeführten Abfragen einem Benchmarking unterziehen müssen, anstatt davon auszugehen, welcher Motor besser geeignet ist.
SELECT * FROM tbl WHERE index_col = xx
- Hier sind zwei Faktoren, die wahrscheinlich zu mehr Variationen in der Grafik führen: Primärschlüssel vs. Sekundärschlüssel; Index ist zwischengespeichert vs nicht.
SELECT COUNT(*)
ist ein klarer Gewinner für MyISAM, bis Sie eine WHERE
Klausel hinzufügen .
Welche ist schneller? Beides könnte schneller sein. YMMV.
Welches solltest du verwenden? InnoDB - Absturzsicher usw. usw.