web-dev-qa-db-ja.com

InnoDBとMyISAMのどちらが速いですか?

MyISAMをInnoDBより「高速」にするにはどうすればよいですか

  • MyISAMはデータのディスク読み取りを行う必要がありますか?
  • InnoDBはインデックスとデータにバッファプールを使用し、MyISAMはインデックスにのみ使用しますか?
56
jcho360

MyISAMを高速化できる唯一の方法は、InnoDBがこのユニークな状況下にあることです。

MyISAM

読み取られると、MyISAMテーブルのインデックスは.MYIファイルから1回読み取られ、MyISAMキーキャッシュにロードされます(サイズが key_buffer_size のように)。 MyISAMテーブルの.MYDをより速く読み取るにはどうすればよいですか?これとともに:

ALTER TABLE mytable ROW_FORMAT=Fixed;

これについては過去の投稿で書いた

InnoDB

OK、InnoDBはどうですか? InnoDBはクエリに対してディスクI/Oを行いますか?驚いたことに、そうです!あなたはおそらく私がそう言ったことに夢中だと思っているでしょうが、それは絶対に本当ですSELECTクエリについても。この時点で、「InnoDBはクエリでディスクI/Oをどのように実行しているのでしょうか?」

すべては、InnoDBである [〜#〜] acid [〜#〜] -complaint Transactional Storage Engineに戻ります。 InnoDBをトランザクション対応にするためには、IACIDをサポートする必要があります。これは分離です。トランザクションの分離を維持するための手法は MVCC、Multiversion Concurrency Control によって行われます。簡単に言えば、InnoDBは、トランザクションがデータを変更しようとする前に、どのようなデータかを記録します。それはどこに記録されますか?システムテーブルスペースファイルでは、ibdata1としてよく知られています。 ディスクI/Oが必要

[〜#〜]比較[〜#〜]

InnoDBとMyISAMの両方がディスクI/Oを行うので、どのランダムな要因が誰がより速いかを決定しますか?

  • 列のサイズ
  • 列のフォーマット
  • 文字セット
  • 数値の範囲(十分な大きさのINTが必要)
  • ブロック間で分割されている行(行の連鎖)
  • DELETEsUPDATEsによるデータの断片化
  • 主キーのサイズ(InnoDBにはクラスター化インデックスがあり、2つのキー検索が必要です)
  • インデックスエントリのサイズ
  • リストは続く...

したがって、読み取りの多い環境では、トランザクション動作をサポートするためにibdata1内に含まれるUNDOログに書き込まれる十分なデータがある場合、固定行形式のMyISAMテーブルがInnoDBバッファープールからのInnoDB読み取りよりも優れたパフォーマンスを発揮する可能性があります。 InnoDBデータに課せられます。

[〜#〜]結論[〜#〜]

データタイプ、クエリ、ストレージエンジンを慎重に計画します。データが増大すると、データの移動が非常に困難になる可能性があります。 Facebookに聞いてください...

69
RolandoMySQLDBA

単純な世界では、MyISAMは読み取りが高速で、InnoDBは書き込みが高速です。

読み取り/書き込みの混合を導入すると、行ロックメカニズムのおかげで、InnoDBは読み取りも高速になります。

私は MySQLストレージエンジン の比較を数年前に書きましたが、それは今日にも当てはまり、MyISAMとInnoDBのユニークな違いを概説しています。

私の経験では、破損が原因でデータが失われることはそれほど重要ではない、読み取りが多いキャッシュテーブルを除くすべてにInnoDBを使用する必要があります。

22
Mike Peters

2つのエンジンの機械的な違いをカバーするここでの応答に追加するために、経験的な速度比較研究を提示します。

純粋な速度という点では、MyISAMがInnoDBよりも速いとは限りませんが、私の経験では、PURE READ作業環境では約2.0〜2.5倍高速になる傾向があります。明らかに、これはすべての環境に適しているわけではありません。他の人が書いたように、MyISAMにはトランザクションや外部キーなどがありません。

以下のベンチマークを少し行いました-ループにはpythonを、タイミング比較にはtimeitライブラリを使用しました。メモリエンジンも含めたので、これにより最高のパフォーマンスが得られます。全体的には小さいテーブルにのみ適していますが(MySQLのメモリ制限を超えると、常に_The table 'tbl' is full_に遭遇します)私が見るselectの4つのタイプは次のとおりです。

  1. バニラセレクト
  2. カウント
  3. 条件付きSELECT
  4. インデックス付きおよびインデックスなしのサブセレクト

まず、次のSQLを使用して3つのテーブルを作成しました

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

2番目と3番目のテーブルの「InnoDB」と「メモリ」の代わりに「MyISAM」を使用します。

1)バニラが選択

クエリ:_SELECT * FROM tbl WHERE index_col = xx_

結果:描画

Comparison of Vanilla selects by different database engines

これらの速度はすべてほぼ同じで、予想されるように、選択する列の数は直線的です。 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) )
_

2)カウント

クエリ:SELECT count(*) FROM tbl

結果:MyISAM wins

Comparison of counts by different database engines

これは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) )
_

3)条件付き選択

クエリ:_SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5_

結果:MyISAM wins

Comparison of conditional selects by different database engines

ここでは、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) )
_

4)サブセレクト

結果:InnoDBが勝利

このクエリでは、副選択用に追加のテーブルセットを作成しました。それぞれがBIGINTの2つの列であり、1つは主キーインデックスがあり、もう1つはインデックスがありません。テーブルサイズが大きいため、メモリエンジンはテストしませんでした。 SQLテーブル作成コマンドは

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

ここでも、「MyISAM」が2番目のテーブルの「InnoDB」の代わりに使用されます。

このクエリでは、選択テーブルのサイズを1000000のままにして、代わりにサブ選択列のサイズを変更します。

Comparison of sub-selects by different database engines

ここで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) )
_

私はこれすべての持ち帰りメッセージが速度に本当に心配しているなら、あなたがしているクエリをベンチマークする必要があるということだと思いますどのエンジンがより適切であるかについての仮定を行うのではなく。

14
StackG

どちらが速いですか?どちらかが速いかもしれません。 YMMV。

あなたはどちらを使うべきですか? InnoDB-クラッシュセーフなど.

4
Rick James