Відповіді:
Єдиний спосіб MyISAM може бути швидшим, щоб InnoDB опинився під цією унікальною обставиною
Під час читання індекси таблиці MyISAM можна прочитати один раз з файлу .MYI та завантажити в кеш-пам'ять MyISAM (розмір за розміром key_buffer_size ). Як можна швидше зробити .MYD таблиці MyISAM швидше для читання? З цим:
ALTER TABLE mytable ROW_FORMAT=Fixed;
Про це я писав у своїх минулих дописах
Добре, а як щодо InnoDB? Чи InnoDB робить якісь дискові введення / виведення для запитів? Дивно, але так! Ви, напевно, думаєте, що я божевільний, що це сказав, але це абсолютно вірно навіть для SELECT запитів . На даний момент ви, мабуть, замислюєтесь: "Як у світі InnoDB робить дискові введення / виведення для запитів?"
Все повертається до InnoDB, будучи двигуном транзакційного зберігання ACID- скаргами. Для того , щоб бути InnoDB транзакційних, він повинен підтримувати I
в системі ACID
, яка є ізоляцією. Техніка підтримки ізоляції для транзакцій здійснюється за допомогою MVCC, Multiversion Concurrency Control . Простіше кажучи, InnoDB записує, як виглядають дані, перш ніж транзакції намагаються їх змінити. Де це записується? У файлі системного простору таблиць, більш відомий як ibdata1. Для цього потрібен диск вводу / виводу .
Оскільки і InnoDB, і MyISAM роблять диск вводу / виводу, які випадкові фактори диктують, хто швидший?
DELETEs
таUPDATEs
Таким чином, у важкому для читання середовищі можна, щоб таблиця MyISAM з фіксованим форматом рядків перевершила InnoDB зчитування з пулу InnoDB Buffer, якщо в журнали відміни, що містяться в ibdata1, є достатньо даних для підтримки поведінки транзакцій накладені на дані InnoDB.
Ретельно плануйте типи даних, запити та механізм зберігання даних. Як тільки дані зростають, переміщення даних може стати дуже важким. Просто запитайте у Facebook ...
У простому світі MyISAM швидше читає, InnoDB - швидше для запису.
Після того, як ви почнете вводити змішане читання / запис, InnoDB також стане швидшим для читання, завдяки механізму блокування рядків.
Я написав порівняння двигунів зберігання даних MySQL кілька років тому, що до цих пір залишається вірним, окреслюючи унікальні відмінності між MyISAM та InnoDB.
На мій досвід, ви повинні використовувати InnoDB для всього, окрім як для важких кеш-таблиць для читання, де втрата даних через корупцію не є настільки важливою.
Щоб додати тут відповіді, що охоплюють механічні відмінності між двома двигунами, я представляю емпіричне дослідження порівняння швидкості.
Що стосується чистої швидкості, то не завжди буває так, що MyISAM швидше, ніж InnoDB, але, на мій досвід, це, як правило, швидше для робочих середовищ PURE READ в 2,0-2,5 рази. Зрозуміло, що це не підходить для всіх середовищ - як писали інші, MyISAM не вистачає таких речей, як транзакції та зовнішні ключі.
Я трохи провів порівняльний аналіз нижче - я використав python для циклічного циклу та бібліотеку timeit для порівняння часу. Для інтересу я також включив двигун пам'яті, це дає найкращі показники в усьому світі, хоча він підходить лише для менших таблиць (ви постійно стикаєтесь, The table 'tbl' is full
коли перевищуєте ліміт пам’яті MySQL). Я переглядаю чотири типи вибору:
По-перше, я створив три таблиці за допомогою наступного 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" та "пам'ять" у другій та третій таблицях.
Запит: 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) )
Запит: 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) )
Запит: 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) )
Результат: 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) )
Я думаю, що повідомлення про все це - якщо ви дійсно стурбовані швидкістю, вам потрібно порівняти запити, які ви робите, а не робити припущення щодо того, який двигун буде більш підходящим.
SELECT * FROM tbl WHERE index_col = xx
- Ось два фактори, які можуть призвести до більшої зміни в графіку: Первинний ключ та вторинний ключ; індекс кешований проти ні.
SELECT COUNT(*)
є явним переможцем для MyISAM, поки ви не додасте WHERE
пункт.
Що швидше? Або може бути швидше. YMMV.
Який слід використовувати? InnoDB - безпечний для аварій, тощо.