Comment MyISAM peut-il être "plus rapide" qu'InnoDB si
- MyISAM doit faire des lectures de disque pour les données?
- InnoDB utilise le pool de mémoire tampon pour les index et les données, et MyISAM uniquement pour l'index?
Comment MyISAM peut-il être "plus rapide" qu'InnoDB si
Réponses:
La seule façon dont MyISAM peut être plus rapide qu'InnoDB serait dans cette situation unique
Une fois lus, les index d’une table MyISAM peuvent être lus une fois à partir du fichier .MYI et chargés dans le cache de clé MyISAM (au format key_buffer_size ). Comment pouvez-vous accélérer la lecture du .MYD d'une table MyISAM? Avec ça:
ALTER TABLE mytable ROW_FORMAT=Fixed;
J'ai écrit à ce sujet dans mes posts précédents
OK, qu'en est-il de InnoDB? InnoDB effectue-t-il des entrées / sorties sur disque pour les requêtes? Étonnamment, oui c'est le cas !! Vous pensez probablement que je suis fou de dire cela, mais c'est absolument vrai, même pour les requêtes SELECT . À ce stade, vous vous demandez probablement "Comment InnoDB gère-t-il les entrées / sorties sur disque pour les requêtes?"
Tout revient à InnoDB en tant que moteur de stockage transactionnel à réclamation ACID . Pour qu'InnoDB soit transactionnel, il doit supporter l' I
in ACID
, qui est Isolation. La technique de maintenance de l'isolation des transactions est effectuée via MVCC, Multiversion Concurrency Control . En termes simples, InnoDB enregistre à quoi ressemblent les données avant que les transactions ne tentent de les modifier. Où cela est-il enregistré? Dans le fichier de tablespace système, mieux connu sous le nom de ibdata1. Cela nécessite des E / S de disque .
Etant donné qu'InnoDB et MyISAM effectuent des E / S de disque, quels facteurs aléatoires dictent qui est le plus rapide?
DELETEs
etUPDATEs
Ainsi, dans un environnement de lecture intensive, il est possible qu'une table MyISAM avec un format de ligne fixe surpasse les lectures InnoDB du pool de mémoire tampon InnoDB si suffisamment de données sont écrites dans les journaux d'annulation contenus dans ibdata1 pour prendre en charge le comportement transactionnel. imposées sur les données InnoDB.
Planifiez soigneusement vos types de données, vos requêtes et votre moteur de stockage. Une fois que les données ont augmenté, il peut devenir très difficile de les déplacer. Il suffit de demander à Facebook ...
Dans un monde simple, MyISAM est plus rapide pour les lectures, InnoDB est plus rapide pour les écritures.
Une fois que vous avez commencé à introduire des lectures / écritures mixtes, InnoDB sera également plus rapide pour les lectures, grâce à son mécanisme de verrouillage de rangée.
J'ai comparé les moteurs de stockage MySQL il y a quelques années, ce qui est toujours valable, soulignant les différences uniques entre MyISAM et InnoDB.
D'après mon expérience, vous devriez utiliser InnoDB pour tout, sauf pour les tables de cache à lecture lourde, où la perte de données en raison d'une corruption n'est pas aussi critique.
Pour ajouter aux réponses ici couvrant les différences mécaniques entre les deux moteurs, je présente une étude de comparaison de vitesse empirique.
En termes de vitesse pure, MyISAM n’est pas toujours plus rapide que InnoDB, mais selon mon expérience, il a tendance à être plus rapide de 2,0 à 2,5 fois plus dans les environnements de travail PURE READ. Clairement, cela ne convient pas à tous les environnements - comme d'autres l'ont écrit, MyISAM manque d'éléments tels que les transactions et les clés étrangères.
J'ai fait un peu de comparaison ci-dessous - j'ai utilisé python pour les boucles et la bibliothèque timeit pour les comparaisons de temps. Il est intéressant de noter que j'ai également inclus le moteur de mémoire, qui offre les meilleures performances globales, même s'il ne convient que pour les petites tables (que vous rencontrez continuellement The table 'tbl' is full
lorsque vous dépassez la limite de mémoire MySQL). Les quatre types de sélection que je regarde sont les suivants:
Tout d'abord, j'ai créé trois tables en utilisant le code SQL suivant
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
avec 'MyISAM' substitué à 'InnoDB' et 'memory' dans les deuxième et troisième tables.
Requete: SELECT * FROM tbl WHERE index_col = xx
Résultat: dessiner
La vitesse de ceux-ci est globalement la même et, comme prévu, est linéaire dans le nombre de colonnes à sélectionner. InnoDB semble légèrement plus rapide que MyISAM mais ceci est vraiment 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) )
Requete: SELECT count(*) FROM tbl
Résultat: MyISAM gagne
Celui-ci montre une grande différence entre MyISAM et InnoDB - MyISAM (et la mémoire) garde la trace du nombre d'enregistrements dans la table, donc cette transaction est rapide et O (1). Le temps nécessaire à InnoDB pour compter augmente de façon super linéaire avec la taille de la table dans la plage que j'ai étudiée. Je pense que bon nombre des accélérations résultant des requêtes MyISAM observées dans la pratique sont dues à des effets similaires.
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) )
Requete: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
Résultat: MyISAM gagne
Ici, MyISAM et la mémoire fonctionnent à peu près de la même manière et battent InnoDB d’environ 50% pour les tables plus volumineuses. C'est le type de requête pour lequel les avantages de MyISAM semblent être maximisés.
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) )
Résultat: InnoDB gagne
Pour cette requête, j'ai créé un ensemble supplémentaire de tables pour la sous-sélection. Chacune consiste simplement en deux colonnes de BIGINT, une avec un index de clé primaire et une sans index. En raison de la grande taille de la table, je n'ai pas testé le moteur de mémoire. La commande de création de table SQL était
CREATE TABLE
subselect_myisam
(
index_col bigint NOT NULL,
non_index_col bigint,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
où, encore une fois, «MyISAM» est remplacé par «InnoDB» dans le deuxième tableau.
Dans cette requête, je laisse la taille de la table de sélection à 1000000 et à la place je fais varier la taille des colonnes sous-sélectionnées.
Ici, l'InnoDB gagne facilement. Une fois que nous arrivons à une table de taille raisonnable, les deux moteurs s’échelonnent linéairement avec la taille de la sous-sélection. L'index accélère la commande MyISAM mais a peu d'effet intéressant sur la vitesse InnoDB. 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) )
Je pense que le message à retenir de tout cela est que si vous êtes vraiment préoccupé par la vitesse, vous devez analyser les requêtes que vous faites plutôt que de faire des suppositions quant au moteur le plus approprié.
SELECT * FROM tbl WHERE index_col = xx
- Deux facteurs sont susceptibles d’entraîner davantage de variations dans le graphique: clé primaire vs clé secondaire; l'index est mis en cache vs pas.
SELECT COUNT(*)
est un gagnant clair pour MyISAM jusqu'à ce que vous ajoutiez une WHERE
clause.
Lequel est plus vite? Soit pourrait être plus rapide. YMMV.
Lequel devriez-vous utiliser? InnoDB - sans risque d'accident, etc.