¿Cómo puede MyISAM ser "más rápido" que InnoDB si
- MyISAM necesita hacer lecturas de disco para los datos?
- InnoDB usa el grupo de búferes para índices y datos, y MyISAM solo para el índice.
¿Cómo puede MyISAM ser "más rápido" que InnoDB si
Respuestas:
La única forma en que MyISAM puede ser más rápido que InnoDB sería bajo esta circunstancia única
Cuando se lee, los índices de una tabla MyISAM se pueden leer una vez desde el archivo .MYI y cargarse en el caché de claves MyISAM (según el tamaño de key_buffer_size ). ¿Cómo puede hacer que una tabla MyISAM .MYD sea más rápida de leer? Con este:
ALTER TABLE mytable ROW_FORMAT=Fixed;
Escribí sobre esto en mis publicaciones anteriores
OK, ¿qué hay de InnoDB? ¿InnoDB realiza alguna E / S de disco para consultas? Sorprendentemente, sí lo hace! Probablemente esté pensando que estoy loco por decir eso, pero es absolutamente cierto, incluso para consultas SELECT . En este punto, probablemente se esté preguntando "¿Cómo está InnoDB haciendo E / S de disco para consultas?"
Todo se remonta a que InnoDB es un motor de almacenamiento transaccional de queja ACID . Para que InnoDB sea Transaccional, debe admitir I
in ACID
, que es Aislamiento. La técnica para mantener el aislamiento de las transacciones se realiza a través de MVCC, Control de concurrencia multiversional . En términos simples, InnoDB registra el aspecto de los datos antes de que las transacciones intenten cambiarlos. ¿Dónde se graba eso? En el archivo de espacio de tabla del sistema, mejor conocido como ibdata1. Eso requiere E / S de disco .
Dado que tanto InnoDB como MyISAM hacen E / S de disco, ¿qué factores aleatorios determinan quién es más rápido?
DELETEs
yUPDATEs
Por lo tanto, en un entorno de lectura pesada, es posible que una tabla MyISAM con un formato de fila fija supere las lecturas de InnoDB del InnoDB Buffer Pool si se escriben suficientes datos en los registros de deshacer contenidos en ibdata1 para admitir el comportamiento transaccional impuesta a los datos de InnoDB.
Planifique sus tipos de datos, consultas y motor de almacenamiento con mucho cuidado. Una vez que los datos crecen, puede ser muy difícil mover los datos. Solo pregúntale a Facebook ...
En un mundo simple, MyISAM es más rápido para lecturas, InnoDB es más rápido para escrituras.
Una vez que comience a introducir lecturas / escrituras mixtas, InnoDB también será más rápido para las lecturas, gracias a su mecanismo de bloqueo de filas.
Escribí una comparación de los motores de almacenamiento MySQL hace unos años, que todavía es verdad hasta el día de hoy, describiendo las diferencias únicas entre MyISAM e InnoDB.
En mi experiencia, debería usar InnoDB para todo excepto para las tablas de caché de lectura pesada, donde la pérdida de datos debido a la corrupción no es tan crítica.
Para agregar a las respuestas aquí que cubren las diferencias mecánicas entre los dos motores, presento un estudio empírico de comparación de velocidad.
En términos de velocidad pura, no siempre es el caso de que MyISAM sea más rápido que InnoDB, pero en mi experiencia tiende a ser más rápido para entornos de trabajo PURE READ en un factor de aproximadamente 2.0-2.5 veces. Claramente, esto no es apropiado para todos los entornos: como otros han escrito, MyISAM carece de cosas como transacciones y claves foráneas.
He hecho un poco de evaluación comparativa a continuación: he usado python para bucles y la biblioteca timeit para comparaciones de temporización. Por interés, también he incluido el motor de memoria, que proporciona el mejor rendimiento en todos los ámbitos, aunque solo es adecuado para tablas más pequeñas (se encuentra continuamente The table 'tbl' is full
cuando excede el límite de memoria de MySQL). Los cuatro tipos de selección que miro son:
Primero, creé tres tablas usando el siguiente 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' sustituido por 'InnoDB' y 'memoria' en las tablas segunda y tercera.
Consulta: SELECT * FROM tbl WHERE index_col = xx
Resultado: empate
La velocidad de estos es, en general, la misma, y como se espera es lineal en el número de columnas que se seleccionarán. InnoDB parece un poco más rápido que MyISAM pero esto es realmente marginal.
Código:
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) )
Consulta: SELECT count(*) FROM tbl
Resultado: MyISAM gana
Éste demuestra una gran diferencia entre MyISAM e InnoDB: MyISAM (y la memoria) realiza un seguimiento del número de registros en la tabla, por lo que esta transacción es rápida y O (1). La cantidad de tiempo requerida para que InnoDB cuente aumenta de forma superlineal con el tamaño de la tabla en el rango que investigué. Sospecho que muchas de las aceleraciones de las consultas de MyISAM que se observan en la práctica se deben a efectos similares.
Código:
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) )
Consulta: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
Resultado: MyISAM gana
Aquí, MyISAM y la memoria funcionan aproximadamente igual, y superan a InnoDB en aproximadamente un 50% para tablas más grandes. Este es el tipo de consulta para la cual los beneficios de MyISAM parecen ser maximizados.
Código:
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) )
Resultado: InnoDB gana
Para esta consulta, creé un conjunto adicional de tablas para la sub-selección. Cada uno es simplemente dos columnas de BIGINT, una con un índice de clave principal y otra sin ningún índice. Debido al gran tamaño de la tabla, no probé el motor de memoria. El comando de creación de la tabla SQL fue
CREATE TABLE
subselect_myisam
(
index_col bigint NOT NULL,
non_index_col bigint,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
donde una vez más, 'MyISAM' se sustituye por 'InnoDB' en la segunda tabla.
En esta consulta, dejo el tamaño de la tabla de selección en 1000000 y, en cambio, varío el tamaño de las columnas subseleccionadas.
Aquí el InnoDB gana fácilmente. Después de llegar a una tabla de tamaño razonable, ambos motores se escalan linealmente con el tamaño de la sub-selección. El índice acelera el comando MyISAM pero, curiosamente, tiene poco efecto en la velocidad de InnoDB. subSelect.png
Código:
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) )
Creo que el mensaje final de todo esto es que si está realmente preocupado por la velocidad, debe comparar las consultas que está haciendo en lugar de hacer suposiciones sobre qué motor será más adecuado.
SELECT * FROM tbl WHERE index_col = xx
- Aquí hay dos factores que pueden conducir a una mayor variación en el gráfico: clave primaria frente a clave secundaria; El índice está en caché frente a no.
SELECT COUNT(*)
es un claro ganador para MyISAM hasta que agregue una WHERE
cláusula.
¿Cual es mas rápido? Cualquiera de los dos podría ser más rápido. YMMV.
¿Cuál deberías usar? InnoDB - a prueba de choques, etc., etc.