Mana yang lebih cepat, InnoDB atau MyISAM?

54

Bagaimana MyISAM bisa "lebih cepat" dari InnoDB jika

  • MyISAM perlu melakukan pembacaan disk untuk data?
  • InnoDB menggunakan kumpulan buffer untuk indeks dan data, dan MyISAM hanya untuk indeks?
jcho360
sumber
MyISAM memungkinkan blok data cache OS , sehingga tidak selalu "melakukan pembacaan disk untuk data".
Rick James

Jawaban:

68

Satu-satunya cara MyISAM bisa lebih cepat dari itu InnoDB akan berada di bawah keadaan unik ini

MyISAM

Saat dibaca, indeks tabel MyISAM dapat dibaca sekali dari file .MYI dan dimuat di Cache Kunci MyISAM (sesuai ukuran dengan key_buffer_size ). Bagaimana Anda bisa membuat .MYD tabel MyISAM lebih cepat dibaca? Dengan ini:

ALTER TABLE mytable ROW_FORMAT=Fixed;

Saya menulis tentang ini di posting saya sebelumnya

InnoDB

OK, bagaimana dengan InnoDB? Apakah InnoDB melakukan disk I / O untuk kueri? Anehnya, ya itu benar !! Anda mungkin berpikir saya gila karena mengatakan itu, tetapi itu benar-benar benar, bahkan untuk pertanyaan SELECT . Pada titik ini, Anda mungkin bertanya-tanya "Bagaimana mungkin InnoDB melakukan disk I / O untuk permintaan?"

Semuanya kembali ke InnoDB menjadi Mesin Penyimpanan Transaksional ACID -complaint. Agar InnoDB menjadi Transaksional, ia harus mendukung Iin ACID, yaitu Isolasi. Teknik untuk menjaga isolasi untuk transaksi dilakukan melalui MVCC, Multiversion Concurrency Control . Secara sederhana, InnoDB mencatat data seperti apa sebelum transaksi mencoba mengubahnya. Di mana itu dicatat? Dalam file tablespace sistem, lebih dikenal sebagai ibdata1. Yang membutuhkan disk I / O .

PERBANDINGAN

Karena InnoDB dan MyISAM melakukan disk I / O, faktor acak apa yang menentukan siapa yang lebih cepat?

  • Ukuran Kolom
  • Format kolom
  • Set Karakter
  • Rentang Nilai Numerik (membutuhkan INT yang cukup besar)
  • Baris yang Dipisah Melintang Blok (Row Chaining)
  • Fragmentasi data yang disebabkan oleh DELETEsdanUPDATEs
  • Ukuran Kunci Utama (InnoDB memiliki Indeks Berkelompok, membutuhkan dua pencarian kunci)
  • Ukuran Entri Indeks
  • daftar berjalan ...

Dengan demikian, dalam lingkungan yang banyak dibaca, dimungkinkan untuk tabel MyISAM dengan Format Baris Tetap untuk mengungguli InnoDB membaca dari Pool Buffer InnoDB jika ada cukup data yang ditulis ke dalam undo log yang terkandung dalam ibdata1 untuk mendukung perilaku transaksional dikenakan pada data InnoDB.

KESIMPULAN

Rencanakan tipe data, kueri, dan mesin penyimpanan Anda dengan saksama. Setelah data tumbuh, mungkin akan sangat sulit untuk memindahkan data. Tanyakan saja ke Facebook ...

RolandoMySQLDBA
sumber
1
Jawaban yang bagus, Rolando. Saya harus mempertanyakan pencantuman Anda atas klaim tidak percaya yang dibuat oleh Michael Stonebreaker, yang hanya mencoba menjual produknya sendiri dan tidak tahu apa-apa tentang Facebook. Setelah mendengarkan hadiah Facebook tentang MySQL beberapa kali, jelas bahwa mereka merasa nyaman dengan pilihan mereka.
Aaron Brown
@AaronBrown Saya telah mendengarkan Harrison Fisk tahun lalu di Percona Live NYC dan Anda benar - Facebook sangat senang dengan penggunaan eksklusif mereka dari InnoDB dan bagaimana mereka menghabiskan waktu mencari cara untuk melakukan skema perubahan sistem online secara luas. Dia bahkan menawarkan kesempatan kepada audiens untuk bekerja agar Facebook menangani data besar. Saya memasukkan artikel untuk menunjukkan bahwa ada yang khawatir tentang hal itu. Saya akan menyambut baik kesempatan untuk bekerja dengan data yang sangat besar. Itu akan menyenangkan dan menantang. Bayangkan teknik yang harus dipelajari. Tentu saja, saya tidak akan pernah menyentuh MyISAM selama sisa hidup saya ...
RolandoMySQLDBA
Saya juga hadir di konferensi itu (dan mendapat keberuntungan untuk dapat memberikan ceramah) & Presentasi Harrison sangat baik.
Aaron Brown
20

Dalam dunia yang sederhana, MyISAM lebih cepat untuk dibaca, InnoDB lebih cepat untuk menulis.

Setelah Anda mulai memperkenalkan campuran baca / tulis, InnoDB akan lebih cepat untuk dibaca juga, berkat mekanisme penguncian Row.

Saya menulis perbandingan mesin penyimpanan MySQL beberapa tahun yang lalu, yang masih berlaku sampai hari ini, menguraikan perbedaan unik antara MyISAM dan InnoDB.

Dalam pengalaman saya, Anda harus menggunakan InnoDB untuk semuanya kecuali untuk cache-tabel read-heavy, di mana kehilangan data karena korupsi tidak begitu penting.

Mike Peters
sumber
4
Jawaban ini 5 tahun kedaluwarsa. InnoDB telah menangkap hampir semua hal; tidak ada lagi banyak argumen untuk menggunakan MyISAM. MySQL 8.0 sedang dalam proses menghapus MyISAM bersama-sama.
Rick James
2
Dan tautannya sekarang 9 tahun kedaluwarsa.
Rick James
Koreksi, jawabannya adalah 9 tahun kedaluwarsa (siapa pun yang membaca kalimat pertama akan memiliki beberapa masalah serius ketika melakukan Database mereka) dan tautannya 11 tahun kedaluwarsa. Tangkap Rick James, kau tertinggal :).
CYREX
1
Anda benar @CYREX :-) Hebatnya posting ini masih mendapatkan traffic, 11 tahun kemudian. Begitu banyak yang telah berubah dalam hidup saya dan cara InnoDB dioptimalkan. Saat ini, jarang ada pembenaran untuk menggunakan MyISAM
Mike Peters
Saya harus melihat beberapa database sekarat hari ini dan kedua mesin masih digunakan dengan versi lama mysql. Tabelnya adalah InnoDB dan MyISAM dan rasa ingin tahu saya membawa saya ke pos ini yang sangat membantu.
Farrukh Subhani
14

Untuk menambah tanggapan di sini meliputi perbedaan mekanis antara dua mesin, saya menyajikan studi perbandingan kecepatan empiris.

Dalam hal kecepatan murni, itu tidak selalu terjadi bahwa MyISAM lebih cepat dari InnoDB tetapi dalam pengalaman saya cenderung lebih cepat untuk lingkungan kerja MURNI BACA dengan faktor sekitar 2,0-2,5 kali. Jelas ini tidak sesuai untuk semua lingkungan - seperti yang ditulis orang lain, MyISAM tidak memiliki hal-hal seperti transaksi dan kunci asing.

Saya telah melakukan sedikit pembandingan di bawah ini - Saya telah menggunakan python untuk perulangan dan perpustakaan timeit untuk perbandingan waktu. Untuk menarik saya juga menyertakan mesin memori, ini memberikan kinerja terbaik di seluruh papan meskipun hanya cocok untuk tabel yang lebih kecil (Anda terus menerus menemukan The table 'tbl' is fullketika Anda melebihi batas memori MySQL). Empat jenis pilih yang saya lihat adalah:

  1. PILIH vanilla
  2. penting
  3. SELECT bersyarat
  4. sub-seleks yang diindeks dan tidak diindeks

Pertama, saya membuat tiga tabel menggunakan SQL berikut

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

dengan 'MyISAM' menggantikan 'InnoDB' dan 'memori' di tabel kedua dan ketiga.

 

1) Vanilla memilih

Pertanyaan: SELECT * FROM tbl WHERE index_col = xx

Hasil: menggambar

Perbandingan vanilla dipilih oleh mesin basis data yang berbeda

Kecepatannya semua sama, dan seperti yang diharapkan linear dalam jumlah kolom yang akan dipilih. InnoDB tampaknya sedikit lebih cepat daripada MyISAM tetapi ini benar-benar marjinal.

Kode:

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

Pertanyaan: SELECT count(*) FROM tbl

Hasil: MyISAM menang

Perbandingan jumlah dengan mesin database yang berbeda

Yang ini menunjukkan perbedaan besar antara MyISAM dan InnoDB - MyISAM (dan memori) melacak jumlah catatan dalam tabel, sehingga transaksi ini cepat dan O (1). Jumlah waktu yang diperlukan untuk InnoDB untuk menghitung meningkat secara super-linear dengan ukuran tabel dalam kisaran yang saya selidiki. Saya menduga banyak percepatan dari pertanyaan MyISAM yang diamati dalam praktek adalah karena efek yang sama.

Kode:

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

Pertanyaan: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5

Hasil: MyISAM menang

Perbandingan pilihan kondisional oleh mesin database yang berbeda

Di sini, MyISAM dan memori melakukan kira-kira sama, dan mengalahkan InnoDB sekitar 50% untuk tabel yang lebih besar. Ini adalah jenis permintaan yang manfaat MyISAM tampaknya dimaksimalkan.

Kode:

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

Hasil: InnoDB menang

Untuk kueri ini, saya membuat satu set tabel tambahan untuk sub-pilih. Masing-masing hanya dua kolom BIGINTs, satu dengan indeks kunci utama dan satu tanpa indeks apa pun. Karena ukuran meja yang besar, saya tidak menguji mesin memori. Perintah pembuatan tabel SQL adalah

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

di mana sekali lagi, 'MyISAM' diganti untuk 'InnoDB' di tabel kedua.

Dalam kueri ini, saya meninggalkan ukuran tabel pemilihan pada 1000000 dan sebagai gantinya memvariasikan ukuran kolom yang dipilih.

Perbandingan sub-seleksi oleh berbagai mesin basis data

Di sini InnoDB menang dengan mudah. Setelah kita sampai ke tabel ukuran yang masuk akal, kedua mesin berskala linier dengan ukuran sub-pilih. Indeks mempercepat perintah MyISAM tetapi menarik tidak banyak berpengaruh pada kecepatan InnoDB. subSelect.png

Kode:

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

Saya pikir pesan yang bisa dibawa pulang dari semua ini adalah bahwa jika Anda benar - benar peduli tentang kecepatan, Anda perlu membandingkan permintaan yang Anda lakukan daripada membuat asumsi tentang mesin mana yang akan lebih cocok.

StackG
sumber
1
Saya menyukai jawaban Anda karena itu menguntungkan siapa pun yang Anda tolak dan putuskan. Tidak ada dua sistem yang mendapatkan manfaat yang sama dari mesin penyimpanan yang berbeda dan uji tuntas diperlukan untuk memilih mesin penyimpanan. +1 untuk Anda dan Selamat Datang di DBA StackExchange !!!
RolandoMySQLDBA
1
Juga, lihat posting saya dba.stackexchange.com/questions/1/… bersama dengan jawaban lainnya. Jenis posting Anda berjalan di atas dan di luar.
RolandoMySQLDBA
SELECT * FROM tbl WHERE index_col = xx- Berikut adalah dua faktor yang cenderung menyebabkan lebih banyak variasi dalam grafik: Kunci primer vs kunci sekunder; indeks di-cache vs tidak.
Rick James
2
SELECT COUNT(*)adalah pemenang yang jelas untuk MyISAM sampai Anda menambahkan WHEREklausa.
Rick James
Saya kira poin saya adalah bahwa setiap permintaan perlu diperbandingkan secara terpisah. Saya telah memasukkan kode dalam jawaban - jika Anda ingin mencoba kueri yang berbeda, harap menjadi tamu saya - atau secara eksplisit kueri apa yang Anda inginkan dan saya akan menambahkannya.
StackG
4

Mana yang lebih cepat? Mungkin lebih cepat. YMMV.

Yang mana yang harus Anda gunakan? InnoDB - crash-safe, dll, dll.

Rick James
sumber
tolong tentukan "dll, dll."
dellasavia
1
@dellasavia - "etc" terbaru adalah Oracle berencana untuk menghapus MyISAM. Mereka sangat percaya diri pada InnoDB.
Rick James