Come può MyISAM essere "più veloce" di InnoDB se
- MyISAM deve fare letture del disco per i dati?
- InnoDB utilizza il pool di buffer per indici e dati e MyISAM solo per l'indice?
Come può MyISAM essere "più veloce" di InnoDB se
Risposte:
L'unico modo in cui MyISAM può essere più veloce di InnoDB in questa circostanza unica
Quando viene letto, gli indici di una tabella MyISAM possono essere letti una volta dal file .MYI e caricati nella cache delle chiavi MyISAM (in base alle dimensioni di key_buffer_size ). Come si può rendere più veloce la lettura .MYD di una tabella MyISAM? Con questo:
ALTER TABLE mytable ROW_FORMAT=Fixed;
Ho scritto di questo nei miei post precedenti
OK, che dire di InnoDB? InnoDB esegue I / O su disco per le query? Sorprendentemente, sì, lo fa !! Probabilmente stai pensando che io sia pazzo per averlo detto, ma è assolutamente vero, anche per le query SELECT . A questo punto, probabilmente ti starai chiedendo "In che modo InnoDB sta eseguendo l'I / O su disco per le query?"
Tutto risale al fatto che InnoDB è un motore di memorizzazione transazionale ACID- complaint. Affinché InnoDB sia Transazionale, deve supportare I
in ACID
, che è Isolation. La tecnica per mantenere l'isolamento per le transazioni viene eseguita tramite MVCC, Multiversion Concurrency Control . In termini semplici, InnoDB registra l'aspetto dei dati prima che le transazioni tentino di modificarli. Dove viene registrato? Nel file del tablespace di sistema, meglio noto come ibdata1. Ciò richiede l'I / O del disco .
Poiché sia InnoDB che MyISAM eseguono l'I / O su disco, quali fattori casuali determinano chi è più veloce?
DELETEs
eUPDATEs
Pertanto, in un ambiente di lettura pesante, è possibile che una tabella MyISAM con un formato di riga fissa superi le letture di InnoDB dal pool di buffer InnoDB se vi sono dati sufficienti scritti nei registri di annullamento contenuti in ibdata1 per supportare il comportamento transazionale imposto sui dati InnoDB.
Pianifica i tipi di dati, le query e il motore di archiviazione in modo molto accurato. Una volta che i dati crescono, potrebbe essere molto difficile spostarli. Basta chiedere a Facebook ...
In un mondo semplice, MyISAM è più veloce per le letture, InnoDB è più veloce per le scritture.
Una volta che inizi a introdurre letture / scritture miste, InnoDB sarà più veloce anche per le letture, grazie al suo meccanismo di blocco delle file.
Ho scritto un confronto tra i motori di archiviazione MySQL alcuni anni fa, che è ancora valido fino ad oggi, delineando le differenze uniche tra MyISAM e InnoDB.
Nella mia esperienza, dovresti usare InnoDB per tutto tranne che per le tabelle di cache pesanti in lettura, in cui la perdita di dati a causa della corruzione non è così critica.
Per aggiungere alle risposte qui che riguardano le differenze meccaniche tra i due motori, presento uno studio empirico di confronto della velocità.
In termini di pura velocità, non è sempre vero che MyISAM sia più veloce di InnoDB, ma nella mia esperienza tende ad essere più veloce per gli ambienti di lavoro PURE READ di un fattore di circa 2,0-2,5 volte. Chiaramente questo non è appropriato per tutti gli ambienti - come altri hanno scritto, MyISAM manca di cose come transazioni e chiavi esterne.
Ho fatto un po 'di benchmarking di seguito - ho usato Python per il looping e la libreria timeit per i confronti di temporizzazione. Per interesse ho anche incluso il motore di memoria, questo offre le migliori prestazioni su tutta la linea anche se è adatto solo per tavoli più piccoli (si incontrano continuamente The table 'tbl' is full
quando si supera il limite di memoria MySQL). I quattro tipi di selezione che guardo sono:
Innanzitutto, ho creato tre tabelle utilizzando il seguente SQL
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
con "MyISAM" ha sostituito "InnoDB" e "memoria" nella seconda e terza tabella.
Query: SELECT * FROM tbl WHERE index_col = xx
Risultato: pareggio
La velocità di questi è sostanzialmente la stessa e, come previsto, è lineare nel numero di colonne da selezionare. InnoDB sembra leggermente più veloce di MyISAM ma questo è davvero marginale.
Codice:
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) )
Query: SELECT count(*) FROM tbl
Risultato: MyISAM vince
Questo dimostra una grande differenza tra MyISAM e InnoDB - MyISAM (e memoria) tiene traccia del numero di record nella tabella, quindi questa transazione è veloce e O (1). La quantità di tempo necessaria per il conteggio di InnoDB aumenta in modo super-lineare con la dimensione della tabella nell'intervallo che ho studiato. Sospetto che molte delle accelerazioni delle query MyISAM osservate nella pratica siano dovute a effetti simili.
Codice:
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) )
Query: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
Risultato: MyISAM vince
In questo caso, MyISAM e la memoria si comportano all'incirca allo stesso modo e battono InnoDB di circa il 50% per tavoli più grandi. Questo è il tipo di query per cui i vantaggi di MyISAM sembrano essere massimizzati.
Codice:
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) )
Risultato: InnoDB vince
Per questa query, ho creato un set aggiuntivo di tabelle per la sottoselezione. Ciascuno è semplicemente due colonne di BIGINT, una con un indice di chiave primaria e una senza alcun indice. A causa delle grandi dimensioni della tabella, non ho testato il motore di memoria. Il comando di creazione della tabella SQL era
CREATE TABLE
subselect_myisam
(
index_col bigint NOT NULL,
non_index_col bigint,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
dove ancora una volta, "MyISAM" è sostituito da "InnoDB" nella seconda tabella.
In questa query, lascio la dimensione della tabella di selezione a 1000000 e invece modifico la dimensione delle colonne selezionate.
Qui InnoDB vince facilmente. Dopo aver raggiunto una tabella di dimensioni ragionevoli, entrambi i motori si ridimensionano in modo lineare con le dimensioni della sottoselezione. L'indice accelera il comando MyISAM ma ha un effetto interessante sulla velocità di InnoDB. subSelect.png
Codice:
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) )
Penso che il messaggio da portare a casa di tutto ciò sia che se sei veramente preoccupato per la velocità, devi confrontare le domande che stai facendo piuttosto che fare ipotesi su quale motore sarà più adatto.
SELECT * FROM tbl WHERE index_col = xx
- Qui ci sono due fattori che possono portare a maggiori variazioni nel grafico: chiave primaria vs chiave secondaria; L'indice è memorizzato nella cache e non.
SELECT COUNT(*)
è un chiaro vincitore per MyISAM fino a quando non aggiungi una WHERE
clausola.
Qual è più veloce? O potrebbe essere più veloce. YMMV.
Quale dovresti usare? InnoDB - crash-safe, ecc. Ecc.