私は大きな乱数をキーとして使用しています(別のシステムから入力されます)。かなり小さなテーブル(数百万行など)での挿入と更新には、妥当と思われるよりもはるかに長い時間がかかります。
説明のために非常に簡単なテストを抽出しました。テストテーブルでは、できる限りシンプルにしようとしました。私の実際のコードには、このような単純なレイアウトはなく、リレーションと追加のインデックスなどがあります。ただし、より単純なセットアップは同等のパフォーマンスを示します。
結果は次のとおりです。
creating the MyISAM table took 0.000 seconds
creating 1024000 rows of test data took 1.243 seconds
inserting the test data took 6.335 seconds
selecting 1023742 rows of test data took 1.435 seconds
fetching 1023742 batches of test data took 0.037 seconds
dropping the table took 0.089 seconds
creating the InnoDB table took 0.276 seconds
creating 1024000 rows of test data took 1.165 seconds
inserting the test data took 3433.268 seconds
selecting 1023748 rows of test data took 4.220 seconds
fetching 1023748 batches of test data took 0.037 seconds
dropping the table took 0.288 seconds
MyISAMに1M行を挿入するには6秒かかります。 InnoDBへのアクセスには433秒!
何が間違っていますか?構成の誤りは何ですか? (MySQLはデフォルトの通常のUbuntuインストールです)
テストコードは次のとおりです。
import sys, time, random
import MySQLdb as db
# usage: python script db_username db_password database_name
db = db.connect(Host="127.0.0.1",port=3306,user=sys.argv[1],passwd=sys.argv[2],db=sys.argv[3]).cursor()
def test(engine):
start = time.time() # fine for this purpose
db.execute("""
CREATE TEMPORARY TABLE Testing123 (
k INTEGER PRIMARY KEY NOT NULL,
v VARCHAR(255) NOT NULL
) ENGINE=%s;"""%engine)
duration = time.time()-start
print "creating the %s table took %0.3f seconds"%(engine,duration)
start = time.time()
# 1 million rows in 100 chunks of 10K
data = [[(str(random.getrandbits(48)) if a&1 else int(random.getrandbits(31))) for a in xrange(10*1024*2)] for b in xrange(100)]
duration = time.time()-start
print "creating %d rows of test data took %0.3f seconds"%(sum(len(rows)/2 for rows in data),duration)
sql = "REPLACE INTO Testing123 (k,v) VALUES %s;"%("(%s,%s),"*(10*1024))[:-1]
start = time.time()
for rows in data:
db.execute(sql,rows)
duration = time.time()-start
print "inserting the test data took %0.3f seconds"%duration
# execute the query
start = time.time()
query = db.execute("SELECT k,v FROM Testing123;")
duration = time.time()-start
print "selecting %d rows of test data took %0.3f seconds"%(query,duration)
# get the rows in chunks of 10K
rows = 0
start = time.time()
while query:
batch = min(query,10*1024)
query -= batch
rows += len(db.fetchmany(batch))
duration = time.time()-start
print "fetching %d batches of test data took %0.3f seconds"%(rows,duration)
# drop the table
start = time.time()
db.execute("DROP TABLE Testing123;")
duration = time.time()-start
print "dropping the table took %0.3f seconds"%duration
test("MyISAM")
test("InnoDB")
InnoDBは「ランダムな」主キーにうまく対応していません。シーケンシャルキーまたは自動インクリメントを試してみると、パフォーマンスが向上すると思います。 「実際の」キーフィールドには引き続きインデックスを付けることができますが、一括挿入の場合は、挿入が完了した後、1回のヒットでそのインデックスを削除して再作成した方がよい場合があります。そのためのベンチマークをご覧ください。
関連する質問
InnoDBはトランザクションをサポートしているため、明示的なトランザクションを使用していないため、innoDBは各ステートメントの後にコミットを行う必要があります( 「挿入ごとにログをディスクにフラッシュする」 )。
ループの前に次のコマンドを実行します。
START TRANSACTION
ループした後、これ
COMMIT
MyISAMとInnoDBの両方で同時に挿入が多いアプリケーションのテストを行う必要がありました。私が抱えていた速度の問題を解決する単一の設定がありました。以下を設定してみてください。
innodb_flush_log_at_trx_commit = 2
here の設定について読んで、リスクを理解してください。
https://dba.stackexchange.com/questions/12611/is-it-safe-to-use-innodb-flush-log-at-trx-commit-2/12612 および-も参照してください https://dba.stackexchange.com/a/29974/9405
私のシステムでは非常に異なる結果が得られますが、これはデフォルトを使用していません。デフォルトでは5Mであるinnodb-log-file-sizeでボトルネックになっている可能性があります。 innodb-log-file-size = 100Mでは、次のような結果が得られます(すべての数値は秒単位です)。
MyISAM InnoDB
create table 0.001 0.276
create 1024000 rows 2.441 2.228
insert test data 13.717 21.577
select 1023751 rows 2.958 2.394
fetch 1023751 batches 0.043 0.038
drop table 0.132 0.305
innodb-log-file-size
を増やすと、これが数秒速くなります。 innodb-flush-log-at-trx-commit=2
または0
を設定して耐久性の保証を削除すると、挿入番号も多少改善されます。
InnoDBのデフォルト値は実際にはかなり悪いです。 InnoDBは非常にRAMに依存しています。設定を微調整すると、より良い結果が得られる場合があります。ここで、私が使用したガイドを示します InnoDB最適化の基本
これは古いトピックですが、頻繁に検索されます。最後の1秒程度でコミットされたトランザクションを失うリスク(上記の@philip Koshyが述べたように)を知っている限り、大規模な更新の前に、これらのグローバルパラメータを設定できます。
innodb_flush_log_at_trx_commit=0
sync_binlog=0
更新が完了したら、電源をオンにします(必要な場合)。
innodb_flush_log_at_trx_commit=1
sync_binlog=1
aCIDに完全に準拠しています。
これらの両方をオフとオンにすると、書き込み/更新のパフォーマンスに大きな違いがあります。私の経験では、上で説明した他の要素が多少の違いをもたらしますが、ほんのわずかです。
update/insert
は、全文索引です。 1つのケースでは、フルテキストインデックスを持つ2つのテキストフィールドを持つテーブルで、2mil行を挿入するのに6時間かかり、フルテキストインデックスを削除してから10分しかかかりませんでした。より多くのインデックス、より多くの時間。そのため、一意の主キー以外の検索インデックスは、大量の挿入/更新の前に削除される場合があります。
あなたのinnodbバッファプールのサイズは? RAMの75%に設定してください。通常、InnoDBの主キーの順序で挿入する方が適切です。しかし、プールサイズが大きい場合は、速度が良いはずです。
挿入を高速化するもの:
解決
id
は符号なし整数、auto_incrementですid
列に主キーを作成しますBam、即時の10x +挿入の改善。