MyISAMをInnoDBより「高速」にするにはどうすればよいですか
MyISAMを高速化できる唯一の方法は、InnoDBがこのユニークな状況下にあることです。
読み取られると、MyISAMテーブルのインデックスは.MYIファイルから1回読み取られ、MyISAMキーキャッシュにロードされます(サイズが key_buffer_size のように)。 MyISAMテーブルの.MYDをより速く読み取るにはどうすればよいですか?これとともに:
ALTER TABLE mytable ROW_FORMAT=Fixed;
これについては過去の投稿で書いた
OK、InnoDBはどうですか? InnoDBはクエリに対してディスクI/Oを行いますか?驚いたことに、そうです!あなたはおそらく私がそう言ったことに夢中だと思っているでしょうが、それは絶対に本当ですSELECTクエリについても。この時点で、「InnoDBはクエリでディスクI/Oをどのように実行しているのでしょうか?」
すべては、InnoDBである [〜#〜] acid [〜#〜] -complaint Transactional Storage Engineに戻ります。 InnoDBをトランザクション対応にするためには、I
のACID
をサポートする必要があります。これは分離です。トランザクションの分離を維持するための手法は MVCC、Multiversion Concurrency Control によって行われます。簡単に言えば、InnoDBは、トランザクションがデータを変更しようとする前に、どのようなデータかを記録します。それはどこに記録されますか?システムテーブルスペースファイルでは、ibdata1としてよく知られています。 ディスクI/Oが必要。
InnoDBとMyISAMの両方がディスクI/Oを行うので、どのランダムな要因が誰がより速いかを決定しますか?
DELETEs
とUPDATEs
によるデータの断片化したがって、読み取りの多い環境では、トランザクション動作をサポートするためにibdata1内に含まれるUNDOログに書き込まれる十分なデータがある場合、固定行形式のMyISAMテーブルがInnoDBバッファープールからのInnoDB読み取りよりも優れたパフォーマンスを発揮する可能性があります。 InnoDBデータに課せられます。
データタイプ、クエリ、ストレージエンジンを慎重に計画します。データが増大すると、データの移動が非常に困難になる可能性があります。 Facebookに聞いてください...
単純な世界では、MyISAMは読み取りが高速で、InnoDBは書き込みが高速です。
読み取り/書き込みの混合を導入すると、行ロックメカニズムのおかげで、InnoDBは読み取りも高速になります。
私は MySQLストレージエンジン の比較を数年前に書きましたが、それは今日にも当てはまり、MyISAMとInnoDBのユニークな違いを概説しています。
私の経験では、破損が原因でデータが失われることはそれほど重要ではない、読み取りが多いキャッシュテーブルを除くすべてにInnoDBを使用する必要があります。
2つのエンジンの機械的な違いをカバーするここでの応答に追加するために、経験的な速度比較研究を提示します。
純粋な速度という点では、MyISAMがInnoDBよりも速いとは限りませんが、私の経験では、PURE READ作業環境では約2.0〜2.5倍高速になる傾向があります。明らかに、これはすべての環境に適しているわけではありません。他の人が書いたように、MyISAMにはトランザクションや外部キーなどがありません。
以下のベンチマークを少し行いました-ループにはpythonを、タイミング比較にはtimeitライブラリを使用しました。メモリエンジンも含めたので、これにより最高のパフォーマンスが得られます。全体的には小さいテーブルにのみ適していますが(MySQLのメモリ制限を超えると、常に_The table 'tbl' is full
_に遭遇します)私が見る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」を使用します。
クエリ:_SELECT * FROM tbl WHERE index_col = xx
_
結果:描画
これらの速度はすべてほぼ同じで、予想されるように、選択する列の数は直線的です。 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) )
_
クエリ:SELECT count(*) FROM tbl
結果:MyISAM wins
これは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) )
_
クエリ:_SELECT * FROM tbl WHERE value1<0.5 AND value2<0.5 AND value3<0.5 AND value4<0.5
_
結果:MyISAM wins
ここでは、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) )
_
結果: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のままにして、代わりにサブ選択列のサイズを変更します。
ここで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) )
_
私はこれすべての持ち帰りメッセージが速度に本当に心配しているなら、あなたがしているクエリをベンチマークする必要があるということだと思いますどのエンジンがより適切であるかについての仮定を行うのではなく。
どちらが速いですか?どちらかが速いかもしれません。 YMMV。
あなたはどちらを使うべきですか? InnoDB-クラッシュセーフなど.