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?
Bagaimana MyISAM bisa "lebih cepat" dari InnoDB jika
Jawaban:
Satu-satunya cara MyISAM bisa lebih cepat dari itu InnoDB akan berada di bawah keadaan unik ini
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
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 I
in 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 .
Karena InnoDB dan MyISAM melakukan disk I / O, faktor acak apa yang menentukan siapa yang lebih cepat?
DELETEs
danUPDATEs
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.
Rencanakan tipe data, kueri, dan mesin penyimpanan Anda dengan saksama. Setelah data tumbuh, mungkin akan sangat sulit untuk memindahkan data. Tanyakan saja ke Facebook ...
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.
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 full
ketika Anda melebihi batas memori MySQL). Empat jenis pilih yang saya lihat adalah:
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.
Pertanyaan: SELECT * FROM tbl WHERE index_col = xx
Hasil: menggambar
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) )
Pertanyaan: SELECT count(*) FROM tbl
Hasil: MyISAM menang
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) )
Pertanyaan: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
Hasil: MyISAM menang
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) )
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.
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.
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.
SELECT COUNT(*)
adalah pemenang yang jelas untuk MyISAM sampai Anda menambahkan WHERE
klausa.
Mana yang lebih cepat? Mungkin lebih cepat. YMMV.
Yang mana yang harus Anda gunakan? InnoDB - crash-safe, dll, dll.