回答:
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をどのように行っているのでしょうか?」
すべては、ACIDに準拠したトランザクションストレージエンジンであるInnoDBに戻ります。InnoDBがトランザクション対応であるためには、I
in をサポートする必要が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ライブラリを使用しました。興味深いことに、メモリエンジンも含めました。これは、より小さなテーブルにのみ適していThe table 'tbl' is full
ますが、MySQLのメモリ制限を超えたときに頻繁に発生しますが、全体的に最高のパフォーマンスを提供します。私が検討している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」と「memory」を「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が勝ちます
これは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が勝ちます
ここでは、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;
ここでも、2番目のテーブルの「InnoDB」の代わりに「MyISAM」が使用されています。
このクエリでは、選択テーブルのサイズを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) )
このすべての重要なメッセージは、速度が本当に心配な場合は、どのエンジンがより適しているかを推測するのではなく、実行しているクエリをベンチマークする必要があるということです。
SELECT * FROM tbl WHERE index_col = xx
-グラフのバリエーションを増やす可能性がある2つの要因を以下に示します。主キーと副キー。インデックスはキャッシュされますが、キャッシュされません。
SELECT COUNT(*)
WHERE
条項を追加するまで、MyISAMの明確な勝者です。