sql >> データベース >  >> RDS >> Mysql

MySQLの更新/ステートメントの挿入を高速化

    これを何百万回も実行する必要がある場合、ここにはパフォーマンスの問題がたくさんあります。

    • 同じSQLステートメントを何百万回も何度も準備しています。一度準備して何百万回も実行する方がうまくいくでしょう。

    • 1回のクエリの後、すべての関数呼び出しでデータベースから切断します。つまり、毎回再接続する必要があり、キャッシュされた情報はすべて破棄されます。そんなことはしないで、接続したままにしてください。

    • 各行の後にコミットしています。これは物事を遅くします。代わりに、バッチを実行した後にコミットします。

    • 選択+更新または挿入は、おそらく単一のアップサートとして実行できます。

    • 一時テーブルに大量に挿入しているのは、おそらくパフォーマンスの問題です。

    • テーブルにインデックスが多すぎて挿入が遅くなる可能性がある場合。インデックスを削除し、大規模なバッチ更新を行って、それらを再作成するのが最善の場合もあります。

    • SQLに直接値を入力しているため、SQLはSQLインジェクション攻撃にさらされています> 。

    代わりに...

    • プリペアドステートメントとバインドパラメータを使用する
    • データベースを接続したままにします
    • 一括更新を行う
    • 更新の実行の最後にのみコミットします
    • UPDATEですべての計算を行います SELECT + math + UPDATEではなく 。
    • SELECTの代わりに「UPSERT」を使用します 次にUPDATE またはINSERT

    まず、準備されたステートメント。これらにより、MySQLはステートメントを一度コンパイルしてから再利用できます。アイデアは、値のプレースホルダーを使用してステートメントを作成することです。

    select id, position, impressions, clicks, ctr
    from temp
    where profile_id=%s and
          keyword=%s and 
          landing_page=%s
    

    次に、文字列の一部としてではなく、引数として値を使用して実行します。

    self.cursor.execute(
       'select id, position, impressions, clicks, ctr from temp where profile_id=%s and keyword=%s and landing_page=%s',
       (profile_id, keyword, landing_page)
    )
    

    これにより、データベースは準備されたステートメントをキャッシュでき、毎回再コンパイルする必要がなくなります。また、巧妙な攻撃者が" MORE SQL HERE "のような実際にはよりSQLである値を作成できるSQLインジェクション攻撃を回避します。 。これは非常に、非常に、非常に一般的なセキュリティホールです。

    MySQL独自のものを使用する必要がある場合があることに注意してください。真のプリペアドステートメントを取得するためのPythonデータベースライブラリ 。プリペアドステートメントを使用することはパフォーマンスの最大の問題ではないので、あまり心配する必要はありません。

    次に、基本的に行っているのは、既存の行に追加することです。既存の行がない場合は、新しい行を挿入します。これは、UPSERTを使用して1つのステートメントでより効率的に実行できます。 、結合されたINSERT およびUPDATEMySQLにはINSERT ... ON DUPLICATE KEY UPDATEとしてあります

    これがどのように行われるかを確認するために、SELECT then UPDATEを記述できます。 単一のUPDATEとして 。計算はSQLで行われます。

        update temp
        set impressions = impressions + %s,
            clicks = clicks + %s,
            ctr = (ctr + %s / 2)
        where profile_id=%s and
              keyword=%s and
              landing_page=%s
    

    INSERTは同じままです...

        insert into temp
            (profile_id, landing_page, keyword, position, impressions, clicks, ctr)
            values (%s, %s, %s, %s, %s, %s, %s)
    

    それらを1つのINSERTONDUPLICATEKEYUPDATEに結合します。

        insert into temp
            (profile_id, landing_page, keyword, position, impressions, clicks, ctr)
            values (%s, %s, %s, %s, %s, %s, %s)
        on duplicate key update
        update temp
        set impressions = impressions + %s,
            clicks = clicks + %s,
            ctr = (ctr + %s / 2)
    

    これは、テーブルのキーが何として定義されているかによって異なります。 unique( profile_id, landing_page, keyword )がある場合 そうすれば、コードと同じように機能するはずです。

    アップサートができない場合でも、SELECTを削除できます。 UPDATEを試して 、何かが更新されたかどうか、およびINSERTが実行されなかったかどうかを確認します 。

    更新をまとめて行います。 1つの更新を実行してコミットするサブルーチンを呼び出す代わりに、更新するものの大きなリストを渡して、ループで処理します。 executemanyを利用することもできます。 複数の値を使用して同じステートメントを実行します。次にコミットします。

    UPSERTを実行できる可能性があります まとめて。 INSERT 一度に複数の行を取ることができます。たとえば、これにより3つの行が挿入されます。

    insert into whatever
        (foo, bar, baz)
    values (1, 2, 3),
           (4, 5, 6), 
           (7, 8, 9)
    

    INSERT ON DUPLICATE KEY UPDATEでも同じことができる可能性があります データベースと通信するためのオーバーヘッドの量を削減します。例については、この投稿を参照してください。 (PHPの場合ですが、適応できるはずです)。

    これは最後に挿入された行のIDを返すことを犠牲にしますが、それは休憩です。




    1. それぞれの一意の値とカウントを取得する

    2. PDOを使用して2つのデータベース間で結合を使用してクエリを実行する方法

    3. MySQLにヨーロッパの通貨を保存する方法は?

    4. MariaDBでのFROM_DAYS()のしくみ