web-dev-qa-db-ja.com

大規模なInnoDBテーブルでDELETE FROMパフォーマンスを向上させるにはどうすればよいですか?

私は約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およびSELECTsが頻繁に発生する可能性があるが、UPDATEsは発生しない可能性があるテーブルです。基本的に、それは時々コンテンツの一部を削除する必要があるロギングおよび参照テーブルです。

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

編集: そのような削除の1つは、約100万行程度です。

18
mpe

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

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

この新しい列の追加には時間がかかる可能性がありますが、終了すると、PRIMARYを使用して削除を実行し、この新しい列にインデックスを付けない限り、更新ははるかに高速になります。 。

InnoDBがあなたのような大規模なテーブルでDELETEに時間がかかる理由は、クラスターインデックスが原因です。 PRIMARY、最初にUNIQUE、またはPRIMARYまたはUNIQUEなので、1つの行が削除されると、速度とデフラグのためにテーブル全体がディスク上で物理的に並べ替えられます。したがって、それほど時間がかかっているのはDELETEではありません。その行が削除された後の物理的な並べ替えです。

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

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

12
user1382306

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

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

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

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

23
vdd

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

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

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

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

削除したいIDのセットを選択して_tmp_id_に選択し、delete from my_table where id in (select id from tmp_id);を実行しました。これは12時間で完了しなかったため、_tmp_id_で単一のIDのみを使用してみましたが、 25分。 _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行を削除しました。

1
Jan Larsen
    DELETE FROM problematic_table WHERE problematic_table.taxid = '57';

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

    DELETE FROM problematic_table WHERE problematic_table.taxid = 57;
0
Prabhat