web-dev-qa-db-ja.com

MySQL InnoDBの挿入が非常に遅いのはなぜですか?

私は大きな乱数をキーとして使用しています(別のシステムから入力されます)。かなり小さなテーブル(数百万行など)での挿入と更新には、妥当と思われるよりもはるかに長い時間がかかります。

説明のために非常に簡単なテストを抽出しました。テストテーブルでは、できる限りシンプルにしようとしました。私の実際のコードには、このような単純なレイアウトはなく、リレーションと追加のインデックスなどがあります。ただし、より単純なセットアップは同等のパフォーマンスを示します。

結果は次のとおりです。

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")
55
Will

InnoDBは「ランダムな」主キーにうまく対応していません。シーケンシャルキーまたは自動インクリメントを試してみると、パフォーマンスが向上すると思います。 「実際の」キーフィールドには引き続きインデックスを付けることができますが、一括挿入の場合は、挿入が完了した後、1回のヒットでそのインデックスを削除して再作成した方がよい場合があります。そのためのベンチマークをご覧ください。

関連する質問

42
Paul Dixon

InnoDBはトランザクションをサポートしているため、明示的なトランザクションを使用していないため、innoDBは各ステートメントの後にコミットを行う必要があります( 「挿入ごとにログをディスクにフラッシュする」 )。

ループの前に次のコマンドを実行します。

START TRANSACTION

ループした後、これ

COMMIT
61
flo

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

21
Philip Koshy

私のシステムでは非常に異なる結果が得られますが、これはデフォルトを使用していません。デフォルトでは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を設定して耐久性の保証を削除すると、挿入番号も多少改善されます。

6
Andrew

InnoDBのデフォルト値は実際にはかなり悪いです。 InnoDBは非常にRAMに依存しています。設定を微調整すると、より良い結果が得られる場合があります。ここで、私が使用したガイドを示します InnoDB最適化の基本

5
Kien Truong

これは古いトピックですが、頻繁に検索されます。最後の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分しかかかりませんでした。より多くのインデックス、より多くの時間。そのため、一意の主キー以外の検索インデックスは、大量の挿入/更新の前に削除される場合があります。

2
Allen King

あなたのinnodbバッファプールのサイズは? RAMの75%に設定してください。通常、InnoDBの主キーの順序で挿入する方が適切です。しかし、プールサイズが大きい場合は、速度が良いはずです。

2
Ajay

挿入を高速化するもの:

  • 空のテーブルに大量に挿入する前に、テーブルからすべてのキーを削除しました
  • その後、インデックスがメモリに収まらないという問題があることがわかりました。
  • また、binlogが使用されていなくてもsync_binlog = 0(1である必要があります)が見つかりました。
  • また、私はinnodb_buffer_pool_instancesを設定していませんでした
1
Shimon Doodkin

解決

  1. 現在のプライマリキーと同一の新しいユニークキーを作成します
  2. 新しい列を追加idは符号なし整数、auto_incrementです
  3. 新しいid列に主キーを作成します

Bam、即時の10x +挿入の改善。

0