web-dev-qa-db-ja.com

ひどく非効率的なMySQLDELETEステートメントを停止する必要がありますか?

Linodeの1GB1CPUSSD仮想マシンのMySQL5.5で次のステートメントを実行しています。

DELETE table
FROM (
    SELECT MAX(id) id, field
    FROM table
    GROUP BY field
) temp_table
INNER JOIN table
    ON table.field = temp_table.field
WHERE table.id != temp_table.id

Sending data状態で24時間以上スタックしているのですが、その理由がわかりました。実行プランがひどいです。

mysql> EXPLAIN SELECT 1 FROM (SELECT MAX(id) id, field FROM table GROUP BY field) temp_table INNER JOIN table ON table.field = temp_table.field WHERE table.id != temp_table.id
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows   | Extra                           |
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL  | NULL          | NULL | NULL    | NULL | 381888 |                                 |
|  1 | PRIMARY     | users      | ALL  | NULL          | NULL | NULL    | NULL | 984873 | Using where; Using join buffer  |
|  2 | DERIVED     | users      | ALL  | NULL          | NULL | NULL    | NULL | 984873 | Using temporary; Using filesort |
+----+-------------+------------+------+---------------+------+---------+------+--------+---------------------------------+
3 rows in set (46.12 sec)

(MySQL5.5ではEXPLAIN DELETEが許可されていないため、 this answerのようにEXPLAIN SELECT 1で実行しています)

EXPLAIN自体だけが完了するのに46秒かかることに注意してください。

この声明を止めて、より良いことをしようとすべきですか、それとも待つべきですか?

1
e18r

クエリが何をしているのかわかります。大量の行を削除し、すべてのフィールドに最後に挿入されたIDを保持しようとしています。

私にはもっと良い方法があります。

DROP TABLE IF EXISTS keys_to_keep;
CREATE TABLE keys_to_keep
(
    id INT NOT NULL,
    PRIMARY KEY (id)
);
INSERT INTO keys_to_keep SELECT MAX(id) FROM mytable GROUP BY field;
CREATE TABLE mytable_new LIKE mytable;
INSERT INTO mytable_new
SELECT B.* FROM keys_to_keep A INNER JOIN mytable B USING (id);
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

Mytableを確認してください。フィールドが最後に出現するだけの場合は、次のことができます。

DROP TABLE keys_to_keep;
DROP TABLE mytable_old;

試してみる !!!

私は以前にこのテクニックをお勧めしました

代わりにソフト削除を行うことについても説明しました(削除済みとマークされたIDの追加のマッピングテーブル、または削除された行にフラグを立てるための追加の列を維持する必要があります)。

0
RolandoMySQLDBA

EXPLAINはサブクエリを評価したため、非常に時間がかかりました。おそらく、INDEX(field, id)がないため、サブクエリに非常に時間がかかりました。

テーブルの大きなチャンクを削除する場合、保持したいものすべてを新しいテーブルにコピーしてから、RENAMEを使用してテーブルを交換する方が速いことがよくあります。

または、可能であれば、主キーをウォークスルーして、100〜1000のチャンクで削除を実行することもできます。詳細については 私のブログを削除

0
Rick James