Что быстрее, InnoDB или MyISAM?

54

Как MyISAM может быть «быстрее», чем InnoDB, если

  • MyISAM должен выполнять чтение с диска для данных?
  • InnoDB использует пул буферов для индексов и данных, а MyISAM только для индекса?
jcho360
источник
MyISAM позволяет ОС кэшировать блоки данных , поэтому он не всегда «выполняет чтение с диска для данных».
Рик Джеймс

Ответы:

68

Единственный способ MyISAM может быть быстрее, чем InnoDB в этом уникальном случае

MyISAM

При чтении индексы таблицы MyISAM могут считываться один раз из файла .MYI и загружаться в кэш ключей MyISAM (по размеру key_buffer_size ). Как вы можете сделать .MYD таблицы MyISAM быстрее для чтения? С этим:

ALTER TABLE mytable ROW_FORMAT=Fixed;

Я писал об этом в моих прошлых постах

InnoDB

Хорошо, а как насчет InnoDB? InnoDB выполняет дисковый ввод-вывод для запросов? Удивительно, но да! Вы, наверное, думаете, что я схожу с ума от таких слов, но это абсолютно верно, даже для запросов SELECT . В этот момент вы, вероятно, задаетесь вопросом "Как в мире InnoDB выполняет дисковый ввод-вывод для запросов?"

Все это восходит к InnoDB, являющемуся ACID- жалобой Transactional Storage Engine. Чтобы InnoDB был транзакционным, он должен поддерживать Iin ACID, то есть Isolation. Техника для поддержания изоляции для транзакций осуществляется через MVCC, Multiversion Concurrency Control . Проще говоря, InnoDB записывает, как выглядят данные до того, как транзакции пытаются их изменить. Где это записывается? В системном файле табличного пространства, более известном как ibdata1. Это требует дискового ввода-вывода .

СРАВНЕНИЕ

Поскольку и InnoDB, и MyISAM выполняют дисковый ввод-вывод, какие случайные факторы определяют, кто быстрее?

  • Размер столбцов
  • Формат столбца
  • Наборы символов
  • Диапазон числовых значений (требующих достаточно больших INT)
  • Ряды разбиваются по блокам (цепочка строк)
  • Фрагментация данных, вызванная DELETEsиUPDATEs
  • Размер первичного ключа (InnoDB имеет кластерный индекс, требующий двух ключевых запросов)
  • Размер записей индекса
  • список можно продолжить ...

Таким образом, в среде с интенсивным чтением таблица MyISAM с фиксированным форматом строки может превзойти операции чтения InnoDB из пула буферов InnoDB, если в журналы отмены, содержащиеся в ibdata1, записано достаточно данных для поддержки поведения транзакций. накладывается на данные InnoDB.

ЗАКЛЮЧЕНИЕ

Тщательно планируйте свои типы данных, запросы и механизм хранения. Как только данные растут, их перемещение может стать очень трудным. Просто спросите Facebook ...

RolandoMySQLDBA
источник
1
Отличный ответ, Роландо. Я должен подвергнуть сомнению ваше включение недоверчивых утверждений Майкла Стоунбрейкера, который просто пытается продать свой собственный продукт и ничего не знает о Facebook. Прослушав несколько раз презентацию Facebook о MySQL, стало ясно, что они довольны своим выбором.
Аарон Браун
@AaronBrown Я слушал Харрисона Фиска в прошлом году в Percona Live NYC, и вы правы - Facebook очень доволен их эксклюзивным использованием InnoDB и тем, как они тратят время на поиск способов сделать онлайн-изменение схемы общесистемным. Он даже предложил аудитории возможность работать на Facebook, обрабатывая большие данные. Я включил статью, чтобы показать, что у некоторых есть опасения по этому поводу. Я бы приветствовал возможность работать с огромными данными. Было бы весело и интересно. Вообразите методы, которые нужно изучить. Конечно, я бы никогда не прикоснулся к MyISAM до конца своей жизни ...
RolandoMySQLDBA
Я также был на этой конференции (и мне посчастливилось выступить с докладом), и презентация Харрисона была превосходной.
Аарон Браун
20

В простом мире MyISAM быстрее для чтения, InnoDB быстрее для записи.

Как только вы начнете вводить смешанные операции чтения / записи, InnoDB также будет быстрее для чтения благодаря своему механизму блокировки строк.

Несколько лет назад я написал сравнение механизмов хранения MySQL , которое сохранилось до наших дней, обрисовав в общих чертах уникальные различия между MyISAM и InnoDB.

По моему опыту, вы должны использовать InnoDB для всего, кроме кэш-таблиц с интенсивным чтением, где потеря данных из-за повреждения не так критична.

Майк Питерс
источник
4
Этот ответ устарел на 5 лет. InnoDB догнал практически во всех отношениях; больше нет аргументов для использования MyISAM. MySQL 8.0 находится в процессе удаления MyISAM все вместе.
Рик Джеймс
2
И ссылка сейчас на 9 лет устарела.
Рик Джеймс
Исправление: ответ устарел на 9 лет (у любого, кто читает первое предложение, возникнут серьезные проблемы при работе с базой данных), а ссылка устарела на 11 лет. Догнать Рик Джеймс, вы отстаете :).
CYREX
1
Вы правы @CYREX :-) Удивительно, этот пост все еще получает трафик, 11 лет спустя. Так много изменилось как в моей жизни, так и в том, как оптимизируется InnoDB. В настоящее время редко можно найти оправдание использованию MyISAM
Майк Питерс
Мне пришлось взглянуть на некоторые умирающие базы данных сегодня, и оба движка все еще используются со старой версией mysql. Таблицы как InnoDB, так и MyISAM, и мое любопытство привело меня к этому посту, который был очень полезен.
Фаррух Субхани
14

Чтобы добавить здесь ответы, касающиеся механических различий между двумя двигателями, я приведу эмпирическое исследование сравнения скорости.

С точки зрения чистой скорости, MyISAM не всегда работает быстрее, чем InnoDB, но, по моему опыту, в рабочих средах PURE READ он работает быстрее примерно в 2,0-2,5 раза. Очевидно, что это не подходит для всех сред - как написали другие, в MyISAM отсутствуют такие вещи, как транзакции и внешние ключи.

Ниже я провел несколько сравнительных тестов - я использовал python для циклов и библиотеку timeit для сравнений по времени. Для интереса я также включил механизм памяти, это дает лучшую производительность по всем направлениям, хотя он подходит только для небольших таблиц (вы постоянно сталкиваетесь, The table 'tbl' is fullкогда превышаете лимит памяти MySQL). Я смотрю на четыре типа выбора:

  1. ваниль ВЫБИРАЕТ
  2. счетчики
  3. условный ВЫБОР
  4. индексированные и неиндексированные субвыборы

Во-первых, я создал три таблицы, используя следующий 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

с 'MyISAM', замененным 'InnoDB' и 'memory' во второй и третьей таблицах.

 

1) Ваниль выбирает

Запрос: SELECT * FROM tbl WHERE index_col = xx

Результат: ничья

Сравнение ванильных отборов различными движками базы данных

Скорость их в целом одинакова и, как и ожидалось, является линейной по количеству столбцов, которые будут выбраны. InnoDB кажется немного быстрее, чем MyISAM, но это действительно незначительно.

Код:

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) рассчитывает

Запрос: SELECT count(*) FROM tbl

Результат: MyISAM выигрывает

Сравнение подсчетов различными движками базы данных

Этот демонстрирует большую разницу между MyISAM и InnoDB - MyISAM (и память) отслеживает количество записей в таблице, поэтому эта транзакция быстрая и O (1). Количество времени, необходимое для подсчета InnoDB, увеличивается сверхлинейно с размером таблицы в диапазоне, который я исследовал. Я подозреваю, что многие из ускорений от запросов MyISAM, которые наблюдаются на практике, связаны с подобными эффектами.

Код:

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) Условный выбор

Запрос: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5

Результат: MyISAM выигрывает

Сравнение условных выборок различными движками базы данных

В данном случае MyISAM и память работают примерно одинаково, а InnoDB побеждает примерно на 50% для больших таблиц. Это тот тип запроса, для которого преимущества MyISAM кажутся максимальными.

Код:

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) Подвыбирает

Результат: InnoDB выигрывает

Для этого запроса я создал дополнительный набор таблиц для дополнительного выбора. Каждый из них представляет собой просто два столбца BIGINT, один с индексом первичного ключа, а другой без индекса. Из-за большого размера таблицы я не тестировал движок памяти. Команда создания таблицы SQL была

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

где снова «MyISAM» заменяется на «InnoDB» во второй таблице.

В этом запросе я оставляю размер таблицы выбора на 1000000 и вместо этого изменяю размер выбранных столбцов.

Сравнение подвыборов различными движками базы данных

Здесь InnoDB выигрывает легко. После того, как мы доберемся до таблицы разумных размеров, оба двигателя масштабируются линейно с размером суб-выбора. Индекс ускоряет команду MyISAM, но, что интересно, мало влияет на скорость InnoDB. subSelect.png

Код:

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) )

Я думаю, что основной смысл всего этого заключается в том, что если вы действительно беспокоитесь о скорости, вам нужно сравнивать запросы, которые вы делаете, а не делать какие-либо предположения о том, какой механизм будет более подходящим.

StackG
источник
1
Мне нравится ваш ответ, потому что он в пользу того, кого вы определяете и выбираете. Ни одна из двух систем не может получить одинаковую выгоду от разных механизмов хранения, и для выбора механизма хранения требуется должная осмотрительность. +1 для вас и добро пожаловать в DBA StackExchange !!!
RolandoMySQLDBA
1
Также смотрите мой пост dba.stackexchange.com/questions/1/… вместе с другими ответами. Ваш пост как бы выходит за рамки.
RolandoMySQLDBA
SELECT * FROM tbl WHERE index_col = xx- Вот два фактора, которые могут привести к большей вариации на графике: первичный ключ против вторичного ключа; Индекс кэшируется против нет.
Рик Джеймс
2
SELECT COUNT(*)Явный победитель MyISAM, пока вы не добавите WHEREпредложение.
Рик Джеймс
Я полагаю, что моя точка зрения заключается в том, что каждый запрос должен оцениваться отдельно. Я включил код в ответ - если вы хотите попробовать другой запрос, пожалуйста, будьте моим гостем - или укажите, какой запрос вы хотите, и я добавлю его.
StackG
4

Что быстрее? Либо может быть быстрее. YMMV.

Что вы должны использовать? InnoDB - защита от сбоев и т. Д. И т. Д.

Рик Джеймс
источник
пожалуйста, определите «и т. д.»
Делласавиа
1
@dellasavia - самое последнее «и т. д.» заключается в том, что Oracle планирует удалить MyISAM. Они уверены в InnoDB.
Рик Джеймс