23

私は約1000万行を含むかなり大きなInnoDBテーブルを持っています(そして数えると、そのサイズの20倍になると予想されます)。各行はそれほど大きくはありませんが(平均で131 B)、時々それらのチャンクを削除する必要があり、それには時間がかかります。これはテーブル構造です:

 CREATE TABLE `problematic_table` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `taxid` int(10) unsigned NOT NULL,
    `blastdb_path` varchar(255) NOT NULL,
    `query` char(32) NOT NULL,
    `target` int(10) unsigned NOT NULL,
    `score` double NOT NULL,
    `evalue` varchar(100) NOT NULL,
    `log_evalue` double NOT NULL DEFAULT '-999',
    `start` int(10) unsigned DEFAULT NULL,
    `end` int(10) unsigned DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `taxid` (`taxid`),
    KEY `query` (`query`),
    KEY `target` (`target`),
    KEY `log_evalue` (`log_evalue`)
) ENGINE=InnoDB AUTO_INCREMENT=7888676 DEFAULT CHARSET=latin1;

テーブルから大きなチャンクを削除するクエリは、次のようになります。

DELETE FROM problematic_table WHERE problematic_table.taxid = '57';

このようなクエリは、完了するのにほぼ1時間かかりました。インデックス書き換えのオーバーヘッドにより、これらのクエリが非常に遅くなることが想像できます。

既存のデータベースで実行されるアプリケーションを開発しています。サーバー変数への変更を必須にしない限り(私はそうしたくない)、サーバー変数を制御できない可能性が高いので、それらを変更する提案はほとんど価値がありません。

INSERT ... SELECT削除したくない行を一時テーブルに入れて残りを削除しようとしましたが、削除する行と保持する行の比率が維持する方向にシフトするため、これはもはや有用な解決策ではありません。 。

これは、将来頻繁にINSERTsとsが表示される可能性があるテーブルですが、sは表示されません。基本的に、コンテンツの一部を時々削除する必要があるのは、ロギングおよび参照テーブルです。SELECTUPDATE

このテーブルのインデックスの長さを制限することで、インデックスを改善できますか?DISABLE KEYSトランザクション中にサポートするMyISAMヘルプに切り替えますか?DELETEパフォーマンスを改善するために他に何を試みることができますか?

編集:そのような削除の1つは、約100万行のオーダーになります。

4

5 に答える 5

30

200万行のテーブルとdeleteステートメントを使用した同様のシナリオがありました。これにより、約10万行が削除されます。これには、約10分かかりました。

構成を確認したところ、MySQLサーバーがデフォルトinnodb_buffer_pool_size= 8 MB(!)で実行されていることがわかりました。

= 1.5GBで再起動した後innodb_buffer_pool_size、同じシナリオに10秒かかりました。

したがって、「テーブルの並べ替え」がbuffer_poolに収まるかどうかに依存関係があるように見えます。

于 2013-10-23T15:25:46.347 に答える
17

このソリューションは、完了するとパフォーマンスが向上しますが、プロセスの実装には時間がかかる場合があります。

新しいBIT列を追加して、デフォルトTRUEで「アクティブ」とFALSE「非アクティブ」にすることができます。それが十分な状態でない場合はTINYINT、256の可能な値で使用できます。

この新しい列の追加にはおそらく長い時間がかかりますが、それが終わったらPRIMARY、削除の場合と同じように、この新しい列のインデックスを作成しない限り、更新ははるかに高速になります。

InnoDBがあなたのような巨大なテーブルに非常に長い時間がかかる理由DELETEは、クラスターインデックスのためです。テーブルはPRIMARY、最初に見つかったもの、またはUNIQUE見つからない場合に適切な代替として判断できるものに基づいて物理的に並べ替えられます。したがって、1つの行が削除されると、テーブル全体がディスク上で物理的に並べ替えられ、速度が向上します。デフラグ。ですから、それほど時間がかかるわけではありません。その行が削除された後の物理的な並べ替えです。PRIMARYUNIQUEDELETE

固定幅の列を作成して更新すると、行とテーブル自体が消費するスペースが一定であるため、削除する代わりに、巨大なテーブル全体で物理的に並べ替える必要はありません。

営業時間外は、シングルDELETEを使用して不要な行を削除できます。この操作はまだ低速ですが、個々の行を削除するよりも集合的にはるかに高速です。

于 2013-01-13T00:55:21.143 に答える
3

ストアドプロシージャを使用して同様の問題を解決し、パフォーマンスを数千倍向上させました。

私のテーブルには3300万行といくつかのインデックスがあり、10K行を削除したいと思いました。私のDBはAzureにあり、innodb_buffer_pool_sizeを制御できませんでした。

tmp_id簡単にするために、プライマリidフィールドのみを含むテーブルを作成しました。

CREATE TABLE `tmp_id` (
    `id` bigint(20) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`)
)

削除したいIDのセットを選択して実行しましtmp_iddelete from my_table where id in (select id from tmp_id);。これは12時間で完了しなかったため、1つのIDのみで試してみましたが、 tmp_id25分かかりました。実行delete from my_table where id = 1234は数ミリ秒で完了したので、代わりに手順で実行してみることにしました。

CREATE PROCEDURE `delete_ids_in_tmp`()
BEGIN
    declare finished integer default 0;
    declare v_id bigint(20);
    declare cur1 cursor for select id from tmp_id;
    declare continue handler for not found set finished=1;    
    open cur1;
    igmLoop: loop
        fetch cur1 into v_id;
        if finished = 1 then leave igmLoop; end if;
        delete from problematic_table where id = v_id;
    end loop igmLoop;
    close cur1;
END

call delete_ids_in_tmp();1分以内にすべての10K行を削除しました。

于 2017-12-11T12:20:46.973 に答える
3

約2億行のInnoDBテーブルがあり、同じ問題が発生しました。行の削除には永遠に時間がかかりました。

テーブルには、主キー、一意キー、および複数の複合インデックスがあります。

小さなチャンクで削除する場合はかなり高速だったので、制限付きで複数の反復で行を削除するだけのストアドプロシージャを作成することにしました。Jan Larsenの答えのようなものですが、別のテーブルは必要ありません。

これにより、数分以内に大量のデータ(約500K行)を削除することが可能になりました。

エラーの変更をロールバックできるようにするためにInnoDBが実行する必要のあるトランザクションが大きすぎるため、メモリに収まらないため、削除のパフォーマンスが非常に悪くなっているようです。

手順:

CREATE DEFINER=`root`@`%` PROCEDURE `delete_rows`()
BEGIN
    declare v_max int unsigned default 100;
    declare v_counter int unsigned default 1;

        while v_counter < v_max do
            DELETE from items where a = 'A' AND b = 'B' AND c = 'C' LIMIT 10000;
            set v_counter=v_counter+1;
        end while;
END

次に、次のように呼び出します。

CALL delete_rows();

where文は、a、b、c-columnsで始まる複合インデックスと一致します。これは重要だと思います。そのため、MySQLは行を一致させるために全表スキャンを行う必要がありません。

于 2020-01-27T10:34:03.677 に答える
-1
    DELETE FROM problematic_table WHERE problematic_table.taxid = '57';

引用符を削除します。taxidは整数であり、引用符で値を渡すと文字列になります。整数と文字列を比較するため、インデックスは選択されません。

    DELETE FROM problematic_table WHERE problematic_table.taxid = 57;
于 2019-12-09T06:54:53.530 に答える