MyISAM nasıl InnoDB'den daha "hızlı" olabilir?
- MyISAM'ın veri için disk okuma yapması gerekiyor mu?
- InnoDB, dizinler ve veriler için arabellek havuzunu ve sadece dizin için MyISAM'i kullanır mı?
MyISAM nasıl InnoDB'den daha "hızlı" olabilir?
Yanıtlar:
MyISAM’in InnoDB’nin bu benzersiz koşulda daha hızlı olmasının tek yolu
Okunduğunda, bir MyISAM tablosunun dizinleri bir kez .MYI dosyasından okunabilir ve MyISAM Anahtar Önbelleğine yüklenebilir ( key_buffer_size tarafından boyutlandırıldığı gibi ). Bir MyISAM masasının .MYD'sini okumak için daha hızlı nasıl yapabilirsiniz? Bununla:
ALTER TABLE mytable ROW_FORMAT=Fixed;
Bunu geçmiş yazılarımda yazdım
Tamam, peki ya InnoDB? InnoDB, sorgular için herhangi bir disk G / Ç yapıyor mu? Şaşırtıcı, evet öyle !! Muhtemelen bunu söylediğim için deli olduğumu düşünüyorsunuz, ancak SELECT sorguları için bile kesinlikle doğru . Bu noktada, muhtemelen merak ediyorsanız "InnoDB dünyada nasıl sorgular için disk G / Ç yapıyor?"
Her şey bir ACID- şikayet İşlemsel Depolama Motoru olarak InnoDB'ye dayanıyor. InnoDB'nin İşlem Yapabilmesi için İzolasyon olan I
girişi desteklemesi gerekir ACID
. İşlemlerde izolasyonun korunmasına yönelik teknik, MVCC, Multiversion Concurrency Control aracılığıyla gerçekleştirilir . Basit bir ifadeyle, InnoDB, işlemlerin onları değiştirmeyi denemeden önce verinin nasıl göründüğünü kaydeder. Bu nerede kaydedilir? Sistem tablo alanı dosyasında, ibdata1 olarak daha iyi bilinir. Bu disk G / Ç gerektirir .
Hem InnoDB hem de MyISAM disk I / O'yu yaptığından, rastgele faktörler kimin daha hızlı olduğunu belirler?
DELETEs
veUPDATEs
Bu nedenle, yoğun okunan bir ortamda, Sabit Davranış Formatına sahip bir MyISAM tablasının, işlem davranışını desteklemek için ibdata1 içinde yer alan geri alma günlüklerine yazılan yeterli veri varsa, InnoDB Tampon Havuzundan daha iyi okunması mümkündür. InnoDB verilerine dayatılan.
Veri türlerinizi, sorgularınızı ve depolama motorunuzu çok dikkatli bir şekilde planlayın. Veriler büyüdükçe, verileri dolaşmak çok zor olabilir. Sadece Facebook'a sor ...
Basit bir dünyada, MyISAM okuma için daha hızlı, InnoDB ise yazma için daha hızlı.
Karışık okuma / yazma işlemine başladığınızda, InnoDB, Row kilitleme mekanizması sayesinde okumalar için daha hızlı olacaktır.
Birkaç yıl önce MySQL depolama motorlarının bir karşılaştırmasını yazdım , bu hala geçerli.
Tecrübelerime göre, InnoDB'yi yolsuzluk nedeniyle veri kaybetmenin kritik olmadığı okuma ağırlıklı önbellek tabloları dışındaki her şey için kullanmalısınız.
Buradaki iki motor arasındaki mekanik farklılıkları kapsayan cevapları eklemek için ampirik bir hız karşılaştırma çalışması sunuyorum.
Saf hız açısından, her zaman MyISAM'ın InnoDB'den daha hızlı olması söz konusu değildir, ancak benim deneyimimde PURE READ çalışma ortamları için yaklaşık 2.0-2.5 kat faktörle daha hızlı olma eğilimindedir. Açıkçası bu, tüm ortamlar için uygun değildir - diğerlerinin yazdığı gibi, MyISAM, işlemler ve yabancı anahtarlar gibi şeylerden yoksundur.
Aşağıda bir miktar kıyaslama yaptım - döngü için python ve zamanlama karşılaştırmaları için timeit kütüphanesini kullandım. İlgilendiğim bellek motorunu da dahil ettim, bu sadece daha küçük tablolar için uygun olmasına rağmen anakartta en iyi performansı veriyor (sürekli The table 'tbl' is full
MySQL bellek limitini aştığınızda karşılaşıyorsunuz ). Baktığım dört tip seçim:
İlk olarak, aşağıdaki SQL'i kullanarak üç tablo oluşturdum.
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' ile ikinci ve üçüncü tablolarda 'InnoDB' ve 'hafıza' ile değiştirildi.
Sorgu: SELECT * FROM tbl WHERE index_col = xx
Sonuç: çizmek
Bunların hızı genel olarak aynıdır ve beklendiği gibi, seçilecek sütun sayısında doğrusaldır. InnoDB, MyISAM'den biraz daha hızlı görünüyor , ancak bu gerçekten marjinal.
Kod:
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) )
Sorgu: SELECT count(*) FROM tbl
Sonuç: MyISAM kazandı
Bu, MyISAM ve InnoDB arasında büyük bir fark olduğunu gösteriyor - MyISAM (ve bellek), tablodaki kayıt sayısını takip ediyor, bu yüzden bu işlem hızlı ve O (1). InnoDB'nin sayması için gereken süre, araştırdığım aralıktaki tablo boyutuyla süper doğrusal olarak artar. Uygulamada gözlemlenen MyISAM sorgularının hızlanmasının çoğunun benzer etkilerden kaynaklandığından şüpheleniyorum.
Kod:
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) )
Sorgu: SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
Sonuç: MyISAM kazandı
Burada, MyISAM ve bellek yaklaşık olarak aynı performans gösteriyor ve daha büyük tablolar için InnoDB'yi yaklaşık% 50 oranında yendi. Bu, MyISAM'in yararlarının en üst düzeye çıktığı bir sorgu türüdür.
Kod:
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) )
Sonuç: InnoDB kazandı
Bu sorgu için alt seçim için ek bir tablo seti oluşturdum. Her biri, biri birincil anahtar dizini, diğeri de dizini olmayan iki BÜYÜK sütuntan ibarettir. Büyük masa boyutu nedeniyle, hafıza motorunu test etmedim. SQL tablosu oluşturma komutu
CREATE TABLE
subselect_myisam
(
index_col bigint NOT NULL,
non_index_col bigint,
PRIMARY KEY (index_col)
)
ENGINE=MyISAM DEFAULT CHARSET=utf8;
bir kez daha, ikinci tabloda 'MyISAM', 'InnoDB' ile değiştirildi.
Bu sorguda, seçim tablosunun boyutunu 1000000 olarak bırakıyorum ve bunun yerine seçilen alt sütunların boyutunu değiştiriyorum.
İşte InnoDB kolayca kazanıyor. Makul bir boyut tablosuna ulaştıktan sonra her iki motor da alt seçimin boyutuna göre doğrusal olarak ölçeklenir. Dizin MyISAM komutunu hızlandırıyor ancak ilginç bir şekilde InnoDB hızı üzerinde çok az etkisi var. subSelect.png
Kod:
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) )
Bence bunların hepsinin geri dönüş mesajı, gerçekten hız konusunda endişe duyuyorsanız, hangi motorun daha uygun olacağı konusunda herhangi bir varsayımda bulunmak yerine, yaptığınız sorguları karşılaştırmanız gerektiğidir.
SELECT * FROM tbl WHERE index_col = xx
- İşte grafikte daha fazla çeşitliliğe yol açabilecek iki faktör var: Birincil anahtar ile ikincil anahtar; İndeks önbelleklenir vs değil.
SELECT COUNT(*)
WHERE
madde eklemek kadar MyISAM için açık bir kazanan .
Hangisi daha hızlı? Her ikisi de daha hızlı olabilir. YMMV.
Hangisini kullanmalısın? InnoDB - çarpışma emniyetli vb.