Въведение
Здравейте. Аз съм ningenMe, уеб разработчик.
Както е посочено в заглавието, моята история е история за физическото изтриване на 300 милиона записа в MySQL.
Интересувах се от това, затова реших да направя бележка (инструкция).
Начало — Alert
В пакетния сървър, който използвам и поддържам, има редовен процес, който веднъж на ден събира данни за последния месец от MySQL.
Обикновено този процес приключва за около 1 час, но този път не завърши за 7 или 8 часа, и alert не спираше да изскача…
Търсене на причината
Опитах се да рестартирам процеса, да прегледам логовете, но не видях нищо тревожно.
Запитването беше индексирано правилно. Но когато се замислих какво може да е проблемът, осъзнах, че обемът на БД е доста голям.
hoge_table | 350'000'000 |350 милиона записа. Изглежда, че индексацията работеше правилно, просто много бавно.
Изискваното събиране на данни за месеца беше около 12 000 000 записа. Изглежда, че командата select отне много време, и транзакцията дълго не се изпълняваше.
DB
По същество, това е таблица, която всеки ден нараства с около 400 000 записа. Базата данни трябваше да събира данни само за последния месец, така че се предполагаше, че ще може да се справи именно с този обем данни, но за съжаление операцията rotate не беше включена.
Тази база данни не беше разработена от мен. Аз я получих от друг разработчик, така че остана усещането за технически дълг.
Настъпи момент, когато обемът на ежедневно добавяните данни стана голям и накрая достигна предел. Предполага се, че работейки с такъв голям обем данни, трябва да се разделят, но за съжаление, това не беше направено.
И тук влязох в играта.
Поправка
Беше по-разумно да намаля самата база данни и да съкратя времето за обработка, отколкото да променям самата логика.
Ситуацията трябва значително да се промени, ако изтрия 300 милиона записа, затова реших да го направя… Ах, мислех си, че това определено ще сработи.
Действие 1
След като подготвих надеждна резервна копия, най-накрая започнах да изпращам заявки.
「Изпращане на заявка」
DELETE FROM hoge_table WHERE create_time <= 'YYYY-MM-DD HH:MM:SS';「…」
「…」
“Хм… Няма отговор. Може би процесът отнема много време?” — помислих си, но за всеки случай погледнах в grafana и видях, че натоварването на диска бързо нараства.
„Опасно“ — помислих отново и веднага спрях заявката.
Действие 2
След като анализирах всичко, разбрах, че обемът на данните е твърде голям, за да се изтрие всичко наведнъж.
Реших да напиша скрипт, който може да изтрива около 1 000 000 записа и го стартирах.
„реализирам скрипт“
„Сега със сигурност ще сработи“, — помислих си
Действие 3
Вторият метод сработи, но се оказа много трудоемък.
За да направя всичко старателно, без излишно напрежение, щеше да ми отнеме около две седмици. Но все пак този сценарий не отговаряше на изискванията на услугата, затова трябваше да се откажа от него.
Затова, ето какво реших да направя:
Копираме таблицата и я прекръщаваме
От предишната стъпка разбрах, че изтриването на толкова голям обем данни създава също толкова голямо натоварване. Затова реших да създам нова таблица от нула с помощта на insert и в нея да преместя данните, които планирах да изтрия.
| hoge_table | 350'000'000|
| tmp_hoge_table | 50'000'000|Ако направим нова таблица с размер, равен на посочения по-горе, скоростта на обработка на данните също трябва да стане 1/7 по-бърза.
След като създадох таблицата и я прекръстих, започнах да я използвам като основна таблица. Сега, ако изтрия таблицата с 300 милиона записа, всичко трябва да е наред.
Разбрах, че truncate или drop създават по-малко натоварване в сравнение с delete и реших да използвам този метод.
Изпълнение
「Изпращане на заявка」
INSERT INTO tmp_hoge_table SELECT FROM hoge_table create_time > 'YYYY-MM-DD HH:MM:SS'; 「…」
「…」
„Ъмм…?“
Действие 4
Мислех, че предишната идея ще сработи, но след изпращане на insert заявката се появиха множество грешки. MySQL не прощава.
Вече бях толкова уморен, че започнах да мисля, че вече не искам да се занимавам с това.
Постоях, помислих и схванах, че може би за един път заявките insert бяха твърде много…
Опитах да изпратя insert заявка на обема данни, който базата трябва да обработва за 1 ден. Получи се!
След това продължаваме да изпращаме заявки на същия обем данни. Тъй като трябва да премахнем месечния обем данни, повтаряме тази операция около 35 пъти.
Преименуване на таблицата
Тук късметът беше на моя страна: всичко мина гладко.
Alert изчезнаха
Скоростта на пакетната обработка се увеличи.
По-рано този процес отнемаше около час, сега отнема приблизително 2 минути.
След като се уверих, че всички проблеми са решени, изтрих 300 милиона записа. Изтрих таблицата и се почувствах като новороден.
Резюме
Разбрах, че по време на партидната обработка е пропусната ротационната обработка, което беше основният проблем. Такава грешка в архитектурата води до безполезно губене на време.
Замисляли ли сте се за натоварването при репликация на данни, докато изтривате записи от базата? Нека не натоварваме MySQL.
Хората, които разбират добре от бази данни, определено няма да се сблъскат с такъв проблем. А на останалите се надявам, че тази статия е била полезна.
Благодаря ви, че прочетохте!
Ще се радваме, ако ни кажете, дали статията ви е харесала, дали преводът е разбираем и дали е била полезна за вас?
Източник: habr.com
