最近、作業中のサイトでバグを見つけて修正しました。これにより、テーブル内のデータの重複行が数百万になりますが、それらは行がなくても非常に大きくなります(まだ数百万)。これらの重複行を簡単に見つけることができ、単一の削除クエリを実行してそれらをすべて削除できます。問題は、この多数の行を1回のショットで削除しようとすると、テーブルが長時間ロックされることです。これは可能な限り避けたいと思います。 (テーブルをロックすることによって)サイトを削除することなく、これらの行を削除する唯一の方法は次のとおりです。
他の誰かがこの問題を以前に経験したことがあるのか、もしそうなら、サイトを停止せずに、どうすればユーザーの中断を最小限に抑えて対処したのかと思いました。 2番目の方法、または別の同様の方法を選択した場合、夜遅くに実行するようにスケジュールを設定し、翌朝早くにマージを実行して、ユーザーに事前に知らせることができます。これはたいしたことではありません。クリーンアップを行うためのより良い、またはより簡単な方法について、誰かがアイデアを持っているかどうかを探しています。
DELETE FROM `table`
WHERE (whatever criteria)
ORDER BY `id`
LIMIT 1000
洗浄、すすぎ、影響を受ける行がなくなるまで繰り返します。繰り返しの間に1〜3秒間スリープするスクリプトの場合があります。
また、テーブルにいくつかの制約を追加して、これが再び発生しないようにすることをお勧めします。 1ショットあたり1000の100万行では、スクリプトを1000回繰り返して完了する必要があります。スクリプトが3.6秒ごとに1回実行されると、1時間で完了します。心配ない。あなたのクライアントは気づかないでしょう。
MySQLの25M +行のテーブルから1M +行を削除するユースケースがありました。バッチ削除のようなさまざまなアプローチを試みました(上記)。
最速の方法(必要なレコードを新しいテーブルにコピーする)がわかりました。
CREATE TABLE id_temp_table(temp_id int);
id_temp_table(temp_id)select .....に挿入します.
新しいテーブルtable_newを作成
Id_temp_tableにある不要な行なしで、テーブルからtable_newにすべてのレコードを挿入します
table_newに挿入.... table_id NOT IN(id_temp_tableからdistinct(temp_id)を選択);
プロセス全体で約1時間かかりました。私の使用例では、100レコードのバッチの単純な削除には10分かかりました。
以下は、1つずつ1,000,000レコードを削除します。
for i in `seq 1 1000`; do
mysql -e "select id from table_name where (condition) order by id desc limit 1000 " | sed 's;/|;;g' | awk '{if(NR>1)print "delete from table_name where id = ",$1,";" }' | mysql;
done
あなたはそれらを一緒にグループ化して、IN(id1、id2、.. idN)があまりにも多くの難しさを確信しているtable_nameを削除することができます
同様の問題に直面しました。パーティションがなく、primary_key列にインデックスが1つしかない、サイズが約500 GBの非常に大きなテーブルがありました。マスターはマシンの塊、128コア、512ギガのRAMであり、複数のスレーブもありました。行の大規模な削除に取り組むためのいくつかの手法を試しました。私たちが見つけた最悪のものから最高のものまですべてここにあります
したがって、IMO、テーブルにパーティションを作成する余裕がある場合は、オプション#4を選択してください。そうでない場合は、オプション#3で停止します。
mk-archiver 優れた Maatkit ユーティリティパッケージ(MySQL管理用のPerlスクリプトの束)を使用しますMaatkitはO'Reillyの作者であるBaron Schwartzからです「高性能MySQL」ブック。
目標は、OLTPクエリに多くの影響を与えずにテーブルから古いデータをニブルする、影響の少ないフォワード専用ジョブです。データを別のテーブルに挿入できます。同じサーバー。LOAD DATA INFILEに適した形式でファイルに書き込むこともできますが、どちらもできません。
不要な行を小さなバッチでアーカイブするために既に構築されており、ボーナスとして、削除する行を選択するクエリを台無しにした場合に削除された行をファイルに保存できます。
インストールは不要で、単に http://www.maatkit.org/get/mk-archiver を取得し、その上でperldocを実行(またはWebサイトを参照)してドキュメントを取得します。
一度に2000行のバッチで実行します。中間でコミットします。 100万行はそれほど多くありません。テーブルに多くのインデックスがなければ、これは高速です。
私たちにとって、DELETE WHERE %s ORDER BY %s LIMIT %d
回答はオプションではありませんでした。なぜなら、WHERE基準は遅く(インデックスのない列)、マスターにヒットするからです。
削除するプライマリキーのリストをリードレプリカから選択します。この種類の形式でエクスポートします。
00669163-4514-4B50-B6E9-50BA232CA5EB
00679DE5-7659-4CD4-A919-6426A2831F35
次のbashスクリプトを使用してこの入力を取得し、それをDELETEステートメントにチャンクします[mapfile
built-inのためにbash≥4が必要]:
sql-chunker.sh
(chmod +x
meを忘れずに、bash 4実行可能ファイルを指すようにShebangを変更):
#!/usr/local/Cellar/bash/4.4.12/bin/bash
# Expected input format:
: <<!
00669163-4514-4B50-B6E9-50BA232CA5EB
00669DE5-7659-4CD4-A919-6426A2831F35
!
if [ -z "$1" ]
then
echo "No chunk size supplied. Invoke: ./sql-chunker.sh 1000 ids.txt"
fi
if [ -z "$2" ]
then
echo "No file supplied. Invoke: ./sql-chunker.sh 1000 ids.txt"
fi
function join_by {
local d=$1
shift
echo -n "$1"
shift
printf "%s" "${@/#/$d}"
}
while mapfile -t -n "$1" ary && ((${#ary[@]})); do
printf "DELETE FROM my_cool_table WHERE id IN ('%s');\n" `join_by "','" "${ary[@]}"`
done < "$2"
次のように呼び出します。
./sql-chunker.sh 1000 ids.txt > batch_1000.sql
これにより、出力が次のようにフォーマットされたファイルが得られます(バッチサイズ2を使用しました)。
DELETE FROM my_cool_table WHERE id IN ('006CC671-655A-432E-9164-D3C64191EDCE','006CD163-794A-4C3E-8206-D05D1A5EE01E');
DELETE FROM my_cool_table WHERE id IN ('006CD837-F1AD-4CCA-82A4-74356580CEBC','006CDA35-F132-4F2C-8054-0F1D6709388A');
次に、次のようにステートメントを実行します。
mysql --login-path=master billing < batch_1000.sql
login-path
に不慣れな人にとっては、コマンドラインにパスワードを入力せずにログインするためのショートカットにすぎません。
mysql documentation によると、TRUNCATE TABLE
はDELETE FROM
の高速な代替手段です。これを試して:
TRUNCATE TABLE table_name
5,000万行でこれを試しましたが、2分以内に完了しました。
注:切り捨て操作はトランザクションに対して安全ではありません。アクティブなトランザクションまたはアクティブなテーブルロックの過程で試行しようとするとエラーが発生する
遅いのは、実際のレコードが主キーインデックス内に(主キーインデックスの順に)保存されるMySQlの「クラスター化インデックス」によるものだと思います。つまり、主キーを介したレコードへのアクセスは、インデックス内で正しい主キーを見つけたディスク上のレコードがすぐそこにあるため、1回のディスクフェッチで済むため、非常に高速です。
クラスター化インデックスを持たない他のデータベースでは、インデックス自体はレコードを保持せず、テーブルファイル内のレコードの場所を示す「オフセット」または「場所」を保持し、そのファイルで実際のデータを取得するために2回目のフェッチを行う必要があります。
クラスター化インデックスのレコードを削除するとき、テーブル内のそのレコードより上のすべてのレコードを下に移動して、インデックスに大量の穴が作成されないようにする必要があることを想像できます(それは少なくとも数年前のことです-後のバージョンこれを変更した可能性があります)。
上記のことを知って、MySQLで本当に高速な削除が行われることは、削除を逆の順序で実行することでした。これは、最初からレコードを削除するため、レコードの移動量が最小になります。つまり、後続の削除では再配置するオブジェクトが少なくなります。
私はこれを行うためにスクリプトを作成していません。適切に実行するにはスクリプトが絶対に必要ですが、別のオプションは、新しい複製テーブルを作成し、保持するすべての行を選択することです。このプロセスが完了する間、トリガーを使用して最新の状態に保ちます。同期している場合(削除する行を除く)、トランザクション内の両方のテーブルの名前を変更して、新しいテーブルが古いテーブルの代わりになるようにします。古いテーブルを落として、出来上がり!
これには(明らかに)多くの追加のディスク領域が必要であり、I/Oリソースに負担がかかる場合がありますが、そうでない場合ははるかに高速になります。
データの性質に応じて、または緊急時に、古いテーブルの名前を変更し、その場所に新しい空のテーブルを作成し、暇なときに新しいテーブルに「キープ」行を選択することができます...