MySQL の 300 億レコードを物理的に削除する物語

導入

こんにちは。 Web開発者のningenMeです。

タイトルにあるように、私の話は MySQL の 300 億レコードを物理的に削除する話です。

気になったのでリマインダー(説明書)を作ってみることにしました。

ホーム - アラート

私が使用および保守しているバッチ サーバーには、MySQL から XNUMX 日に XNUMX 回先月のデータを収集する定期的なプロセスがあります。

通常は1時間程度でこの処理が完了するのですが、今回は7~8時間経っても完了せず、アラートが出なくなりませんでした…。

理由を見つける

プロセスを再起動してログを見てみましたが、何も問題はありませんでした。
クエリは正しくインデックス付けされました。 しかし、何が問題なのか考えてみたところ、データベースのサイズが非常に大きいことに気づきました。

hoge_table | 350'000'000 |

350 億 XNUMX 万件のレコード。 インデックス作成は正しく機能しているように見えましたが、非常に遅かったです。

12 か月あたりに必要なデータ収集は約 000 万レコードでした。 select コマンドに時間がかかり、トランザクションが長時間実行されなかったようです。

DB

これは基本的に、毎日約 400 エントリずつ増加するテーブルです。 データベースは先月分のデータのみを収集することになっていたため、まさにこの量のデータに耐えることが期待されていましたが、残念ながら回転操作は含まれていませんでした。

このデータベースは私が開発したものではありません。 別の開発者から引き継いだので、依然として技術的負債のように感じられました。

毎日挿入されるデータ量が増大し、ついに限界に達する時点が来ました。 このような大量のデータを扱う場合、データを分離する必要があると考えられますが、残念ながらこれは行われませんでした。

そして、私は行動を起こしました。

補正

ロジック自体を変更するよりも、データベース自体のサイズを小さくして処理時間を短縮する方が合理的でした。

300億件消せば状況は大きく変わるはずなので、そうすることにしたのですが… えー、これなら絶対いけると思いました。

アクション 1

信頼できるバックアップを準備したので、いよいよリクエストの送信を開始しました。

「リクエストを送る」

DELETE FROM hoge_table WHERE create_time <= 'YYYY-MM-DD HH:MM:SS';

「...」

「...」

「うーん…答えはありません。 おそらくこのプロセスには時間がかかるのではないでしょうか?」 — と思いましたが、念のためgrafanaを見てみると、ディスク負荷が急激に増大していることがわかりました。
「危険だ」と私は思い直し、すぐに依頼をやめました。

アクション 2

すべてを分析した結果、データの量が多すぎて一度にすべてを削除できないことがわかりました。

約 1 万件のレコードを削除できるスクリプトを作成して起動することにしました。

「スクリプトを実装します」

「これなら間違いなくうまくいくだろう」と私は思いました。

アクション 3

XNUMX 番目の方法は機能しましたが、非常に労力がかかることが判明しました。
余計な神経を使わずにすべてを慎重に行うと、約 XNUMX 週間かかります。 しかし、それでも、このシナリオはサービス要件を満たしていないため、このシナリオから離れる必要がありました。

そこで私がやることに決めたのは次のとおりです。

テーブルをコピーして名前を変更します

前の手順で、このような大量のデータを削除すると、同様に大きな負荷が発生することがわかりました。 そこで、insert を使用して新しいテーブルを最初から作成し、削除するデータをそこに移動することにしました。

| hoge_table     | 350'000'000|
| tmp_hoge_table |  50'000'000|

新しいテーブルを上記と同じサイズにすると、データ処理速度も 1/7 速くなるはずです。

テーブルを作成して名前を変更した後、それをマスターテーブルとして使用し始めました。 ここで、300 億件のレコードを含むテーブルを削除すると、すべてがうまくいくはずです。
切り捨てまたはドロップの方が削除よりもオーバーヘッドが少ないことがわかり、この方法を使用することにしました。

実行

「リクエストを送る」

INSERT INTO tmp_hoge_table SELECT FROM hoge_table create_time > 'YYYY-MM-DD HH:MM:SS';

「...」
「...」
「えっ…?」

アクション 4

前のアイデアはうまくいくと思いましたが、挿入リクエストを送信した後、複数のエラーが表示されました。 MySQL は寛容ではありません。

もう疲れてしまったので、もうやりたくない、と思い始めました。

座って考えてみると、一度に挿入クエリが多すぎる可能性があることに気付きました...
データベースが 1 日に処理するデータ量の挿入リクエストを送信してみました。 起こりました!

さて、その後も同じ量のデータのリクエストを送信し続けます。 35か月分のデータを削除する必要があるため、この操作を約XNUMX回繰り返します。

テーブルの名前を変更する

ここでは幸運が味方してくれました。すべてがスムーズに進みました。

アラートが消えた

バッチ処理速度が向上しました。

以前はこのプロセスに約 2 時間かかりましたが、現在は約 XNUMX 分で完了します。

すべての問題が解決されたと確信した後、300 億件のレコードを削除しました。 テーブルを削除して、生まれ変わった気分になりました。

まとめ

バッチ処理に回転処理が欠けていることが最大の問題であることに気づきました。 この種のアーキテクチャ上のエラーは時間の無駄につながります。

データベースからレコードを削除する際、データレプリケーション時の負荷を考慮していますか? MySQL に過負荷をかけないようにしましょう。

データベースに精通している人であれば、このような問題に遭遇することはまずありません。 残りの皆さんにとって、この記事がお役に立てば幸いです。

読んでくれてありがとう!

この記事が気に入ったかどうか、翻訳が明確かどうか、役に立ったかどうかを教えていただければ幸いです。

出所: habr.com

コメントを追加します