it-swarm-eu.dev

Was ist schneller, InnoDB oder MyISAM?

Wie kann MyISAM "schneller" sein als InnoDB, 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?
56
jcho360

Nur so kann MyISAM schneller sein als InnoDB unter diesen einzigartigen Umständen

MyISAM

Beim Lesen können die Indizes einer MyISAM-Tabelle einmal aus der .MYI-Datei gelesen und in den MyISAM-Schlüsselcache geladen werden (Größe nach key_buffer_size ). ). Wie können Sie die .MYD einer MyISAM-Tabelle schneller lesen lassen? Mit diesem:

ALTER TABLE mytable ROW_FORMAT=Fixed;

Ich habe darüber in meinen letzten Beiträgen geschrieben

InnoDB

OK, was ist mit InnoDB? Führt InnoDB Festplatten-E/A für Abfragen aus? Überraschenderweise ja ja !! Sie denken wahrscheinlich, ich bin verrückt danach, das zu sagen, aber es ist absolut wahr, sogar für SELECT-Abfragen . An diesem Punkt fragen Sie sich wahrscheinlich: "Wie in aller Welt führt InnoDB Festplatten-E/A für Abfragen aus?"

Es geht alles darauf zurück, dass InnoDB eine [~ # ~] Säure [~ # ~] - Beschwerde Transactional Storage Engine ist. Damit InnoDB transaktional ist, muss es I in ACID unterstützen, was Isolation ist. Die Technik zur Aufrechterhaltung der Isolation 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 Systemtabellenbereichsdatei, besser bekannt als ibdata1. Dies erfordert Festplatten-E/A .

[~ # ~] Vergleich [~ # ~]

Welche zufälligen Faktoren bestimmen, wer schneller ist, da sowohl InnoDB als auch MyISAM Festplatten-E/A ausführen?

  • Größe der Spalten
  • Spaltenformat
  • Zeichensätze
  • Bereich numerischer Werte (erfordert ausreichend große INTs)
  • Zeilen werden blockweise aufgeteilt (Zeilenverkettung)
  • Datenfragmentierung durch DELETEs und UPDATEs
  • Größe des Primärschlüssels (InnoDB verfügt über einen Clustered-Index, für den zwei Schlüsselsuchen erforderlich sind)
  • Größe der Indexeinträge
  • die Liste geht weiter...

In einer Umgebung mit starken Lesevorgängen ist es daher möglich, dass eine MyISAM-Tabelle mit einem festen Zeilenformat die InnoDB-Lesevorgänge aus dem InnoDB-Pufferpool übertrifft, wenn genügend Daten in die in ibdata1 enthaltenen Rückgängig-Protokolle geschrieben werden, um das Transaktionsverhalten zu unterstützen den InnoDB-Daten auferlegt.

[~ # ~] Schlussfolgerung [~ # ~]

Planen Sie Ihre Datentypen, Abfragen und Speicher-Engines sehr sorgfältig. Sobald die Daten wachsen, kann es sehr schwierig werden, Daten zu verschieben. Frag einfach Facebook ...

69
RolandoMySQLDBA

In einer einfachen Welt ist MyISAM schneller für Lesevorgänge, InnoDB schneller für Schreibvorgänge.

Sobald Sie mit der Einführung gemischter Lese-/Schreibvorgänge beginnen, ist InnoDB dank seines Zeilensperrmechanismus auch für Lesevorgänge schneller.

Ich habe vor einigen Jahren einen Vergleich von MySQL Storage Engines geschrieben, der bis heute gilt und die einzigartigen Unterschiede zwischen MyISAM und InnoDB umreißt.

Nach meiner Erfahrung sollten Sie InnoDB für alles verwenden, außer für leselastige Cache-Tabellen, bei denen der Verlust von Daten aufgrund von Beschädigungen nicht so kritisch ist.

22
Mike Peters

Um die Antworten hier zu ergänzen, die die mechanischen Unterschiede zwischen den beiden Motoren abdecken, 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 tendenziell um das 2,0- bis 2,5-fache schneller. Dies ist natürlich nicht für alle Umgebungen geeignet. Wie andere geschrieben haben, fehlen MyISAM beispielsweise Transaktionen und Fremdschlüssel.

Ich habe unten ein bisschen Benchmarking durchgeführt - ich habe python für Schleifen und die Timeit-Bibliothek für Timing-Vergleiche verwendet. Aus Interesse habe ich auch die Speicher-Engine aufgenommen, dies bietet die beste Leistung auf der ganzen Linie, obwohl es nur für kleinere Tabellen geeignet ist (Sie stoßen ständig auf The table 'tbl' is full, wenn Sie das MySQL-Speicherlimit überschreiten). Die vier Arten von Auswahl, die ich betrachte, sind:

  1. Vanille WÄHLT
  2. zählt
  3. bedingte SELECTs
  4. indizierte und nicht indizierte Unterauswahlen

Zuerst habe ich drei Tabellen mit dem 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

in der zweiten und dritten Tabelle wird "InnoDB" und "Speicher" durch "MyISAM" ersetzt.

1) Vanille wählt aus

Abfrage: SELECT * FROM tbl WHERE index_col = xx

Ergebnis: nentschieden

Comparison of Vanilla selects by different database engines

Die Geschwindigkeit dieser ist im Großen und Ganzen gleich und wie erwartet linear in der Anzahl der auszuwählenden Spalten. InnoDB scheint etwas schneller als MyISAM zu sein, 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

Comparison of counts by different database engines

Dieser zeigt einen großen Unterschied zwischen MyISAM und InnoDB - MyISAM (und Speicher) verfolgt die Anzahl der Datensätze in der Tabelle, sodass diese Transaktion schnell und O (1) ist. Die Zeit, die InnoDB zum Zählen benötigt, 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) )

3) Bedingte Auswahl

Abfrage: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5

Ergebnis: MyISAM gewinnt

Comparison of conditional selects by different database engines

Hier arbeiten MyISAM und Speicher ungefähr gleich und übertreffen InnoDB bei größeren Tabellen um etwa 50%. Dies ist die Art von Abfrage, für die die Vorteile von MyISAM maximiert 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) Unterauswahl

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 Spalten von BIGINTs, eine mit einem Primärschlüsselindex und eine ohne Index. Aufgrund der großen Tabellengröße habe ich die Speicher-Engine nicht getestet. Der Befehl zum Erstellen einer SQL-Tabelle war

CREATE TABLE
    subselect_myisam
    (
        index_col bigint NOT NULL,
        non_index_col bigint,
        PRIMARY KEY (index_col)
    )
    ENGINE=MyISAM DEFAULT CHARSET=utf8;

in der zweiten Tabelle wird 'InnoDB' durch 'MyISAM' ersetzt.

In dieser Abfrage belasse ich die Größe der Auswahltabelle bei 1000000 und variiere stattdessen die Größe der unterausgewählten Spalten.

Comparison of sub-selects by different database engines

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 wenig Einfluss 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 über Geschwindigkeit besorgt sind, die von Ihnen ausgeführten Abfragen vergleichen müssen anstatt irgendwelche Annahmen darüber zu treffen, welcher Motor besser geeignet ist.

14
StackG

Welche ist schneller? Beides könnte schneller sein. YMMV.

Welches solltest du verwenden? InnoDB - Absturzsicher usw. usw.

4
Rick James