Als Ergänzung zu den zahlreichen Antworten, die hier zu den mechanischen Unterschieden zwischen den beiden Motoren gegeben wurden, stelle ich eine empirische Studie zum Geschwindigkeitsvergleich vor.
In Bezug auf die reine Geschwindigkeit ist MyISAM nicht immer schneller als InnoDB, aber meiner Erfahrung nach ist es in reinen READ-Arbeitsumgebungen in der Regel um den Faktor 2,0-2,5 schneller. Natürlich ist dies nicht für alle Umgebungen geeignet - wie andere geschrieben haben, fehlen MyISAM Dinge wie Transaktionen und Fremdschlüssel.
Ich habe ein wenig Benchmarking unten getan - ich habe Python für Schleifen und die timeit Bibliothek für Timing-Vergleiche verwendet. Aus Interesse habe ich auch die Speicher-Engine einbezogen, die die beste Leistung auf der ganzen Linie bietet, obwohl sie nur für kleinere Tabellen geeignet ist (man trifft ständig auf The table 'tbl' is full
wenn Sie das MySQL-Speicherlimit überschreiten). Die vier Arten von Select, die ich betrachte, sind:
- Vanille SELECTs
- zählt
- bedingte SELECTs
- indizierte und nicht-indizierte Subselects
Zunächst habe ich drei Tabellen mit folgendem 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.
1) Vanille wählt aus
Abfrage: SELECT * FROM tbl WHERE index_col = xx
Ergebnis: zeichnen
Die Geschwindigkeit ist im Großen und Ganzen die gleiche und hängt erwartungsgemäß linear von der Anzahl der auszuwählenden Spalten ab. InnoDB scheint leicht schneller 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) )
2) Zählt
Abfrage: SELECT count(*) FROM tbl
Ergebnis: MyISAM gewinnt
Hier zeigt sich ein großer Unterschied zwischen MyISAM und InnoDB - MyISAM (und der Speicher) verfolgt die Anzahl der Datensätze in der Tabelle, daher ist diese Transaktion schnell und O(1). Die Zeit, die InnoDB zum Zählen benötigt, steigt in dem von mir untersuchten Bereich superlinear mit der Tabellengröße an. Ich vermute, dass viele der in der Praxis beobachteten Geschwindigkeitssteigerungen bei 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) )
3) Bedingte Auswahlen
Abfrage: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
Ergebnis: MyISAM gewinnt
Hier schneiden MyISAM und Speicher ungefähr gleich gut ab und schlagen InnoDB bei größeren Tabellen um etwa 50 %. Dies ist die Art von Abfrage, bei der die Vorteile von MyISAM am größten zu sein scheinen.
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) )
4) Unter-Auswahlen
Ergebnis: InnoDB gewinnt
Für diese Abfrage habe ich einen zusätzlichen Satz von Tabellen für die Unterauswahl erstellt. Jede besteht einfach aus zwei BIGINT-Spalten, eine mit einem Primärschlüsselindex und eine ohne Index. Aufgrund der großen Tabellengröße habe ich die Speicher-Engine nicht getestet. Der SQL-Tabellenerstellungsbefehl lautete
CREATE TABLE
subselect_myisam
(
index_col bigint NOT NULL,
non_index_col bigint,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
wobei auch hier in der zweiten Tabelle "MyISAM" durch "InnoDB" ersetzt wird.
In dieser Abfrage belasse ich die Größe der Auswahltabelle bei 1000000 und variiere stattdessen die Größe der unterselektierten Spalten.
Hier gewinnt die InnoDB leicht. Nachdem wir eine vernünftige Tabellengröße erreicht haben, skalieren beide Engines linear mit der Größe der Unterauswahl. Der Index beschleunigt den MyISAM-Befehl, hat aber interessanterweise kaum 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, die man aus all dem mitnehmen kann, ist, dass man, wenn man wirklich Wenn Sie sich Sorgen um die Geschwindigkeit machen, müssen Sie die Abfragen, die Sie durchführen, einem Benchmarking unterziehen, anstatt Vermutungen darüber anzustellen, welche Maschine besser geeignet ist.
14 Stimmen
Le MySQL-Leistungs-Blog ist eine großartige Quelle für diese Art von Dingen.
3 Stimmen
Dies hängt ein wenig davon ab, ob Ihr System OLTP- oder eher Datawarehouse-orientiert ist (wo die meisten Schreibvorgänge in großen Mengen erfolgen).
38 Stimmen
MyISAM unterstützt kein Row-Locking, keine Transaktionen, es unterstützt nicht einmal Fremdschlüssel... verdammt, da es keine SÄURE kann man kaum noch von einer richtigen Datenbank sprechen! Das ist der Grund, warum InnoDB seit MySQL 5.5 die Standard-Engine ist... aber, aus welchem Grund auch immer, ist MyISAM weiterhin die Standard-Engine für Tabellen, die mit PhpMyAdmin erstellt werden, so dass viele Amateur-Datenbanken seitdem auf MyISAM laufen.
0 Stimmen
Siehe dies rackspace.com/knowledge_center/article/
0 Stimmen
Alle sehen MySql DB-Engines verglichen
0 Stimmen
Ich habe es gerade mit einer 80.0000 Datensätze umfassenden Datenbank getestet. MyIsam ist um Sekunden schneller.