2012-05-01 11 views
0

私のmysqlの挿入が非常に遅いことがわかりました。テストし、実証するために、私は次の表を使用:非常に遅いInnodbの挿入:空のテーブルの1Kエントリのために65秒。

+----------+-----------------------------------------------+ 
| Table | Create Table         | 
+----------+-----------------------------------------------+ 
| ik_b64_8 | CREATE TABLE `incr_tbl` (
    `cnt` int(11) NOT NULL, 
    PRIMARY KEY (`cnt`) 
) ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin | 
+----------+-----------------------------------------------+ 

とPythonコード:私は空のテーブルの上に、この挿入のみのコードを実行すると

def profile_basic(cnt): 
    db = database.Connection("localhost","testing_delme","root", "") 
    t1 = time.time() 
    for ii in range(cnt): 
     db.execute("INSERT INTO testing_delme.incr_tbl VALUES (%s)", ii) 
    print time.time() - t1 

、それは1Kの挿入のために65秒を消費します。私はinnodb_flush_log_at_trx_commit = 1を持っています。テーブルがデータを失うことができないので、私はそれを必要とします。私の質問は、このセットでは、インサートがとても遅くなることですか?あるいは何か他のものが欠けていますか?

+0

すべてを1つのステートメントで挿入しようとしましたか? – jeremyharris

+0

開始トランザクション。 foo(c1)の値(v1)、(v2)、(v3)に挿入します。コミット; –

+0

私は実際のテーブルのようにトランザクションを試していません(上のデモンストレーションのみです)。一度に1エントリしか挿入できません。それはWebサーバーの背後にあり、ユーザー登録を取得します。したがって、それは助けにならないでしょう。しかし、それが何か手がかりを与えるなら、私はこのテーブルでそれを試してみることができます。 – Ethan

答えて

1

「データを失うことはできません。」それは程度の問題です。フラッシュしないと、データの最後の秒が失われる可能性があります。フラッシュすると、その時に書かれていたものだけを失うかもしれません。あなたは本当にその上に大規模なパフォーマンスのヒットを取るしたいですか?

また、ディスクが壊れた場合は、最後のバックアップ以降のすべてのデータが失われてしまいます。これは長期的には不可避であり、さらに多くのデータが必要になるため、バックアップを頻繁に行うことが心配です。

フラッシングを無効にします。私は、すべてのディスク操作のために、1つのインサートにつき数百ミリ秒ではないにせよ、数十万を簡単に取ると思います。テーブルにいくつかのインデックスを付けることは、それをさらに悪化させるでしょう。

これにもかかわらず、書き込みごとに絶対にフラッシュする必要がある場合は、データベースをSSDに置くとパフォーマンスが大幅に向上します。

+0

私が心配している表は注文情報を格納しています。あなたが言うように、最悪の場合、私は1つのインサートを緩めます。私の質問は、それは本当にインサートあたり65msかかるのですか、それとも私が逃している他の要因(innodb_flush_log_at_trx_commitを除く)がありますか? – Ethan

+0

さて、innodb_flush_log_at_trx_commitをオフにして、どれだけの違いがあるかを見てみてください。 – Thomas

+0

はい、私はそれを試みました。 innodb_flush_log_at_trx_commit = 2 - > 140ms/1K挿入。 0に設定すると109msかかる。私もinnodb_file_per_tableを設定しました。これは上記の値をわずかに123msと109msにまで下げています。 – Ethan

0

1秒あたりのトランザクション数に限界があると思います。それぞれの挿入は個別のトランザクションとして扱われますので、自動コミット・モードにする必要があります。

(1)単一のSQL文で多くの行を挿入し、[OK]をしている:

db.execute("insert into incr_tbl values %s" % ",".join(["(%s)" % i for i in range(xx)])) 

(2)また、あなたが開始することができます:

insert into incr_tbl values (1),(2),(3).... 

Pythonの同等はこのようなものですトランザクションを明示的に指定してください:

db = ...(autocommit=False) 
db.begin_transaction() 
for i in xx: db.execute(... insert i ...) 
db.commit() 

適切なサーバーをお持ちの場合、料金ははるかに高いはずです15秒以上の時間があるので、マシンとmysqlの設定を確認してください。ディスクにコミットして毎回待つ(sqlite)場合でも、コモディティハードドライブで100tx/sにする必要があります。私はデータベースがusbのフラッシュに位置していたときに、この低いレートしか見ていない。もう一つの可能​​な説明は、あなたのpythonがデータベースから非常に遠くにあり、ネットワークの待ち時間がパフォーマンスを殺していることです。この場合、(1)が助けます、(2)はしません。

0

db.execute()とすると、1つ1つ挿入すると非常に遅くなります。ループを使用してリストを作成し、一括挿入を実行します。すなわち、db.executemany()

def profile_basic(cnt): 
    import mysql.connector, time 
    cnx = mysql.connector.connect("localhost","testing_delme","root", "") 
    db = cnx.cursor() 
    list_ii = [] 
    t1 = time.time() 
    for ii in range(cnt): 
     list_ii.append(ii) 
    # One bulk insert 
    db.executemany("INSERT INTO testing_delme.incr_tbl VALUES (%s)", list_ii) 
    # Don't forget to commit 
    db.commit() 
    print time.time() - t1 
+0

を参照してください。かなり長いですが、私のケースは、ユーザーはwebappに登録します。私は私が使用しているtorndbのデフォルトである自動コミットモードにいると思います。自動コミットを削除してトランザクションを使用する以外にも、より良い挿入パフォーマンスを得るために微調整すべき他のmysql設定がありますか? – Ethan

+0

autocommit = Falseであっても、db.execute()を実行してループの後にdb.commit()を使用すると、コードが1つずつ追加され、すべてコミットされます。私は600kのインサートを追加しなければなりませんでした.100個のインサートあたり10秒(ほぼ1日)でした。そして、それを調べてdb.executemany()に変更しました。 – Delicious

+0

コメントについて「新しいユーザーが登録されたときに挿入する」というタイトルでのパフォーマンスは、1kのインサートあたり65sだったので、1秒あたり約15でした。毎秒より多くのユーザーがいる場合は、新しい登録をキャッシュしてから、executemany()を使用して一括挿入します。 Googleの "memcached"をキャッシュするには – Delicious

関連する問題