想定ケースとしては1億行くらいのテーブルから7000万行くらい消すクエリーを、レプリケーションが遅れずバッファプールも食い切らない程度にちまちま消すようにする。
DELETE FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28'
「ちまちま削除する」なので、トランザクションでAll or Nothingを保証したい場合は使えない。
id
をプライマリーキー(ただしサロゲートキーかどうかは問わない)、 hoge
, last_update
が本来消し込みに使いたいカラムだとする。 プライマリーキー(またはユニークキー)がないテーブルのことは考えない。KEY(hoge, last_update) がある場合
- ターゲットのプライマリーキーを取り出して
DELETE .. WHERE id IN ..
の形に落とし込む - 行ロックに落とし込める
- idの型を選ばない(varcharだろうと使える)
- 自前でINリストを作るのが面倒ならGROUP_CONCATという手もあるけどその場合は group_concat_max_len に注意
DELETE
の方でもとの条件をANDしておくのを忘れると事故ることがある。。- プライマリーキーに対するWHEREがあるので実行計画で暴発しにくい…?
- けどあんまりINのリストを長くするとテーブルスキャン選びやがった on 5.7
SELECT id FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' ORDER BY id LIMIT 1000;
DELETE FROM t1 WHERE id IN (.., ..) AND hoge = 1 AND last_update < '2017-11-28';
SELECT id FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' ORDER BY id LIMIT 1000;
DELETE FROM t1 WHERE id IN (.., ..) AND hoge = 1 AND last_update < '2017-11-28';
..
(KEY(hoge) のみがある、または削除に使えるキーがない) && idが数値型の場合
- idのレンジを細かく区切ってループさせる
- 最終的には全レコードにアクセスしないといけないので一度にやるとバッファプールが荒れる。適度にsleepを入れること
- ネクストキーロックなので、DELETEしてる範囲(+α)にINSERTやUPDATEが来るとブロックされる
DELETE FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' AND id BETWEEN 1 AND 1000;
DELETE FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' AND id BETWEEN 1001 AND 2000;
DELETE FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' AND id BETWEEN 2001 AND 3000;
..
KEY(hoge) のみがある && idが文字列型の場合
- idの最小値を取ってORDER BY id LIMITで少しずつ行く
- 最終的には全レコードにアクセスしないといけないので一度にやるとバッファプールが荒れる。適度にsleepを入れること
- ネクストキーロックなので、DELETEしてる範囲(+α)にINSERTやUPDATEが来るとブロックされる
- 実行計画が暴発すると地獄が見えるのでDELETEの方はUSE INDEXした方が良さげ。DELETEでUSE INDEXするには テーブルリファレンス構文 の方を使う
- 正直この辺まで来るとtsvにでも吐き出させて
WHERE id IN (..)
の形にした方が良いような気がする
SELECT MIN(id) AS min_id FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' ;
DELETE t1 FROM t1 USE INDEX(PRIMARY) WHERE id >= $min_id AND hoge = 1 AND last_update < '2017-11-28' ORDER BY id LIMIT 1000;
SELECT MIN(id) AS min_id FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' ;
DELETE t1 FROM t1 USE INDEX(PRIMARY) WHERE id >= $min_id AND hoge = 1 AND last_update < '2017-11-28' ORDER BY id LIMIT 1000;
..
削除に使えるキーがなくてidが文字列型の場合
- 悔い改めなさい
- 一度tsvにでも吐き出させて
WHERE id IN (..)
の形に落とし込む - SELECTで1回テーブルスキャン、これでバッファプールが荒れそう。ぶん回しても良いバックアップとかバッチ用とかのMySQLがあればここが無視できるからこのやり方でも安定しそう
SELECT id FROM t1 WHERE hoge = 1 AND last_update < '2017-11-28' INTO OUTFILE '/tmp/target.txt; /* これが時間かかるはずなのでたっぷり後悔できる */
DELETE FROM t1 WHERE id IN (.., ..) AND hoge = 1 AND last_update < '2017-11-28'; /* お好みの言語でファイルを処理してINリストを作る */
DELETE FROM t1 WHERE id IN (.., ..) AND hoge = 1 AND last_update < '2017-11-28';
..
どうでしょう。
【2021/05/21 18:31】
MySQL 8.0では取れる手が増えていてちょっと感動した。
はじめまして。こちらの記事で質問させていただけますでしょうか?
返信削除mysql(5.6.10) の delete で force index を使いたく、いろいろと検索していたら、たどり着きました。
「DELETEでUSE INDEXするには テーブルリファレンス構文 の方を使う」
ということがこちらで書かれていますが、こちらに関する公式のマニュアル等があれば、
教えていただけませんでしょうか?
一応、
delete from t1 where ~
と書くところを、
delete t1 from t1 force index(key1) where ~
と書く、
ということで理解はできましたが、
公式のマニュアルも確認しておきたいな、という意図です。
お手数ですが、
よろしくお願いいたします。
DELETEステートメントで「テーブルリファレンス記述が使える」のはDELETEステートメントのページに記述があります。
返信削除MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.2 DELETE 構文 https://dev.mysql.com/doc/refman/5.6/ja/delete.html
「テーブルリファレンス記述」がどういうものかについてはJOINの節に説明があります。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.9.2 JOIN 構文 https://dev.mysql.com/doc/refman/5.6/ja/join.html
これで求める情報になっているでしょうか?
ご回答ありがとうございます。
返信削除・delete 文で table_references 句が使用できること
・テーブルリファレンス(テーブル参照)について
については理解できました。
もう一点、
・delete 文でインデックスヒントを使用するときに、テーブルリファレンス構文を使用する必要がある
ということが記載された公式の文書を見たいのですが、ご存知ではないでしょうか?
何度もお手数ですが、
よろしくお願いいたします。
私の記憶にある限り、存在しなかったと思います。
返信削除1-1. 「テーブルリファレンス」はインデックスヒントを解釈することができます。
1-2. 「テーブル」はテーブルオブジェクトですので、「インデックスヒント」という概念はありません。
2. DELETE構文のドキュメントには、単一テーブルを使った構文( "DELETE FROM tbl_name" ..) = 引数が「テーブル」のみである構文と、テーブルリファレンスを使った構文( "DELETE FROM tbl_name FROM table_references"ともう一つ) = 引数が「テーブル」および「テーブルリファレンス」である構文しかありません。
3. 1-1., 1-2. および 2.より、DELETEステートメントは「テーブルリファレンス」を使う以外にインデックスヒントを使うことができません。
という推移的な結論です。
https://dev.mysql.com/doc/refman/5.6/ja/delete.html
ご回答ありがとうございます。
返信削除頂いた情報で理解でき、スッキリしました!
また、
delete でインデックスヒントを使う、という目的も果たせ、
大変助かりました。
ありがとうございます。