30,000,000行のテーブルでDELETEコマンドが完了しない


22

私はデータベースを継承しており、クリーンアップと高速化を目指しています。私は、30,000,000行を含むテーブルを持っています。その多くは、プログラマーに代わってエラーが原因で挿入されたジャンクデータです。最適化された新しいインデックスを追加する前に、テーブルをMyISAMからInnoDBに変換し、ジャンクデータを含む多くの行を削除しようとしています。

データベースはMySQL 5.0であり、サーバーへのルートアクセス権があります。最初にこれらのコマンドをAdminerで実行し、次にphpMyAdminで実行しましたが、どちらも同じ結果になりました。

私が実行しているコマンドは、

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'

基本的に、ダッシュで始まるこの列のすべてを削除します-

約3〜5分間実行され、プロセスリストを表示すると消えます。

それから走ります

SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'

そして何百万もの行を返します。

削除ステートメントが完了しないのはなぜですか?

PS、MySQL 5.0が古くなっていることに気付いています。私は、DBをMySQL 5.6 w InnoDB(おそらくMariaDB 10 w XtraDB)に移行する作業を行っていますが、それが起こるまでは、DBをそのまま使用して答えを探しています。

-

編集が削除されました。回答を参照してください。

回答:


24

InnoDBのアーキテクチャをご覧ください(Percona CTO Vadim Tkachenkoからの写真)

InnoDB配管

削除する行は、元に戻すログに書き込まれています。削除の期間中、ファイルibdata1は現在成長しているはずです。mysqlperformanceblog.comにReasons for run-away main Innodb Tablespaceよると:

  • トランザクションの多くの変更
  • 非常に長いトランザクション
  • 遅れパージスレッド

あなたの場合、行を削除しているので、理由#1は1つのロールバックセグメントと一部のUNDOスペースを占有します。これらの行は、削除が完了するまでibdata1に存在する必要があります。そのスペースは論理的に破棄されますが、ディスクスペースは縮小しません。

今すぐ削除する必要があります。削除クエリを強制終了すると、削除された行がロールバックされます。

代わりにこれを行います:

CREATE TABLE tablename_new LIKE tablename;
INSERT INTO tablename_new SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%';
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

最初にテーブルのMyISAMバージョンに対してこれを行うことができます。次に、それをInnoDBに変換します。


21

私の場合に必要だった答えを複雑にしたのではないかと思う。RolandとRick Jamesの両方が一時テーブルの作成で正しいことは間違いありません。フィルターを通過する行のみを挿入しますNOT LIKE '-%'が、私にとって解決策は「簡単」でした。おthatび申し上げます。

mysqlインタラクティブプロンプトでクエリを実行すると、エラーメッセージに気づきました。

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

エラーをグーグル検索することで、解決策はファイルをinnodb_buffer_pool_size介して増加し/etc/my.cnf、mysqlデーモンを再起動することであることわかりました。私のサーバーではデフォルトに設定されており、8Mそれを増やしました1G(サーバーには32GBがあり、これが現在InnoDBである唯一のテーブルです)。

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
Query OK, 23517226 rows affected (27 min 33.23 sec)

その後、コマンドを実行して、約27分で2,300万件のレコードを削除できました。

innodb_buffer_pool_size設定する必要があるものに興味がある場合は、RAMの容量に注意し、GB単位で推奨される推定値を示すこのスレッド確認してください


12

ローランドの提案は、両方のことを一度に行うことで、いくつかの速度を上げることができます。

CREATE TABLE tablename_new LIKE tablename;
ALTER TABLE tablename_new ENGINE = InnoDB;
INSERT INTO tablename_new 
    SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%' ORDER BY primary_key;
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

:しかし、ここで一見永遠に取るのではなく、チャンクに大きな削除を行う方法について説明しますブログです http://mysql.rjweb.org/doc.php/deletebig 要旨は1Kをやって、PKを経由して、テーブルを歩くことですが一度に行。(もちろん、注意すべき詳細があります。)

そして、このブログは、InnoDBへの変換における潜在的な落とし穴に対処しています:http ://mysql.rjweb.org/doc.php/myisam2innodb


5

私の最初の本能は、クエリ結果の数を制限し、クエリを複数回実行することにより、複数の小さな削除を行うことです。

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000

このアプローチの欠点:削除ごとに時間がかかります。これは、に一致しない行をさらにスキップする必要があるためWHEREです。
リックジェームズ

確かに、このプロセスがあまり頻繁に行われない場合、複数の完全なテーブルスキャンは、元の問題が解決されるほど悪くはなりません。つまり、元に戻すログサイズのためにクエリが完了しません。
kristianp

有効なポイント。(私はLIMIT低くします; 10000と言います。)
リックジェームズ

4

最も簡単な解決策は、単純にそれを行わないことです-より小さな削除を行うと、より簡単に処理できます。

この場合、フォームの順次削除を試みることをお勧めします。

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'

2

たぶん、あなたはこのようなことをすることができます:

  • という新しいフィールドを追加しますdeleted
  • のような更新を行いUPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'ます。
  • cronこれを夜間に削除するように設定します。

更新に、削除に時間かかる場合があります。
リックジェームズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.