web-dev-qa-db-ja.com

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

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

データベースはMySQL 5.0であり、サーバーへのrootアクセス権があります。私は最初にこれらのコマンドを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でそのまま答えたいと思っています。

-

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

22
bafromca

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

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での見積もり。

21
bafromca

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

InnoDB Plumbing

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

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

あなたの場合、行を削除しているので、理由#1はいくつかの取り消しスペースと共に1つのロールバックセグメントを占有します。これらの行は、削除が完了するまで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に変換します。

24
RolandoMySQLDBA

ローランドの提案は、両方のことを一度に行うことによって、いくつかをスピードアップすることができます:

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;

しかし、これは一見永久にかかるのではなく、チャンクで大きなDELETEを行う方法を説明するブログです: http://mysql.rjweb.org/doc.php/deletebig 要点は、 PKを介してテーブルを作成し、一度に1K行を実行します。 (もちろん、注意すべき詳細事項があります。)

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

12
Rick James

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

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

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

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

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

多分あなたはこのようなことをすることができます:

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