Historia o fizycznym usunięciu 300 milionów rekordów w MySQL

Wprowadzenie

Cześć. Jestem ningenMe, web developer.

Jak wskazuje tytuł, moja historia to opowieść o fizycznym usunięciu 300 milionów rekordów w MySQL.

Zainteresowałem się tym, więc postanowiłem stworzyć notatkę (instrukcję).

Początek — Alert

W pakiecie serwerze, który używam i utrzymuję, jest regularny proces, który raz dziennie zbiera dane z ostatniego miesiąca z MySQL.

Zazwyczaj ten proces kończy się w ciągu około 1 godziny, ale tym razem nie kończył się przez 7 lub 8 godzin, a alert wciąż się pojawiał...

Szukając przyczyny

Próbowałem zrestartować proces, sprawdzić logi, ale nie zauważyłem nic poważnego.
Zapytanie było poprawnie indeksowane. Lecz gdy zastanowiłem się, co może być nie tak, zdałem sobie sprawę, że rozmiar bazy danych jest dość duży.

hoge_table | 350'000'000 |

350 milionów rekordów. Wygląda na to, że indeksacja działała poprawnie, po prostu bardzo wolno.

Wymagany zbiór danych za miesiąc wynosił około 12 000 000 rekordów. Wygląda na to, że polecenie select zajęło dużo czasu, a transakcja długo nie była wykonywana.

DB

W zasadzie to tabela, która każdego dnia zwiększa się o około 400 000 rekordów. Baza powinna zbierać dane tylko za ostatni miesiąc, więc zakładano, że wytrzyma ten wolumin danych, ale niestety operacja rotate nie była włączona.

Ta baza danych nie została stworzona przeze mnie. Przejąłem ją od innego dewelopera, dlatego pozostało odczucie istnienia długu technicznego.

Nadszedł moment, gdy wolumen codziennie wstawianych danych stał się duży i w końcu osiągnął limit. Zakłada się, że pracując z takim dużym woluminem danych, należy je dzielić, ale niestety to nie zostało zrobione.

I wtedy do akcji wkroczyłem ja.

Naprawa

Rozsądniej było zmniejszyć samą bazę danych i skrócić czas jej przetwarzania, niż zmieniać samą logikę.

Sytuacja powinna się znacznie zmienić, jeśli usunę 300 milionów rekordów, więc postanowiłem to zrobić... Ech, myślałem, że to na pewno zadziała.

Akcja 1

Po przygotowaniu solidnej kopii zapasowej w końcu zacząłem wysyłać zapytania.

「Wysyłanie zapytania」

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

「…」

「…」

“Hmm… Brak odpowiedzi. Może proces zajmuje dużo czasu?” — pomyślałem, ale dla pewności spojrzałem na grafana i zobaczyłem, że obciążenie dysku szybko rosło.
„To trochę niebezpieczne” — pomyślałem jeszcze raz i natychmiast zatrzymałem zapytanie.

Działanie 2

Po przeanalizowaniu wszystkiego zrozumiałem, że objętość danych była zbyt duża, aby usunąć wszystko naraz.

Postanowiłem napisać skrypt, który będzie mógł usunąć około 1 000 000 rekordów i uruchomiłem go.

「realizuję skrypt」

„Teraz na pewno zadziała” — pomyślałem.

Działanie 3

Druga metoda zadziałała, ale okazała się bardzo czasochłonna.
Aby zrobić to wszystko dokładnie, bez zbędnego stresu, potrzeba byłoby około dwóch tygodni. Niemniej jednak ten scenariusz nie spełniał wymagań serwisowych, więc musiałem z niego zrezygnować.

Dlatego oto, co postanowiłem zrobić:

Kopiujemy tabelę i zmieniamy jej nazwę.

Z poprzedniego kroku zrozumiałem, że usunięcie tak dużej objętości danych stwarza tak dużą obciążenie. Dlatego postanowiłem stworzyć nową tabelę od zera za pomocą insert i przenieść do niej dane, które zamierzałem usunąć.

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

Jeśli utworzymy nową tabelę o rozmiarze takim jak podano powyżej, prędkość przetwarzania danych również powinna być o 1/7 szybsza.

Utworzywszy tabelę i zmieniając jej nazwę, zacząłem używać jej jako tabeli master (podstawowej). Teraz, jeśli usunę tabelę z 300 milionami rekordów, wszystko powinno być w porządku.
Dowiedziałem się, że truncate lub drop powodują mniejsze obciążenie niż delete i postanowiłem użyć tej metody.

Wykonywanie

「Wysyłanie zapytania」

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

「…」
「…」
„em…?”

Działanie 4

Myślałem, że poprzedni pomysł zadziała, ale po wysłaniu zapytania insert pojawiło się wiele błędów. MySQL nie oszczędza.

Już byłem tak zmęczony, że zacząłem myśleć, że nie chcę się już tym zajmować.

Siedziałem, myślałem i zrozumiałem, że może było za dużo zapytań insert na raz…
Spróbowałem wysłać zapytanie insert na objętość danych, którą baza powinna przetwarzać przez jeden dzień. Udało się!

Po tym kontynuujemy wysyłanie zapytań na tę samą objętość danych. Ponieważ trzeba usunąć miesięczną objętość danych, powtarzamy tę operację około 35 razy.

Zmiana nazwy tabeli

Tutaj szczęście było po mojej stronie: wszystko poszło gładko.

Alerty zniknęły.

Szybkość przetwarzania wsadowego wzrosła.

Wcześniej ten proces zajmował około godziny, teraz trwa około 2 minut.

Po tym, jak upewniłem się, że wszystkie problemy zostały rozwiązane, usunąłem 300 milionów rekordów. Usunąłem tabelę i poczułem się jak na nowo narodzony.

Podsumowanie

Zrozumiałem, że w przetwarzaniu wsadowym przeoczono przetwarzanie rotacji, co stanowiło główny problem. Taki błąd w architekturze prowadzi do marnowania czasu.

Czy zastanawiasz się nad obciążeniem podczas replikacji danych, usuwając rekordy z bazy? Nie przeciążajmy MySQL.

Osoby, które dobrze znają się na bazach danych, na pewno nie napotkają takiego problemu. Mam nadzieję, że ten artykuł był pomocny dla pozostałych.

Dziękujemy za przeczytanie!

Będziemy bardzo wdzięczni, jeśli opowiesz nam, czy podobał Ci się ten artykuł, czy tłumaczenie było zrozumiałe i czy było pomocne.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster