Hangisi daha hızlı, InnoDB veya MyISAM?


54

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 OS önbellek sağlayan veri bloklarını, bu nedenle yok değil her zaman "disk veri okuma yapmak".
Rick James,

Yanıtlar:


68

MyISAM’in InnoDB’nin bu benzersiz koşulda daha hızlı olmasının tek yolu

MyISAM

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

InnoDB'nin

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 Igiriş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 .

KARŞILAŞTIRMA

Hem InnoDB hem de MyISAM disk I / O'yu yaptığından, rastgele faktörler kimin daha hızlı olduğunu belirler?

  • Sütunların Boyutu
  • Sütun biçimi
  • Karakter setleri
  • Sayısal Değerler Aralığı (yeterince büyük INT'ler gerektirir)
  • Satırlar Bloklar Arasında Bölünüyor (Satır Zincirleme)
  • Veri Parçalanma neden olduğu DELETEsveUPDATEs
  • Birincil Anahtarın Boyutu (InnoDB, iki anahtar arama gerektiren Kümelenmiş Bir Dizine sahiptir)
  • Dizin Girişlerinin Boyutu
  • liste devam ediyor...

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.

SONUÇ

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


1
Mükemmel cevap Rolando. Sadece kendi ürününü satmaya çalışan ve Facebook hakkında hiçbir şey bilmeyen Michael Stonebreaker'in yaptığı inanılmaz iddiaları dahil etmenizi sorgulamam gerekiyor. Facebook'un MySQL hakkındaki sunumunu birkaç kez dinledikten sonra, seçimlerinde rahat oldukları açıktır.
Aaron Brown

@AaronBrown Geçen yıl Percona Live NYC'de Harrison Fisk'i dinledim ve haklısınız - Facebook, InnoDB'nin özel kullanımından ve çevrimiçi şema değişikliği sistemini geniş bir şekilde yapmanın yollarını bulmak için nasıl zaman harcadıklarını çok mutlu ediyor. Hatta izleyicilere Facebook'un büyük verileri yönetme konusunda çalışma şansı bile sunuyor. Bazılarının bu konuda endişeleri olduğunu göstermek için makaleyi ekledim. Büyük verilerle çalışma şansını memnuniyetle karşılardım. Eğlenceli ve zorlu olurdu. Öğrenilmesi gereken teknikleri hayal edin. Tabii ki, hayatımın sonuna kadar MyISAM'a asla dokunmam ...
RolandoMySQLDBA

Ayrıca o konferanstaydım (ve bir konuşma yapabilmek için iyi bir şansım vardı) & Harrison'ın sunumu mükemmeldi.
Aaron Brown

20

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.


4
Bu Cevap 5 yıl eskidir. InnoDB neredeyse her yönden yakalandı; MyISAM kullanmak için artık fazla bir tartışma yok. MySQL 8.0, MyISAM'i hep birlikte kaldırma sürecinde .
Rick James

2
Ve bağlantı şimdi 9 yıl eski.
Rick James

Düzeltme, cevabın tarihi 9 yıl (ilk cümleyi okuyan herkes Veritabanını yaparken ciddi sorunlar yaşayacaktır) ve bağlantı 11 yıl eski olacak. Yakala Rick James, geride kalıyorsun :).
CYREX

1
Haklısın @CYREX :-) Bu yayın 11 yıl sonra hala trafik alıyor. Hem hayatımda hem de InnoDB'nin optimizasyonu konusunda çok şey değişti. Günümüzde, MyISAM'ı kullanmak için nadiren bir gerekçe yoktur
Mike Peters

Bugün ölmekte olan bazı veritabanlarına bakmak zorunda kaldım ve her iki motor hala eski mysql versiyonuyla kullanılıyor. Tablolar hem InnoDB hem de MyISAM ve merakım beni çok yardımcı olan bu yazıya getirdi.
Farrukh Subhani

14

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 fullMySQL bellek limitini aştığınızda karşılaşıyorsunuz ). Baktığım dört tip seçim:

  1. vanilya SELECT
  2. sayımları
  3. koşullu SELECT
  4. dizine alınmış ve dizine eklenmemiş alt seçimler

İ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.

 

1) Vanilya seçer

Sorgu: SELECT * FROM tbl WHERE index_col = xx

Sonuç: çizmek

Vanilya seçimlerinin farklı veri tabanı motorlarıyla karşılaştırılması

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

 

2) sayar

Sorgu: SELECT count(*) FROM tbl

Sonuç: MyISAM kazandı

Sayımların farklı veritabanı motorları ile karşılaştırılması

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

 

3) Koşullu seçimler

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

Sonuç: MyISAM kazandı

Koşullu seçimlerin farklı veritabanı motorları ile karşılaştırılması

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

 

4) Alt seçer

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.

Alt seçimin farklı veritabanı motorları ile karşılaştırılması

İş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.


1
Cevabını beğendim çünkü karşılaştırdığın ve karar verdiğin kişi lehine. İki sistem farklı depolama motorlarından aynı şekilde faydalanmamaktadır ve bir depolama motoru seçmek için gereken özen gösterilmesi gerekmektedir. +1 sizin için ve DBA StackExchange'e Hoş Geldiniz !!!
RolandoMySQLDBA

1
Ayrıca, diğer cevapların yanı sıra dba.stackexchange.com/questions/1/… adresime de bak . Göreviniz biraz yukarıda ve öteye gidiyor.
RolandoMySQLDBA

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.
Rick James,

2
SELECT COUNT(*)WHEREmadde eklemek kadar MyISAM için açık bir kazanan .
Rick James,

Demek istediğim, her bir sorgunun ayrı ayrı kıyaslanması gerektiğidir. Kodu cevaba ekledim - eğer farklı bir sorgu denemek isterseniz, lütfen benim misafirim olun - ya da hangi soruyu istediğinizi açıkça belirtin ve ekleyeceğim.
StackG

4

Hangisi daha hızlı? Her ikisi de daha hızlı olabilir. YMMV.

Hangisini kullanmalısın? InnoDB - çarpışma emniyetli vb.


lütfen, "vb." tanımını yapınız.
dellasavia

1
@dellasavia - En son "etc", Oracle'ın MyISAM'ı kaldırmayı planladığı yönünde. InnoDB'ye o kadar güveniyorlar ki.
Rick James
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.