web-dev-qa-db-ja.com

MySQL HA_ERR_LOCK_WAIT_TIMEOUTはinnodb_lock_wait_timeoutを無視します(ほとんどのテーブルを削除します)

一部のデータベースに対して長時間実行される削除クエリを発行していますが、次のエラーが発生します。

_Last_Error: Could not execute Write_rows event on table DATABASE.TABLENAME; Lock wait timeout exceeded; try restarting transaction, Error_code: 1205; handler error HA_ERR_LOCK_WAIT_TIMEOUT; the event's master log mysql-bin.005698, end_log_pos 91495479
_

「innodb_lock_wait_timeout」を高い値に設定したので、クエリがタイムアウトになることはありません。

_SELECT @@GLOBAL.innodb_lock_wait_timeout;
+-----------------------------------+
| @@GLOBAL.innodb_lock_wait_timeout |
+-----------------------------------+
|                           1000000 |
+-----------------------------------+
_

このエラーを回避する方法に関する提案はありますか?大量のデータを削除する必要があるため、監視せずにループでクエリを発行します。 mysqlのバグに少し似ています。 MySQLのバージョンは5.6です。

編集:正確なクエリは次のとおりです。データは、示されている関数(MOD(MOD(user_id,65536), shard_count) != shard_id))に基づいてクライアント側で分割されます。DB内のデータの大部分はそこにあるべきではないため、削除は大きく、次のようなテーブルにカスケードされます。 300GB(たとえば、各ユーザーが200アイテムの在庫を持つ場合)

_DELETE FROM User
WHERE account_type = "type1"
AND
user_id IN (
    SELECT b.user_id
    FROM (
        SELECT a.account_type, a.user_id
        FROM (
            SELECT account_type, user_id
            FROM User
            WHERE account_type = "type1"
            AND
            MOD(MOD(user_id,65536), 20) != 9
            LIMIT 1000000
        ) a
    ) b
)
_

編集:要求に応じて、_SHOW CREATE TABLE_の出力

_mysql> SHOW CREATE TABLE User \G;
*************************** 1. row ***************************
       Table: User
Create Table: CREATE TABLE `User` (
  `account_type` varchar(20) NOT NULL DEFAULT '',
  `user_id` bigint(20) unsigned NOT NULL,
  `mod_date_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `team_id` int(10) unsigned DEFAULT NULL,
  `shard_key` int(10) unsigned DEFAULT '0',
  PRIMARY KEY (`account_type`,`user_id`),
  KEY `PLAY_SHKEY_I` (`shard_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
_

編集:私はpythonクライアントから本当に大きなループが完了するまでcommit()を呼び出していなかったので、これはプログラミングエラーに原因があると信じています。私はこれまでにコミットしなかった巨大な進行中のトランザクションがあったと思います(それがループ全体に渡ったことがないため)

1
Arthur M

ループするときにOFFSETを実行するときにしないDELETEsを使用しないだんだん遅くなる可能性が高く、タイムアウトを増やしてもヒットする可能性があります。

すべての削除を1つのトランザクションに含めないようにしてください。代わりに、1つの削除が失敗した場合に削除を再実行しても「正しく」機能するようにしてください。

一度に1000行を超える行を削除しないでください。

「大きな削除」の詳細: MySQL Big DELETEs

テーブルの95%を削除

何が起こっているのかを詳しく説明したので、新しいテーブルを作成して5%以上コピーすることにより、 テーブルを再構築する をお勧めします。 (この手法はどのパーセンテージにも適用されますが、50%を超えるコピーの場合、その利点は疑わしいものです。)

ご了承ください innodb_file_per_table肥大化したテーブルを構築する前にオンにしておく必要があります。そうしないと、ディスク領域を取り戻すことはできません。

テーブルが長く拘束される場合は、PRIMARY KEY 1K行のチャンクで、各チャンクの約95%を削除します。 (そのリンクの他の場所で説明されているように。)ディスクスペースをOSに戻すことはできません(ロックなしではOPTIMIZE TABLE)、ただし「解放された」スペースは最終的に新しいINSERTsによって使用されます。

1
Rick James

複数の削除トランザクションを同時に実行していて、これらの削除クエリの一部が同じ行をロックしようとしていて、それらが互いに待機しているため、おそらくこの問題が発生しました。

異なるトランザクションの同じ行を同時に削除しないようにしてください。

スキーマとクエリを共有する場合は、さらにアドバイスを提供できます。

また、問題なくテーブルから大量のデータをアーカイブ/削除できる pt-archiver と呼ばれるツールもあります。

0
Tibor Korocz